Add Zephyr port

This port is meant to grow to encompass all existing boards. For
now, it is a port while we transition over.

It is named `zephyr-cp` to differentiate it from the MicroPython
`zephyr` port. They are separate implementations.
This commit is contained in:
Scott Shawcroft 2024-12-02 11:20:37 -08:00
parent 11711fd53d
commit 7f0cc9e7b4
No known key found for this signature in database
140 changed files with 10322 additions and 294 deletions

View file

@ -22,7 +22,8 @@ runs:
if: >- if: >-
inputs.port != 'none' && inputs.port != 'none' &&
inputs.port != 'litex' && inputs.port != 'litex' &&
inputs.port != 'espressif' inputs.port != 'espressif' &&
inputs.port != 'zephyr-cp'
uses: carlosperate/arm-none-eabi-gcc-action@v1 uses: carlosperate/arm-none-eabi-gcc-action@v1
with: with:
# When changing this update what Windows grabs too! # When changing this update what Windows grabs too!
@ -51,7 +52,7 @@ runs:
# common # common
- name: Cache python dependencies - name: Cache python dependencies
if: inputs.port != 'espressif' if: inputs.port != 'espressif' && inputs.port != 'zephyr-cp'
uses: ./.github/actions/deps/python uses: ./.github/actions/deps/python
with: with:
action: ${{ inputs.action }} action: ${{ inputs.action }}

View file

@ -4,33 +4,29 @@ inputs:
board: board:
required: true required: true
type: string type: string
outputs:
port: port:
value: ${{ steps.board-to-port.outputs.port }} required: true
type: string
runs: runs:
using: composite using: composite
steps: steps:
- name: Board to port
id: board-to-port
run: |
PORT=$(find ports/*/boards/ -type d -name ${{ inputs.board }} | sed 's/^ports\///g;s/\/boards.*//g')
if [ -z $PORT ]; then (exit 1); else echo >> $GITHUB_OUTPUT "port=$PORT"; fi
shell: bash
- name: Set up broadcom - name: Set up broadcom
if: steps.board-to-port.outputs.port == 'broadcom' if: inputs.port == 'broadcom'
uses: ./.github/actions/deps/ports/broadcom uses: ./.github/actions/deps/ports/broadcom
- name: Set up espressif - name: Set up espressif
if: steps.board-to-port.outputs.port == 'espressif' if: inputs.port == 'espressif'
uses: ./.github/actions/deps/ports/espressif uses: ./.github/actions/deps/ports/espressif
- name: Set up litex - name: Set up litex
if: steps.board-to-port.outputs.port == 'litex' if: inputs.port == 'litex'
uses: ./.github/actions/deps/ports/litex uses: ./.github/actions/deps/ports/litex
- name: Set up nordic - name: Set up nordic
if: steps.board-to-port.outputs.port == 'nordic' if: inputs.port == 'nordic'
uses: ./.github/actions/deps/ports/nordic uses: ./.github/actions/deps/ports/nordic
- name: Set up Zephyr
if: inputs.port == 'zephyr-cp'
uses: ./.github/actions/deps/ports/zephyr-cp

View file

@ -0,0 +1,15 @@
name: Fetch Zephyr port deps
runs:
using: composite
steps:
- name: Setup Zephyr project
uses: zephyrproject-rtos/action-zephyr-setup@v1
with:
app-path: zephyr-config
base-path: ports/zephyr-cp
toolchains: arm-zephyr-eabi
- name: Export cmake info
run: west zephyr-export
shell: bash
working-directory: ports/zephyr-cp

View file

@ -80,7 +80,7 @@ runs:
git fetch --no-recurse-submodules --shallow-since="2021-07-01" origin $GITHUB_SHA git fetch --no-recurse-submodules --shallow-since="2021-07-01" origin $GITHUB_SHA
git repack -d git repack -d
echo "::endgroup::" echo "::endgroup::"
CP_VERSION=$(tools/describe) CP_VERSION=$(python py/version.py)
echo "$CP_VERSION" echo "$CP_VERSION"
echo "CP_VERSION=$CP_VERSION" >> $GITHUB_ENV echo "CP_VERSION=$CP_VERSION" >> $GITHUB_ENV
echo "cp-version=$CP_VERSION" >> $GITHUB_OUTPUT echo "cp-version=$CP_VERSION" >> $GITHUB_OUTPUT

View file

@ -97,7 +97,7 @@ jobs:
download: false download: false
- name: Versions - name: Versions
run: | run: |
tools/describe python py/version.py
gcc --version gcc --version
python3 --version python3 --version
cmake --version || true cmake --version || true

View file

@ -9,6 +9,9 @@ on:
cp-version: cp-version:
required: true required: true
type: string type: string
port:
required: true
type: string
secrets: secrets:
AWS_ACCESS_KEY_ID: AWS_ACCESS_KEY_ID:
required: false required: false
@ -43,6 +46,7 @@ jobs:
uses: ./.github/actions/deps/ports uses: ./.github/actions/deps/ports
with: with:
board: ${{ matrix.board }} board: ${{ matrix.board }}
port: ${{ inputs.port }}
- name: Set up submodules - name: Set up submodules
id: set-up-submodules id: set-up-submodules
@ -51,7 +55,7 @@ jobs:
- name: Set up external - name: Set up external
uses: ./.github/actions/deps/external uses: ./.github/actions/deps/external
with: with:
port: ${{ steps.set-up-port.outputs.port }} port: ${{ inputs.port }}
- name: Set up mpy-cross - name: Set up mpy-cross
if: steps.set-up-submodules.outputs.frozen == 'True' if: steps.set-up-submodules.outputs.frozen == 'True'
uses: ./.github/actions/mpy_cross uses: ./.github/actions/mpy_cross

View file

@ -299,6 +299,35 @@ jobs:
# ERROR: Platform MINGW64_NT-10.0-17763-x86_64 appears to be unsupported # ERROR: Platform MINGW64_NT-10.0-17763-x86_64 appears to be unsupported
# https://github.com/espressif/esp-idf/issues/7062 # https://github.com/espressif/esp-idf/issues/7062
windows-zephyr:
strategy:
matrix:
os: [windows-2022, windows-2025]
runs-on: ${{ matrix.os }}
needs: scheduler
if: needs.scheduler.outputs.windows == 'True'
env:
CP_VERSION: ${{ needs.scheduler.outputs.cp-version }}
steps:
- name: Set up repository
uses: actions/checkout@v4
with:
submodules: false
show-progress: false
fetch-depth: 1
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Set up Zephyr
uses: ./.github/actions/deps/ports/zephyr-cp
- name: Set up submodules
uses: ./.github/actions/deps/submodules
- name: build mpy-cross
run: make -j4 -C mpy-cross
- name: build ek_ra8d1
run: make -j4 -C ports/zephyr-cp BOARD=renesas_ek_ra8d1
ports: ports:
needs: [scheduler, mpy-cross, tests] needs: [scheduler, mpy-cross, tests]
if: needs.scheduler.outputs.ports != '{}' if: needs.scheduler.outputs.ports != '{}'
@ -311,3 +340,4 @@ jobs:
with: with:
boards: ${{ toJSON(fromJSON(needs.scheduler.outputs.ports)[matrix.port]) }} boards: ${{ toJSON(fromJSON(needs.scheduler.outputs.ports)[matrix.port]) }}
cp-version: ${{ needs.scheduler.outputs.cp-version }} cp-version: ${{ needs.scheduler.outputs.cp-version }}
port: ${{ matrix.port }}

View file

@ -25,7 +25,8 @@ repos:
tests/unicode/data/utf-8_invalid.txt| tests/unicode/data/utf-8_invalid.txt|
tests/extmod/data/qr.pgm| tests/extmod/data/qr.pgm|
tests/basics/bytearray_byte_operations.py| tests/basics/bytearray_byte_operations.py|
ports/raspberrypi/sdk ports/raspberrypi/sdk|
ports/zephyr-cp/cptools/compat2driver.py
) )
- repo: local - repo: local
hooks: hooks:

View file

@ -32,8 +32,6 @@ from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.ext import intersphinx from sphinx.ext import intersphinx
tools_describe = str(pathlib.Path(__file__).parent / "tools/describe")
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
@ -145,7 +143,7 @@ copyright = f'2014-{current_date.tm_year}, MicroPython & CircuitPython contribut
final_version = "" final_version = ""
git_describe = subprocess.run( git_describe = subprocess.run(
[tools_describe], ["python", "py/version.py"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
encoding="utf-8" encoding="utf-8"

View file

@ -26,7 +26,7 @@ import os
import pathlib import pathlib
import re import re
import subprocess import subprocess
import tomllib
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
@ -43,6 +43,7 @@ SUPPORTED_PORTS = [
"renode", "renode",
"silabs", "silabs",
"stm", "stm",
"zephyr-cp",
] ]
ALIASES_BY_BOARD = { ALIASES_BY_BOARD = {
@ -130,14 +131,21 @@ def get_board_mapping():
boards = {} boards = {}
for port in SUPPORTED_PORTS: for port in SUPPORTED_PORTS:
board_path = root_dir / "ports" / port / "boards" board_path = root_dir / "ports" / port / "boards"
for board_path in os.scandir(board_path): # Zephyr port has vendor specific subdirectories to match zephyr (and
# clean up the boards folder.)
g = "*/*" if port == "zephyr-cp" else "*"
for board_path in board_path.glob(g):
if board_path.is_dir(): if board_path.is_dir():
board_id = board_path.name board_id = board_path.name
if port == "zephyr-cp":
vendor = board_path.parent.name
board_id = f"{vendor}_{board_id}"
aliases = ALIASES_BY_BOARD.get(board_path.name, []) aliases = ALIASES_BY_BOARD.get(board_path.name, [])
boards[board_id] = { boards[board_id] = {
"port": port, "port": port,
"download_count": 0, "download_count": 0,
"aliases": aliases, "aliases": aliases,
"directory": board_path,
} }
for alias in aliases: for alias in aliases:
boards[alias] = { boards[alias] = {
@ -145,6 +153,7 @@ def get_board_mapping():
"download_count": 0, "download_count": 0,
"alias": True, "alias": True,
"aliases": [], "aliases": [],
"directory": board_path,
} }
return boards return boards
@ -295,15 +304,6 @@ def lookup_setting(settings, key, default=""):
return value return value
def all_ports_all_boards(ports=SUPPORTED_PORTS):
for port in ports:
port_dir = get_circuitpython_root_dir() / "ports" / port
for entry in (port_dir / "boards").iterdir():
if not entry.is_dir():
continue
yield (port, entry)
def support_matrix_by_board(use_branded_name=True, withurl=True, def support_matrix_by_board(use_branded_name=True, withurl=True,
add_port=False, add_chips=False, add_port=False, add_chips=False,
add_pins=False, add_branded_name=False): add_pins=False, add_branded_name=False):
@ -313,29 +313,44 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
base = build_module_map() base = build_module_map()
def support_matrix(arg): def support_matrix(arg):
port, entry = arg board_id, board_info = arg
port_dir = get_circuitpython_root_dir() / "ports" / port port = board_info["port"]
settings = get_settings_from_makefile(str(port_dir), entry.name) board_directory = board_info["directory"]
port_dir = board_directory.parent.parent
if port != "zephyr-cp":
settings = get_settings_from_makefile(str(port_dir), board_directory.name)
autogen_board_info = None
else:
circuitpython_toml_fn = board_directory / "circuitpython.toml"
with circuitpython_toml_fn.open("rb") as f:
settings = tomllib.load(f)
autogen_board_info_fn = board_directory / "autogen_board_info.toml"
with autogen_board_info_fn.open("rb") as f:
autogen_board_info = tomllib.load(f)
if use_branded_name or add_branded_name: if use_branded_name or add_branded_name:
with open(entry / "mpconfigboard.h") as get_name: if autogen_board_info:
board_contents = get_name.read() branded_name = autogen_board_info["name"]
board_name_re = re.search( else:
r"(?<=MICROPY_HW_BOARD_NAME)\s+(.+)", board_contents with open(board_directory / "mpconfigboard.h") as get_name:
) board_contents = get_name.read()
if board_name_re: board_name_re = re.search(
branded_name = board_name_re.group(1).strip('"') r"(?<=MICROPY_HW_BOARD_NAME)\s+(.+)", board_contents
if '"' in branded_name: # sometimes the closing " is not at line end )
branded_name = branded_name[:branded_name.index('"')] if board_name_re:
board_name = branded_name branded_name = board_name_re.group(1).strip('"')
if '"' in branded_name: # sometimes the closing " is not at line end
branded_name = branded_name[:branded_name.index('"')]
board_name = branded_name
if use_branded_name: if use_branded_name:
board_name = branded_name board_name = branded_name
else: else:
board_name = entry.name board_name = board_directory.name
if add_chips: if add_chips:
with open(entry / "mpconfigboard.h") as get_name: with open(board_directory / "mpconfigboard.h") as get_name:
board_contents = get_name.read() board_contents = get_name.read()
mcu_re = re.search( mcu_re = re.search(
r'(?<=MICROPY_HW_MCU_NAME)\s+(.+)', board_contents r'(?<=MICROPY_HW_MCU_NAME)\s+(.+)', board_contents
@ -346,7 +361,7 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
mcu = mcu[:mcu.index('"')] mcu = mcu[:mcu.index('"')]
else: else:
mcu = "" mcu = ""
with open(entry / "mpconfigboard.mk") as get_name: with open(board_directory / "mpconfigboard.mk") as get_name:
board_contents = get_name.read() board_contents = get_name.read()
flash_re = re.search( flash_re = re.search(
r'(?<=EXTERNAL_FLASH_DEVICES)\s+=\s+(.+)', board_contents r'(?<=EXTERNAL_FLASH_DEVICES)\s+=\s+(.+)', board_contents
@ -363,7 +378,7 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
if add_pins: if add_pins:
pins = [] pins = []
try: try:
with open(entry / "pins.c") as get_name: with open(board_directory / "pins.c") as get_name:
pin_lines = get_name.readlines() pin_lines = get_name.readlines()
except FileNotFoundError: # silabs boards have no pins.c except FileNotFoundError: # silabs boards have no pins.c
pass pass
@ -376,17 +391,25 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
pins.append((board_pin, chip_pin)) pins.append((board_pin, chip_pin))
board_modules = [] board_modules = []
for module in base: if autogen_board_info:
key = base[module]["key"] autogen_modules = autogen_board_info["modules"]
if int(lookup_setting(settings, key, "0")): for k in autogen_modules:
board_modules.append(base[module]["name"]) if autogen_modules[k]:
board_modules.append(k)
else:
for module in base:
key = base[module]["key"]
if int(lookup_setting(settings, key, "0")):
board_modules.append(base[module]["name"])
board_modules.sort() board_modules.sort()
if "CIRCUITPY_BUILD_EXTENSIONS" in settings: if "CIRCUITPY_BUILD_EXTENSIONS" in settings:
board_extensions = [ board_extensions = settings["CIRCUITPY_BUILD_EXTENSIONS"]
extension.strip() if isinstance(board_extensions, str):
for extension in settings["CIRCUITPY_BUILD_EXTENSIONS"].split(",") board_extensions = [
] extension.strip()
for extension in board_extensions.split(",")
]
else: else:
raise OSError(f"Board extensions undefined: {board_name}.") raise OSError(f"Board extensions undefined: {board_name}.")
@ -420,8 +443,8 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
board_info board_info
) )
] ]
if entry.name in ALIASES_BY_BOARD: if board_id in ALIASES_BY_BOARD:
for alias in ALIASES_BY_BOARD[entry.name]: for alias in ALIASES_BY_BOARD[board_id]:
if use_branded_name: if use_branded_name:
if alias in ALIASES_BRAND_NAMES: if alias in ALIASES_BRAND_NAMES:
alias = ALIASES_BRAND_NAMES[alias] alias = ALIASES_BRAND_NAMES[alias]
@ -450,8 +473,14 @@ def support_matrix_by_board(use_branded_name=True, withurl=True,
return board_matrix # this is now a list of (board,modules) return board_matrix # this is now a list of (board,modules)
board_mapping = get_board_mapping()
real_boards = []
for board in board_mapping:
if not board_mapping[board].get("alias", False):
real_boards.append((board, board_mapping[board]))
executor = ThreadPoolExecutor(max_workers=os.cpu_count()) executor = ThreadPoolExecutor(max_workers=os.cpu_count())
mapped_exec = executor.map(support_matrix, all_ports_all_boards()) mapped_exec = executor.map(support_matrix, real_boards)
# flatmap with comprehensions # flatmap with comprehensions
boards = dict( boards = dict(
sorted( sorted(

View file

@ -25,3 +25,4 @@ Additional testing is limited.
../ports/silabs/README ../ports/silabs/README
../ports/stm/README ../ports/stm/README
../ports/unix/README ../ports/unix/README
../ports/zephyr-cp/README

View file

@ -41,8 +41,8 @@ static mbedtls_x509_crt s_dummy_crt;
#define LOGD(tag, fmt, ...) do {} while (0) #define LOGD(tag, fmt, ...) do {} while (0)
#endif #endif
extern const uint8_t x509_crt_imported_bundle_bin_start[] asm ("_binary_x509_crt_bundle_start"); extern const uint8_t x509_crt_imported_bundle_bin_start[] __asm__ ("_binary_x509_crt_bundle_start");
extern const uint8_t x509_crt_imported_bundle_bin_end[] asm ("_binary_x509_crt_bundle_end"); extern const uint8_t x509_crt_imported_bundle_bin_end[] __asm__ ("_binary_x509_crt_bundle_end");
typedef struct crt_bundle_t { typedef struct crt_bundle_t {

@ -1 +1 @@
Subproject commit 2d7d1070fc9eb8db7061b4f0e20408a453df1f7e Subproject commit eca025f7143141fd2bc99a94619c62a6fd666f28

6
main.c
View file

@ -974,7 +974,13 @@ static int run_repl(safe_mode_t safe_mode) {
return exit_code; return exit_code;
} }
#if defined(__ZEPHYR__) && __ZEPHYR__ == 1
#include <zephyr/console/console.h>
int circuitpython_main(void) {
#else
int __attribute__((used)) main(void) { int __attribute__((used)) main(void) {
#endif
// initialise the cpu and peripherals // initialise the cpu and peripherals
set_safe_mode(port_init()); set_safe_mode(port_init());

View file

@ -70,3 +70,5 @@ CIRCUITPY_BUSDEVICE = 0
# For CircuitPython CI # For CircuitPython CI
CIRCUITPY_BUILD_EXTENSIONS ?= elf CIRCUITPY_BUILD_EXTENSIONS ?= elf
CIRCUITPY_PORT_SERIAL = 1

View file

@ -23,6 +23,10 @@
#endif #endif
#endif #endif
void port_serial_early_init(void) {
}
void port_serial_init(void) { void port_serial_init(void) {
#if MAX32_SERIAL #if MAX32_SERIAL
MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN; MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN;

View file

@ -46,6 +46,8 @@ CIRCUITPY_WIFI_RADIO_SETTABLE_LISTEN_INTERVAL = 1
# Never use our copy of MBEDTLS # Never use our copy of MBEDTLS
CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0 CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0
CIRCUITPY_PORT_SERIAL = 1
# These modules are implemented in ports/<port>/common-hal: # These modules are implemented in ports/<port>/common-hal:
CIRCUITPY_ALARM ?= 1 CIRCUITPY_ALARM ?= 1
CIRCUITPY_ALARM_TOUCH ?= 0 CIRCUITPY_ALARM_TOUCH ?= 0

View file

@ -15,6 +15,9 @@
#include "supervisor/usb_serial_jtag.h" #include "supervisor/usb_serial_jtag.h"
#endif #endif
void port_serial_early_init(void) {
}
void port_serial_init(void) { void port_serial_init(void) {
#if CIRCUITPY_ESP_USB_SERIAL_JTAG #if CIRCUITPY_ESP_USB_SERIAL_JTAG
usb_serial_jtag_init(); usb_serial_jtag_init();

View file

@ -15,6 +15,8 @@ CIRCUITPY_RTC ?= 1
CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_USB_DEVICE = 0
CIRCUITPY_WATCHDOG ?=1 CIRCUITPY_WATCHDOG ?=1
CIRCUITPY_PORT_SERIAL = 1
ifeq ($(MCU_SERIES),MG24) ifeq ($(MCU_SERIES),MG24)
# Not yet implemented common-hal modules: # Not yet implemented common-hal modules:
CIRCUITPY_AUDIOIO ?= 0 CIRCUITPY_AUDIOIO ?= 0

View file

@ -28,6 +28,7 @@ ifeq ($(MCU_SERIES),F4)
CIRCUITPY_NVM ?= 0 CIRCUITPY_NVM ?= 0
CIRCUITPY_ROTARYIO ?= 0 CIRCUITPY_ROTARYIO ?= 0
CIRCUITPY_RTC ?= 1 CIRCUITPY_RTC ?= 1
CIRCUITPY_PORT_SERIAL ?= 1
USB_NUM_ENDPOINT_PAIRS = 4 USB_NUM_ENDPOINT_PAIRS = 4
UF2_FAMILY_ID ?= 0x57755a57 UF2_FAMILY_ID ?= 0x57755a57
endif endif

View file

@ -16,6 +16,10 @@
UART_HandleTypeDef huart2; UART_HandleTypeDef huart2;
#endif #endif
void port_serial_early_init(void) {
}
void port_serial_init(void) { void port_serial_init(void) {
#if CPY_STM32F4 #if CPY_STM32F4
huart2.Instance = USART2; huart2.Instance = USART2;

7
ports/zephyr-cp/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# West manages these folders.
bootloader
build
modules
tools
zephyr
.west

View file

@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS lib/zephyr)
project(circuitpython)
target_sources(app PRIVATE zephyr_main.c)
# From: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/application_development/external_lib/CMakeLists.txt
# The external static library that we are linking with does not know
# how to build for this platform so we export all the flags used in
# this zephyr build to the external build system.
#
# Other external build systems may be self-contained enough that they
# do not need any build information from zephyr. Or they may be
# incompatible with certain zephyr options and need them to be
# filtered out.
zephyr_get_include_directories_for_lang_as_string( C includes)
zephyr_get_system_include_directories_for_lang_as_string(C system_includes)
zephyr_get_compile_definitions_for_lang_as_string( C definitions)
zephyr_get_compile_options_for_lang_as_string( C options)
if(DEFINED CMAKE_C_COMPILER_TARGET)
set(target_flag "--target=${CMAKE_C_COMPILER_TARGET}")
endif()
set(external_project_cflags
"${target_flag} ${includes} ${definitions} ${options} ${system_includes}"
)
zephyr_get(TRANSLATION SYSBUILD GLOBAL)
zephyr_get(CONFIG_LTO SYSBUILD GLOBAL)
zephyr_get(BOARD_ALIAS SYSBUILD GLOBAL)
ExternalProject_Add(circuitpython
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/cptools/build_circuitpython.py
CC=${CMAKE_C_COMPILER}
AR=${CMAKE_AR}
CFLAGS=${external_project_cflags}
BOARD=${BOARD}
BOARD_ALIAS=${BOARD_ALIAS}
BOARD_REVISION=${BOARD_REVISION}
BOARD_QUALIFIERS=${BOARD_QUALIFIERS}
SOC_DIRECTORIES=${SOC_DIRECTORIES}
OUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/libcircuitpython.a
PORT_SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
TRANSLATION=${TRANSLATION}
LTO=${CONFIG_LTO}
BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/libcircuitpython.a
BUILD_JOB_SERVER_AWARE TRUE
BUILD_ALWAYS TRUE
DEPENDS zephyr
INSTALL_COMMAND ""
)
add_library(circuitpython_wrapper STATIC IMPORTED GLOBAL)
add_dependencies(
circuitpython_wrapper
circuitpython
)
set_target_properties(circuitpython_wrapper PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/libcircuitpython.a)
target_link_libraries(circuitpython_wrapper INTERFACE kernel)
target_link_libraries(app PRIVATE circuitpython_wrapper)

View file

@ -0,0 +1,15 @@
# Copyright 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
source "share/sysbuild/Kconfig"
config NET_CORE_BOARD
string
default "nrf5340dk/nrf5340/cpunet" if $(BOARD) = "nrf5340dk"
default "nrf7002dk/nrf5340/cpunet" if $(BOARD) = "nrf7002dk"
default "nrf5340_audio_dk/nrf5340/cpunet" if $(BOARD) = "nrf5340_audio_dk"
config NET_CORE_IMAGE_HCI_IPC
bool "HCI IPC image on network core"
default y
depends on NET_CORE_BOARD != ""

33
ports/zephyr-cp/Makefile Normal file
View file

@ -0,0 +1,33 @@
ALL_BOARDS_IN_PORT := $($(wildcard boards/*/*):boards/=:/=)
# An incorrect BOARD might have been specified, so check against the list.
# There is deliberately no space after the :=
VALID_BOARD :=$(filter $(BOARD),$(ALL_BOARDS_IN_PORT))
# If the build directory is not given, make it reflect the board name.
BUILD ?= build-$(BOARD)
TRANSLATION ?= en_US
.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug clean menuconfig
$(BUILD)/zephyr-cp/zephyr/zephyr.elf:
python cptools/pre_zephyr_build_prep.py $(BOARD)
west build -b $(BOARD) -d $(BUILD) --sysbuild -- -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION)
$(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
cp $^ $@
$(BUILD)/firmware.hex: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
cp $(BUILD)/zephyr-cp/zephyr/zephyr.hex $@
flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
west flash -d $(BUILD)
debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
west debug -d $(BUILD)
menuconfig:
west build --sysbuild -d $(BUILD) -t menuconfig
clean:
rm -rf $(BUILD)

47
ports/zephyr-cp/README.md Normal file
View file

@ -0,0 +1,47 @@
# Zephyr
This is an initial port of CircuitPython onto Zephyr. We intend on migrating all
existing boards onto Zephyr. To start, we'll only support new boards. Existing
boards will be switched as the Zephyr port reaches parity with the existing
implementation.
## Getting Started
First, install Zephyr tools (see [Zephyr's Getting Started Guide](https://docs.zephyrproject.org/4.0.0/develop/getting_started/index.html)). (These are `fish` commands because that's what Scott uses.)
```sh
pip install west
west init -l zephyr-config
west update
west zephyr-export
pip install -r lib/zephyr/scripts/requirements.txt
west sdk install
```
Now to build from the top level:
```sh
make BOARD=nordic_nrf7002dk
```
This uses Zephyr's cmake to generate Makefiles that then delegate to
`tools/cpbuild/build_circuitpython.py` to build the CircuitPython bits in parallel.
## Testing other boards
[Any Zephyr board](https://docs.zephyrproject.org/latest/boards/index.html#) can
be used with CircuitPython. To test a different board, use `west` directly to
build the board. The build will do its best to support as much as possible. By
default the Zephyr console will be used for output. USB support is limited by
initialization support in `supervisor/usb.c`. Only flash regions not used by
Zephyr are used for CIRCUITPY. A manual `circuitpython` partition can be
specified instead.
For example, to test the `nrf52840dk` board:
```sh
west build -b nrf52840dk/nrf52840
```
This is already supported in `ports/nordic` as `pca10056`.

View file

@ -0,0 +1,45 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "background.h"
#include "py/runtime.h"
#include "supervisor/port.h"
#if CIRCUITPY_DISPLAYIO
#include "shared-module/displayio/__init__.h"
#endif
#if CIRCUITPY_AUDIOBUSIO
#include "common-hal/audiobusio/I2SOut.h"
#endif
#if CIRCUITPY_AUDIOPWMIO
#include "common-hal/audiopwmio/PWMAudioOut.h"
#endif
void port_start_background_tick(void) {
}
void port_finish_background_tick(void) {
}
void port_background_tick(void) {
#if CIRCUITPY_AUDIOPWMIO
audiopwmout_background();
#endif
#if CIRCUITPY_AUDIOBUSIO
i2s_background();
#endif
}
// Allow boards to override this.
MP_WEAK void board_background_task(void) {
}
void port_background_task(void) {
board_background_task();
}

View file

@ -0,0 +1,9 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
void board_background_task(void);

View file

@ -0,0 +1,12 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "py/obj.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "bindings/zephyr_kernel/__init__.h"

View file

@ -0,0 +1,15 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/mpconfig.h"
#include "py/obj.h"
#include "common-hal/zephyr_kernel/__init__.h"
void raise_zephyr_error(int err);
#define CHECK_ZEPHYR_RESULT(x) do { int res = (x); if (res < 0) raise_zephyr_error(res); } while (0)

View file

@ -0,0 +1,287 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <stdint.h>
#include "bindings/zephyr_serial/UART.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/util.h"
#include "shared/runtime/context_manager_helpers.h"
#include "shared/runtime/interrupt_char.h"
#include "py/stream.h"
#include "py/objproperty.h"
#include "py/objtype.h"
#include "py/runtime.h"
#include "py/stream.h"
#define STREAM_DEBUG(...) (void)0
// #define STREAM_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
//| class UART:
//| """A bidirectional serial protocol. Already initialized for Zephyr defined
//| busses in `board`.
//|
//| .. raw:: html
//|
//| <p>
//| <details>
//| <summary>Available on these boards</summary>
//| <ul>
//| {% for board in support_matrix_reverse["zephyr_serial.UART"] %}
//| <li> {{ board }}
//| {% endfor %}
//| </ul>
//| </details>
//| </p>
//|
//| """
//|
static void validate_timeout(mp_float_t timeout) {
mp_arg_validate_int_range((int)timeout, 0, 100, MP_QSTR_timeout);
}
// Helper to ensure we have the native super class instead of a subclass.
static zephyr_serial_uart_obj_t *native_uart(mp_obj_t uart_obj) {
mp_obj_t native_uart = mp_obj_cast_to_native_base(uart_obj, MP_OBJ_FROM_PTR(&zephyr_serial_uart_type));
if (native_uart == MP_OBJ_NULL) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("Must be a %q subclass."), MP_QSTR_UART);
}
mp_obj_assert_native_inited(native_uart);
return MP_OBJ_TO_PTR(native_uart);
}
//| def deinit(self) -> None:
//| """Deinitialises the UART and releases any hardware resources for reuse."""
//| ...
static mp_obj_t _zephyr_serial_uart_obj_deinit(mp_obj_t self_in) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
zephyr_serial_uart_deinit(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_deinit_obj, _zephyr_serial_uart_obj_deinit);
static void check_for_deinit(zephyr_serial_uart_obj_t *self) {
if (zephyr_serial_uart_deinited(self)) {
raise_deinited_error();
}
}
//| def __enter__(self) -> UART:
//| """No-op used by Context Managers."""
//| ...
// Provided by context manager helper.
//| def __exit__(self) -> None:
//| """Automatically deinitializes the hardware when exiting a context. See
//| :ref:`lifetime-and-contextmanagers` for more info."""
//| ...
static mp_obj_t _zephyr_serial_uart_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
zephyr_serial_uart_deinit(MP_OBJ_TO_PTR(args[0]));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(_zephyr_serial_uart___exit___obj, 4, 4, _zephyr_serial_uart_obj___exit__);
// These are standard stream methods. Code is in py/stream.c.
//
//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]:
//| """Read bytes. If ``nbytes`` is specified then read at most that many
//| bytes. Otherwise, read everything that arrives until the connection
//| times out. Providing the number of bytes expected is highly recommended
//| because it will be faster. If no bytes are read, return ``None``.
//|
//| .. note:: When no bytes are read due to a timeout, this function returns ``None``.
//| This matches the behavior of `io.RawIOBase.read` in Python 3, but
//| differs from pyserial which returns ``b''`` in that situation.
//|
//| :return: Data read
//| :rtype: bytes or None"""
//| ...
//| def readinto(self, buf: WriteableBuffer) -> Optional[int]:
//| """Read bytes into the ``buf``. Read at most ``len(buf)`` bytes.
//|
//| :return: number of bytes read and stored into ``buf``
//| :rtype: int or None (on a non-blocking error)
//|
//| *New in CircuitPython 4.0:* No length parameter is permitted."""
//| ...
//| def readline(self) -> bytes:
//| """Read a line, ending in a newline character, or
//| return ``None`` if a timeout occurs sooner, or
//| return everything readable if no newline is found and
//| ``timeout=0``
//|
//| :return: the line read
//| :rtype: bytes or None"""
//| ...
//| def write(self, buf: ReadableBuffer) -> Optional[int]:
//| """Write the buffer of bytes to the bus.
//|
//| *New in CircuitPython 4.0:* ``buf`` must be bytes, not a string.
//|
//| :return: the number of bytes written
//| :rtype: int or None"""
//| ...
// These three methods are used by the shared stream methods.
static mp_uint_t _zephyr_serial_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
STREAM_DEBUG("_zephyr_serial_uart_read stream %d\n", size);
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
byte *buf = buf_in;
// make sure we want at least 1 char
if (size == 0) {
return 0;
}
return zephyr_serial_uart_read(self, buf, size, errcode);
}
static mp_uint_t _zephyr_serial_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
const byte *buf = buf_in;
return zephyr_serial_uart_write(self, buf, size, errcode);
}
static mp_uint_t _zephyr_serial_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
mp_uint_t ret;
if (request == MP_STREAM_POLL) {
mp_uint_t flags = arg;
ret = 0;
if ((flags & MP_STREAM_POLL_RD) && zephyr_serial_uart_rx_characters_available(self) > 0) {
ret |= MP_STREAM_POLL_RD;
}
if ((flags & MP_STREAM_POLL_WR) && zephyr_serial_uart_ready_to_tx(self)) {
ret |= MP_STREAM_POLL_WR;
}
} else {
*errcode = MP_EINVAL;
ret = MP_STREAM_ERROR;
}
return ret;
}
//| baudrate: int
//| """The current baudrate."""
static mp_obj_t _zephyr_serial_uart_obj_get_baudrate(mp_obj_t self_in) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_get_baudrate(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_baudrate_obj, _zephyr_serial_uart_obj_get_baudrate);
static mp_obj_t _zephyr_serial_uart_obj_set_baudrate(mp_obj_t self_in, mp_obj_t baudrate) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
zephyr_serial_uart_set_baudrate(self, mp_obj_get_int(baudrate));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_baudrate_obj, _zephyr_serial_uart_obj_set_baudrate);
MP_PROPERTY_GETSET(_zephyr_serial_uart_baudrate_obj,
(mp_obj_t)&_zephyr_serial_uart_get_baudrate_obj,
(mp_obj_t)&_zephyr_serial_uart_set_baudrate_obj);
//| in_waiting: int
//| """The number of bytes in the input buffer, available to be read"""
static mp_obj_t _zephyr_serial_uart_obj_get_in_waiting(mp_obj_t self_in) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_rx_characters_available(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_in_waiting_obj, _zephyr_serial_uart_obj_get_in_waiting);
MP_PROPERTY_GETTER(_zephyr_serial_uart_in_waiting_obj,
(mp_obj_t)&_zephyr_serial_uart_get_in_waiting_obj);
//| timeout: float
//| """The current timeout, in seconds (float)."""
static mp_obj_t _zephyr_serial_uart_obj_get_timeout(mp_obj_t self_in) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
return mp_obj_new_float(zephyr_serial_uart_get_timeout(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_timeout_obj, _zephyr_serial_uart_obj_get_timeout);
static mp_obj_t _zephyr_serial_uart_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
mp_float_t timeout_float = mp_obj_get_float(timeout);
validate_timeout(timeout_float);
zephyr_serial_uart_set_timeout(self, timeout_float);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_timeout_obj, _zephyr_serial_uart_obj_set_timeout);
MP_PROPERTY_GETSET(_zephyr_serial_uart_timeout_obj,
(mp_obj_t)&_zephyr_serial_uart_get_timeout_obj,
(mp_obj_t)&_zephyr_serial_uart_set_timeout_obj);
//| def reset_input_buffer(self) -> None:
//| """Discard any unread characters in the input buffer."""
//| ...
//|
static mp_obj_t _zephyr_serial_uart_obj_reset_input_buffer(mp_obj_t self_in) {
zephyr_serial_uart_obj_t *self = native_uart(self_in);
check_for_deinit(self);
zephyr_serial_uart_clear_rx_buffer(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_reset_input_buffer_obj, _zephyr_serial_uart_obj_reset_input_buffer);
static const mp_rom_map_elem_t _zephyr_serial_uart_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&_zephyr_serial_uart___exit___obj) },
// Standard stream methods.
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)},
{ MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&_zephyr_serial_uart_reset_input_buffer_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&_zephyr_serial_uart_baudrate_obj) },
{ MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&_zephyr_serial_uart_in_waiting_obj) },
{ MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&_zephyr_serial_uart_timeout_obj) },
};
static MP_DEFINE_CONST_DICT(_zephyr_serial_uart_locals_dict, _zephyr_serial_uart_locals_dict_table);
static const mp_stream_p_t uart_stream_p = {
.read = _zephyr_serial_uart_read,
.write = _zephyr_serial_uart_write,
.ioctl = _zephyr_serial_uart_ioctl,
.is_text = false,
// Disallow optional length argument for .readinto()
.pyserial_readinto_compatibility = true,
};
MP_DEFINE_CONST_OBJ_TYPE(
zephyr_serial_uart_type,
MP_QSTR_UART,
MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
locals_dict, &_zephyr_serial_uart_locals_dict,
iter, mp_stream_unbuffered_iter,
protocol, &uart_stream_p
);

View file

@ -0,0 +1,41 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "common-hal/microcontroller/Pin.h"
#include "common-hal/zephyr_serial/UART.h"
#include "py/ringbuf.h"
#include <zephyr/device.h>
extern const mp_obj_type_t zephyr_serial_uart_type;
// Construct an underlying UART object.
extern void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self,
const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer);
extern void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self);
extern bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self);
// Read characters. len is in characters NOT bytes!
extern size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self,
uint8_t *data, size_t len, int *errcode);
// Write characters. len is in characters NOT bytes!
extern size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self,
const uint8_t *data, size_t len, int *errcode);
extern uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self);
extern void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate);
extern mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self);
extern void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout);
extern uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self);
extern void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self);
extern bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self);
extern void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self);

View file

@ -0,0 +1,33 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "bindings/zephyr_serial/__init__.h"
#include "bindings/zephyr_serial/UART.h"
#include "py/runtime.h"
//| """Zephyr UART driver for fixed busses.
//| """
static const mp_rom_map_elem_t zephyr_serial_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_serial) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&zephyr_serial_uart_type) },
};
static MP_DEFINE_CONST_DICT(zephyr_serial_module_globals, zephyr_serial_module_globals_table);
const mp_obj_module_t zephyr_serial_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&zephyr_serial_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_zephyr_serial, zephyr_serial_module);

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft
//
// SPDX-License-Identifier: MIT
#pragma once

View file

@ -0,0 +1,8 @@
set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840)
set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5)
set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1)
set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp)
set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp)
set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp)
set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk)
set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx)

View file

@ -0,0 +1,3 @@
&s28hl512t {
/delete-node/ partitions;
};

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "Nordic Semiconductor nRF5340 DK"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = true
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1,3 @@
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]
USB_VID=0x239A
USB_PID=0x8166

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "Nordic Semiconductor nRF54L15 DK"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = false
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1 @@
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "Nordic Semiconductor nRF7002 DK"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = true # Zephyr networking enabled
ssl = true # Zephyr networking enabled
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = true
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = true # Zephyr board has wifi
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1,4 @@
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]
USB_VID=0x239A
USB_PID=0x8168
BLOBS=["nrf_wifi"]

View file

@ -0,0 +1,6 @@
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_EXT_ADV=y

View file

@ -0,0 +1,12 @@
CONFIG_NETWORKING=y
CONFIG_WIFI=y
CONFIG_MBEDTLS_TLS_VERSION_1_2=y
CONFIG_MBEDTLS_USE_PSA_CRYPTO=n
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_EXT_ADV=y

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "Renesas Electronics Corporation RA6M5 Evaluation Kit"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = false # No TinyUSB settings for r7fa6m5bh3cfc
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1 @@
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "Renesas Electronics Corporation RA8D1 Evaluation Kit"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = false # No TinyUSB settings for r7fa8d1bhecbd
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1 @@
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "STMicroelectronics Nucleo U575ZI Q"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = false
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = true
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1,3 @@
CIRCUITPY_BUILD_EXTENSIONS = ["hex"]
USB_VID=0x239A
USB_PID=0x816A

View file

@ -0,0 +1,109 @@
# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info.
name = "STMicroelectronics STM32H7B3I Discovery kit"
[modules]
__future__ = false
_bleio = false
_eve = false
_pew = false
_pixelmap = false
_stage = false
adafruit_bus_device = false
adafruit_pixelbuf = false
aesio = false
alarm = false
analogbufio = false
analogio = false
atexit = false
audiobusio = false
audiocore = false
audiodelays = false
audiofilters = false
audioio = false
audiomixer = false
audiomp3 = false
audiopwmio = false
aurora_epaper = false
bitbangio = false
bitmapfilter = false
bitmaptools = false
bitops = false
board = false
busdisplay = false
busio = false
camera = false
canio = false
codeop = false
countio = false
digitalio = true
displayio = false
dotclockframebuffer = false
dualbank = false
epaperdisplay = false
floppyio = false
fontio = false
fourwire = false
framebufferio = false
frequencyio = false
getpass = false
gifio = false
gnss = false
hashlib = false
i2cdisplaybus = false
i2ctarget = false
imagecapture = false
ipaddress = false
is31fl3741 = false
jpegio = false
keypad = false
keypad_demux = false
locale = false
math = false
max3421e = false
mdns = false
memorymap = false
memorymonitor = false
microcontroller = true
msgpack = false
neopixel_write = false
nvm = false
onewireio = false
os = true
paralleldisplaybus = false
ps2io = false
pulseio = false
pwmio = false
qrio = false
rainbowio = false
random = true
rgbmatrix = false
rotaryio = false
rtc = false
sdcardio = false
sdioio = false
sharpdisplay = false
socketpool = false
ssl = false
storage = true # Zephyr board has flash
struct = true
supervisor = false
synthio = false
terminalio = false
time = true
touchio = false
traceback = false
uheap = false
usb = false
usb_cdc = false
usb_hid = false
usb_host = false
usb_midi = false
usb_video = false
ustack = false
vectorio = false
warnings = false
watchdog = false
wifi = false
zephyr_kernel = false
zephyr_serial = true
zlib = false

View file

@ -0,0 +1 @@
CIRCUITPY_BUILD_EXTENSIONS = ["hex"]

View file

@ -0,0 +1,7 @@
&mx25lm51245 {
/delete-node/ partitions;
};
&rng {
status = "okay";
};

View file

@ -0,0 +1,129 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/digitalio/DigitalInOut.h"
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
void common_hal_digitalio_digitalinout_never_reset(
digitalio_digitalinout_obj_t *self) {
}
digitalinout_result_t common_hal_digitalio_digitalinout_construct(
digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin) {
claim_pin(pin);
self->pin = pin;
if (!device_is_ready(pin->port)) {
printk("Port device not ready\n");
return DIGITALINOUT_PIN_BUSY;
}
if (gpio_pin_configure(pin->port, pin->number, GPIO_INPUT) != 0) {
return DIGITALINOUT_PIN_BUSY;
}
self->direction = DIRECTION_INPUT;
return DIGITALINOUT_OK;
}
bool common_hal_digitalio_digitalinout_deinited(digitalio_digitalinout_obj_t *self) {
return self->pin == NULL;
}
void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t *self) {
if (common_hal_digitalio_digitalinout_deinited(self)) {
return;
}
reset_pin(self->pin);
self->pin = NULL;
}
digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_input(
digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
self->direction = DIRECTION_INPUT;
common_hal_digitalio_digitalinout_set_pull(self, pull);
return DIGITALINOUT_OK;
}
digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
digitalio_digitalinout_obj_t *self, bool value,
digitalio_drive_mode_t drive_mode) {
self->direction = DIRECTION_OUTPUT;
common_hal_digitalio_digitalinout_set_drive_mode(self, drive_mode);
common_hal_digitalio_digitalinout_set_value(self, value);
return DIGITALINOUT_OK;
}
digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
digitalio_digitalinout_obj_t *self) {
return self->direction;
}
void common_hal_digitalio_digitalinout_set_value(
digitalio_digitalinout_obj_t *self, bool value) {
int res = gpio_pin_set(self->pin->port, self->pin->number, value);
if (res != 0) {
printk("Failed to set value %d\n", res);
}
// Not all MCUs can read back the output value, so store it.
self->value = value;
}
bool common_hal_digitalio_digitalinout_get_value(
digitalio_digitalinout_obj_t *self) {
if (self->direction == DIRECTION_OUTPUT) {
return self->value;
}
return gpio_pin_get(self->pin->port, self->pin->number) == 1;
}
digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode(
digitalio_digitalinout_obj_t *self,
digitalio_drive_mode_t drive_mode) {
// Also INPUT so we can read the value back.
gpio_flags_t flags = GPIO_OUTPUT;
if (drive_mode == DRIVE_MODE_OPEN_DRAIN) {
flags |= GPIO_OPEN_DRAIN;
}
int res = gpio_pin_configure(self->pin->port, self->pin->number, flags);
if (res != 0) {
// TODO: Fake open drain.
printk("Failed to set drive mode %d\n", res);
return DIGITALINOUT_INVALID_DRIVE_MODE;
}
self->drive_mode = drive_mode;
return DIGITALINOUT_OK;
}
digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode(
digitalio_digitalinout_obj_t *self) {
return self->drive_mode;
}
digitalinout_result_t common_hal_digitalio_digitalinout_set_pull(
digitalio_digitalinout_obj_t *self, digitalio_pull_t pull) {
gpio_flags_t pull_flags = 0;
if (pull == PULL_UP) {
pull_flags = GPIO_PULL_UP;
} else if (pull == PULL_DOWN) {
pull_flags = GPIO_PULL_DOWN;
}
if (gpio_pin_configure(self->pin->port, self->pin->number, GPIO_INPUT | pull_flags) != 0) {
return DIGITALINOUT_INVALID_PULL;
}
self->pull = pull;
return DIGITALINOUT_OK;
}
digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(
digitalio_digitalinout_obj_t *self) {
return self->pull;
}

View file

@ -0,0 +1,21 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "common-hal/microcontroller/Pin.h"
#include "shared-bindings/digitalio/Direction.h"
#include "shared-bindings/digitalio/DriveMode.h"
#include "shared-bindings/digitalio/Pull.h"
typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t *pin;
digitalio_direction_t direction;
bool value;
digitalio_drive_mode_t drive_mode;
digitalio_pull_t pull;
} digitalio_digitalinout_obj_t;

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC
//
// SPDX-License-Identifier: MIT
// No digitalio module functions.

View file

@ -0,0 +1,73 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "py/mphal.h"
// Bit mask of claimed pins on each of up to two ports. nrf52832 has one port; nrf52840 has two.
// static uint32_t claimed_pins[GPIO_COUNT];
// static uint32_t never_reset_pins[GPIO_COUNT];
void reset_all_pins(void) {
// for (size_t i = 0; i < GPIO_COUNT; i++) {
// claimed_pins[i] = never_reset_pins[i];
// }
// for (uint32_t pin = 0; pin < NUMBER_OF_PINS; ++pin) {
// if ((never_reset_pins[nrf_pin_port(pin)] & (1 << nrf_relative_pin_number(pin))) != 0) {
// continue;
// }
// nrf_gpio_cfg_default(pin);
// }
// // After configuring SWD because it may be shared.
// reset_speaker_enable_pin();
}
// Mark pin as free and return it to a quiescent state.
void reset_pin(const mcu_pin_obj_t *pin) {
// Clear claimed bit.
// claimed_pins[nrf_pin_port(pin_number)] &= ~(1 << nrf_relative_pin_number(pin_number));
// never_reset_pins[nrf_pin_port(pin_number)] &= ~(1 << nrf_relative_pin_number(pin_number));
}
void never_reset_pin_number(uint8_t pin_number) {
// never_reset_pins[nrf_pin_port(pin_number)] |= 1 << nrf_relative_pin_number(pin_number);
}
void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) {
never_reset_pin_number(pin->number);
}
void common_hal_reset_pin(const mcu_pin_obj_t *pin) {
if (pin == NULL) {
return;
}
reset_pin(pin);
}
void claim_pin(const mcu_pin_obj_t *pin) {
// Set bit in claimed_pins bitmask.
// claimed_pins[nrf_pin_port(pin->number)] |= 1 << nrf_relative_pin_number(pin->number);
}
bool pin_number_is_free(uint8_t pin_number) {
return false; // !(claimed_pins[nrf_pin_port(pin_number)] & (1 << nrf_relative_pin_number(pin_number)));
}
bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) {
return true;
}
void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
claim_pin(pin);
}

View file

@ -0,0 +1,23 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/mphal.h"
#include <zephyr/drivers/gpio.h>
typedef struct {
mp_obj_base_t base;
const struct device *port;
gpio_pin_t number;
} mcu_pin_obj_t;
#include "autogen-pins.h"
void reset_all_pins(void);
void reset_pin(const mcu_pin_obj_t *pin);
void claim_pin(const mcu_pin_obj_t *pin);

View file

@ -0,0 +1,46 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Dan Halbert for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "py/runtime.h"
#include "common-hal/microcontroller/Processor.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "shared-bindings/microcontroller/ResetReason.h"
#include <sys/types.h>
#include <zephyr/drivers/hwinfo.h>
float common_hal_mcu_processor_get_temperature(void) {
return 0.0;
}
extern uint32_t SystemCoreClock;
uint32_t common_hal_mcu_processor_get_frequency(void) {
return SystemCoreClock;
}
float common_hal_mcu_processor_get_voltage(void) {
return 3.3f;
}
void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
ssize_t len = hwinfo_get_device_id(raw_id, COMMON_HAL_MCU_PROCESSOR_UID_LENGTH);
if (len < 0) {
printk("UID retrieval failed: %d\n", len);
len = 0;
}
if (len < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH) {
printk("UID shorter %d than defined length %d\n", len, COMMON_HAL_MCU_PROCESSOR_UID_LENGTH);
memset(raw_id + len, 0, COMMON_HAL_MCU_PROCESSOR_UID_LENGTH - len);
}
}
mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void) {
mcu_reset_reason_t r = RESET_REASON_UNKNOWN;
return r;
}

View file

@ -0,0 +1,23 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Dan Halbert for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#if CONFIG_HWINFO_NRF
#define COMMON_HAL_MCU_PROCESSOR_UID_LENGTH 8
#else
// Extra long and the remainder will be left empty. Will print out the actual length.
#define COMMON_HAL_MCU_PROCESSOR_UID_LENGTH 32
#endif
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
// Stores no state currently.
} mcu_processor_obj_t;
extern uint32_t reset_reason_saved;

View file

@ -0,0 +1,116 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "py/mphal.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "common-hal/microcontroller/Pin.h"
#include "common-hal/microcontroller/Processor.h"
// #include "shared-bindings/nvm/ByteArray.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/microcontroller/Processor.h"
#include "supervisor/filesystem.h"
#include "supervisor/port.h"
#include "supervisor/shared/safe_mode.h"
#include <zephyr/kernel.h>
// This routine should work even when interrupts are disabled. Used by OneWire
// for precise timing.
void common_hal_mcu_delay_us(uint32_t delay) {
}
static uint32_t _irq_key;
static volatile uint32_t nesting_count = 0;
void common_hal_mcu_disable_interrupts() {
if (nesting_count == 0) {
// Unlike __disable_irq(), this should only be called the first time
// "is_nested_critical_region" is sd's equivalent of our nesting count
// so a nested call would store 0 in the global and make the later
// exit call not actually re-enable interrupts
//
// This only disables interrupts of priority 2 through 7; levels 0, 1,
// and 4, are exclusive to softdevice and should never be used, so
// this limitation is not important.
// sd_nvic_critical_region_enter(&is_nested_critical_region);
if (!k_is_in_isr()) {
k_sched_lock();
}
_irq_key = irq_lock();
}
nesting_count++;
}
void common_hal_mcu_enable_interrupts() {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables.
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
return;
}
irq_unlock(_irq_key);
if (!k_is_in_isr()) {
k_sched_unlock();
}
}
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
enum { DFU_MAGIC_UF2_RESET = 0x57 };
uint8_t new_value = 0;
if (runmode == RUNMODE_BOOTLOADER || runmode == RUNMODE_UF2) {
new_value = DFU_MAGIC_UF2_RESET;
}
// int err_code = sd_power_gpregret_set(0, DFU_MAGIC_UF2_RESET);
// if (err_code != NRF_SUCCESS) {
// // Set it without the soft device if the SD failed. (It may be off.)
// nrf_power_gpregret_set(NRF_POWER, new_value);
// }
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}
void common_hal_mcu_reset(void) {
filesystem_flush();
reset_cpu();
}
// The singleton microcontroller.Processor object, bound to microcontroller.cpu
// It currently only has properties, and no state.
const mcu_processor_obj_t common_hal_mcu_processor_obj = {
.base = {
.type = &mcu_processor_type,
},
};
#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0
// The singleton nvm.ByteArray object.
const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = {
.base = {
.type = &nvm_bytearray_type,
},
.start_address = (uint8_t *)CIRCUITPY_INTERNAL_NVM_START_ADDR,
.len = CIRCUITPY_INTERNAL_NVM_SIZE,
};
#endif
#if CIRCUITPY_WATCHDOG
// The singleton watchdog.WatchDogTimer object.
watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj = {
.base = {
.type = &watchdog_watchdogtimer_type,
},
.timeout = 0.0f,
.mode = WATCHDOGMODE_NONE,
};
#endif

View file

@ -0,0 +1,48 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "genhdr/mpversion.h"
#include "py/mpconfig.h"
#include "py/objstr.h"
#include "py/objtuple.h"
#include "shared-bindings/os/__init__.h"
#include <zephyr/random/random.h>
static const qstr os_uname_info_fields[] = {
MP_QSTR_sysname, MP_QSTR_nodename,
MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
};
static const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, "nrf52");
static const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, "nrf52");
static const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING);
static const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE);
static const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME);
static MP_DEFINE_ATTRTUPLE(
os_uname_info_obj,
os_uname_info_fields,
5,
(mp_obj_t)&os_uname_info_sysname_obj,
(mp_obj_t)&os_uname_info_nodename_obj,
(mp_obj_t)&os_uname_info_release_obj,
(mp_obj_t)&os_uname_info_version_obj,
(mp_obj_t)&os_uname_info_machine_obj
);
mp_obj_t common_hal_os_uname(void) {
return (mp_obj_t)&os_uname_info_obj;
}
bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length) {
#if !DT_HAS_CHOSEN(zephyr_entropy)
return false;
#else
return sys_csrand_get(buffer, length) == 0;
#endif
}

View file

@ -0,0 +1,697 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Lucian Copeland for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/socketpool/Socket.h"
#include "shared/runtime/interrupt_char.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "shared-bindings/socketpool/SocketPool.h"
#include "common-hal/socketpool/__init__.h"
#include "common-hal/wifi/__init__.h"
#if CIRCUITPY_SSL
#include "shared-bindings/ssl/SSLSocket.h"
#include "shared-module/ssl/SSLSocket.h"
#endif
#include "supervisor/port.h"
#include "supervisor/shared/tick.h"
#include "supervisor/workflow.h"
#include <zephyr/net/net_ip.h>
// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) {
// // struct addrinfo *result_i;
// // const struct addrinfo hints = {
// // .ai_family = family,
// // .ai_socktype = type,
// // };
// // int error = socketpool_getaddrinfo_common(hostname, port, &hints, &result_i);
// if (true) {
// common_hal_socketpool_socketpool_raise_gaierror_noname();
// }
// // memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage));
// // lwip_freeaddrinfo(result_i);
// }
// static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) {
// socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port);
// }
// StackType_t socket_select_stack[2 * configMINIMAL_STACK_SIZE];
// /* Socket state table:
// * 0 := Closed (unused)
// * 1 := Open
// * 2 := Closing (remove from rfds)
// * Index into socket_fd_state is calculated from actual lwip fd. idx := fd - LWIP_SOCKET_OFFSET
// */
// #define FDSTATE_CLOSED 0
// #define FDSTATE_OPEN 1
// #define FDSTATE_CLOSING 2
// static uint8_t socket_fd_state[CONFIG_LWIP_MAX_SOCKETS];
// How long to wait between checks for a socket to connect.
#define SOCKET_CONNECT_POLL_INTERVAL_MS 100
// static socketpool_socket_obj_t *user_socket[CONFIG_LWIP_MAX_SOCKETS];
// StaticTask_t socket_select_task_buffer;
// TaskHandle_t socket_select_task_handle;
// static int socket_change_fd = -1;
// static void socket_select_task(void *arg) {
// uint64_t signal;
// fd_set readfds;
// fd_set excptfds;
// while (true) {
// FD_ZERO(&readfds);
// FD_ZERO(&excptfds);
// FD_SET(socket_change_fd, &readfds);
// int max_fd = socket_change_fd;
// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) {
// if ((socket_fd_state[i] == FDSTATE_OPEN) && (user_socket[i] == NULL)) {
// int sockfd = i + LWIP_SOCKET_OFFSET;
// max_fd = MAX(max_fd, sockfd);
// FD_SET(sockfd, &readfds);
// FD_SET(sockfd, &excptfds);
// }
// }
// int num_triggered = select(max_fd + 1, &readfds, NULL, &excptfds, NULL);
// // Hard error (or someone closed a socket on another thread)
// if (num_triggered == -1) {
// assert(errno == EBADF);
// continue;
// }
// assert(num_triggered > 0);
// // Notice event trigger
// if (FD_ISSET(socket_change_fd, &readfds)) {
// read(socket_change_fd, &signal, sizeof(signal));
// num_triggered--;
// }
// // Handle active FDs, close the dead ones
// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) {
// int sockfd = i + LWIP_SOCKET_OFFSET;
// if (socket_fd_state[i] != FDSTATE_CLOSED) {
// if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &excptfds)) {
// if (socket_fd_state[i] == FDSTATE_CLOSING) {
// socket_fd_state[i] = FDSTATE_CLOSED;
// num_triggered--;
// }
// }
// }
// }
// if (num_triggered > 0) {
// // Wake up CircuitPython by queuing request
// supervisor_workflow_request_background();
// ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// }
// }
// close(socket_change_fd);
// socket_change_fd = -1;
// vTaskDelete(NULL);
// }
void socket_user_reset(void) {
// if (socket_change_fd < 0) {
// esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
// ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config));
// // Clear initial socket states
// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) {
// socket_fd_state[i] = FDSTATE_CLOSED;
// user_socket[i] = NULL;
// }
// socket_change_fd = eventfd(0, 0);
// // Run this at the same priority as CP so that the web workflow background task can be
// // queued while CP is running. Both tasks can still sleep and, therefore, sleep overall.
// socket_select_task_handle = xTaskCreateStaticPinnedToCore(socket_select_task,
// "socket_select",
// 2 * configMINIMAL_STACK_SIZE,
// NULL,
// uxTaskPriorityGet(NULL),
// socket_select_stack,
// &socket_select_task_buffer,
// xPortGetCoreID());
// } else {
// // Not init - close open user sockets
// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) {
// if ((socket_fd_state[i] == FDSTATE_OPEN) && user_socket[i]) {
// common_hal_socketpool_socket_close(user_socket[i]);
// }
// }
// }
}
// Unblock select task (ok if not blocked yet)
void socketpool_socket_poll_resume(void) {
// if (socket_select_task_handle) {
// xTaskNotifyGive(socket_select_task_handle);
// }
}
// The writes below send an event to the socket select task so that it redoes the
// select with the new open socket set.
// static bool register_open_socket(int fd) {
// if (fd < FD_SETSIZE) {
// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN;
// user_socket[fd - LWIP_SOCKET_OFFSET] = NULL;
// uint64_t signal = 1;
// write(socket_change_fd, &signal, sizeof(signal));
// socketpool_socket_poll_resume();
// return true;
// }
// return false;
// }
// static void mark_user_socket(int fd, socketpool_socket_obj_t *obj) {
// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN;
// user_socket[fd - LWIP_SOCKET_OFFSET] = obj;
// // No need to wakeup select task
// }
static bool _socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type,
int proto,
socketpool_socket_obj_t *sock) {
int addr_family;
int ipproto;
if (family == SOCKETPOOL_AF_INET) {
addr_family = AF_INET;
ipproto = IPPROTO_IP;
#if CIRCUITPY_SOCKETPOOL_IPV6
} else { // INET6
addr_family = AF_INET6;
ipproto = IPPROTO_IPV6;
#endif
}
int socket_type;
if (type == SOCKETPOOL_SOCK_STREAM) {
socket_type = SOCK_STREAM;
} else if (type == SOCKETPOOL_SOCK_DGRAM) {
socket_type = SOCK_DGRAM;
} else { // SOCKETPOOL_SOCK_RAW
socket_type = SOCK_RAW;
ipproto = proto;
}
sock->type = socket_type;
sock->family = addr_family;
sock->ipproto = ipproto;
sock->pool = self;
sock->timeout_ms = (uint)-1;
// Create LWIP socket
// int socknum = -1;
// socknum = lwip_socket(sock->family, sock->type, sock->ipproto);
// if (socknum < 0) {
// return false;
// }
// sock->num = socknum;
// // Sockets should be nonblocking in most cases
// lwip_fcntl(socknum, F_SETFL, O_NONBLOCK);
return true;
}
// special entry for workflow listener (register system socket)
bool socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type,
int proto, socketpool_socket_obj_t *sock) {
if (!_socketpool_socket(self, family, type, proto, sock)) {
return false;
}
// This shouldn't happen since we have room for the same number of sockets as LWIP.
// if (!register_open_socket(sock->num)) {
// lwip_close(sock->num);
// return false;
// }
return true;
}
socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto) {
switch (family) {
#if CIRCUITPY_SOCKETPOOL_IPV6
case SOCKETPOOL_AF_INET6:
#endif
case SOCKETPOOL_AF_INET:
break;
default:
mp_raise_NotImplementedError(MP_ERROR_TEXT("Unsupported socket type"));
}
socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, &socketpool_socket_type);
if (!_socketpool_socket(self, family, type, proto, sock)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets"));
}
// mark_user_socket(sock->num, sock);
return sock;
}
int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) {
struct sockaddr_storage peer_addr;
// socklen_t socklen = sizeof(peer_addr);
int newsoc = -1;
bool timed_out = false;
uint64_t start_ticks = supervisor_ticks_ms64();
// Allow timeouts and interrupts
while (newsoc == -1 && !timed_out) {
if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) {
timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms;
}
RUN_BACKGROUND_TASKS;
// newsoc = lwip_accept(self->num, (struct sockaddr *)&peer_addr, &socklen);
// In non-blocking mode, fail instead of timing out
if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) {
return -MP_EAGAIN;
}
}
if (timed_out) {
return -ETIMEDOUT;
}
if (newsoc < 0) {
return -MP_EBADF;
}
// We got a socket. New client socket will not be non-blocking by default, so make it non-blocking.
// lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK);
if (accepted != NULL) {
// Error if called with open socket object.
assert(common_hal_socketpool_socket_get_closed(accepted));
// Register if system socket
// if (!register_open_socket(newsoc)) {
// lwip_close(newsoc);
// return -MP_EBADF;
// }
// Replace the old accepted socket with the new one.
accepted->num = newsoc;
accepted->pool = self->pool;
accepted->connected = true;
accepted->type = self->type;
}
if (peer_out) {
*peer_out = sockaddr_to_tuple(&peer_addr);
}
return newsoc;
}
socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out) {
// Set the socket type only after the socketpool_socket_accept succeeds, so that the
// finaliser is not called on a bad socket.
socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, NULL);
int newsoc = socketpool_socket_accept(self, peer_out, NULL);
if (newsoc > 0) {
// Create the socket
// mark_user_socket(newsoc, sock);
sock->base.type = &socketpool_socket_type;
sock->num = newsoc;
sock->pool = self->pool;
sock->connected = true;
sock->type = self->type;
return sock;
} else {
mp_raise_OSError(-newsoc);
return NULL;
}
}
size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self,
const char *host, size_t hostlen, uint32_t port) {
// struct sockaddr_storage bind_addr;
const char *broadcast = "<broadcast>";
// bind_addr.ss_family = self->family;
#if CIRCUITPY_SOCKETPOOL_IPV6
if (self->family == AF_INET6) {
struct sockaddr_in6 *addr6 = (void *)&bind_addr;
addr6->sin6_port = htons(port);
// no ipv6 broadcast
if (hostlen == 0) {
memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr));
} else {
socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port);
}
} else
#endif
{
// struct sockaddr_in *addr4 = (void *)&bind_addr;
// addr4->sin_port = htons(port);
if (hostlen == 0) {
// addr4->sin_addr.s_addr = IPADDR_ANY;
} else if (hostlen == strlen(broadcast) &&
memcmp(host, broadcast, strlen(broadcast)) == 0) {
// addr4->sin_addr.s_addr = IPADDR_BROADCAST;
} else {
// socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port);
}
}
// int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
// if (result == 0) {
// return 0;
// }
return errno;
}
void socketpool_socket_close(socketpool_socket_obj_t *self) {
#if CIRCUITPY_SSL
if (self->ssl_socket) {
ssl_sslsocket_obj_t *ssl_socket = self->ssl_socket;
self->ssl_socket = NULL;
common_hal_ssl_sslsocket_close(ssl_socket);
return;
}
#endif
self->connected = false;
// int fd = self->num;
// Ignore bogus/closed sockets
// if (fd >= LWIP_SOCKET_OFFSET) {
// if (user_socket[fd - LWIP_SOCKET_OFFSET] == NULL) {
// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSING;
// lwip_shutdown(fd, SHUT_RDWR);
// lwip_close(fd);
// } else {
// lwip_shutdown(fd, SHUT_RDWR);
// lwip_close(fd);
// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSED;
// user_socket[fd - LWIP_SOCKET_OFFSET] = NULL;
// }
// }
self->num = -1;
}
void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) {
socketpool_socket_close(self);
}
void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self,
const char *host, size_t hostlen, uint32_t port) {
// struct sockaddr_storage addr;
// resolve_host_or_throw(self, host, &addr, port);
// Replace above with function call -----
// Emulate SO_CONTIMEO, which is not implemented by lwip.
// All our sockets are non-blocking, so we check the timeout ourselves.
int result = -1;
// result = lwip_connect(self->num, (struct sockaddr *)&addr, addr.s2_len);
if (result == 0) {
// Connected immediately.
self->connected = true;
return;
}
if (result < 0 && errno != EINPROGRESS) {
// Some error happened; error is in errno.
mp_raise_OSError(errno);
return;
}
// struct timeval timeout = {
// .tv_sec = 0,
// .tv_usec = SOCKET_CONNECT_POLL_INTERVAL_MS * 1000,
// };
// Keep checking, using select(), until timeout expires, at short intervals.
// This allows ctrl-C interrupts to be detected and background tasks to run.
mp_uint_t timeout_left = self->timeout_ms;
while (timeout_left > 0) {
RUN_BACKGROUND_TASKS;
// Allow ctrl-C interrupt
if (mp_hal_is_interrupted()) {
return;
}
// fd_set fds;
// FD_ZERO(&fds);
// FD_SET(self->num, &fds);
// result = select(self->num + 1, NULL, &fds, NULL, &timeout);
if (result == 0) {
// No change to fd's after waiting for timeout, so try again if some time is still left.
// Don't wrap below 0, because we're using a uint.
if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) {
timeout_left = 0;
} else {
timeout_left -= SOCKET_CONNECT_POLL_INTERVAL_MS;
}
continue;
}
if (result < 0) {
// Some error happened when doing select(); error is in errno.
mp_raise_OSError(errno);
}
// select() indicated the socket is writable. Check if any connection error occurred.
int error_code = 0;
// socklen_t socklen = sizeof(error_code);
// result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen);
if (result < 0 || error_code != 0) {
mp_raise_OSError(errno);
}
self->connected = true;
return;
}
// No connection after timeout. The connection attempt is not stopped.
// This imitates what happens in Python.
mp_raise_OSError(ETIMEDOUT);
}
bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self) {
return self->num < 0;
}
bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) {
return self->connected;
}
bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) {
// return lwip_listen(self->num, backlog) == 0;
return false;
}
mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self,
uint8_t *buf, uint32_t len, mp_obj_t *source_out) {
// struct sockaddr_storage source_addr;
// socklen_t socklen = sizeof(source_addr);
// LWIP Socket
uint64_t start_ticks = supervisor_ticks_ms64();
int received = -1;
bool timed_out = false;
while (received == -1 &&
!timed_out &&
!mp_hal_is_interrupted()) {
if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) {
timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms;
}
RUN_BACKGROUND_TASKS;
// received = lwip_recvfrom(self->num, buf, len, 0, (struct sockaddr *)&source_addr, &socklen);
// In non-blocking mode, fail instead of looping
if (received == -1 && self->timeout_ms == 0) {
mp_raise_OSError(MP_EAGAIN);
}
}
if (timed_out) {
mp_raise_OSError(ETIMEDOUT);
}
if (received < 0) {
mp_raise_BrokenPipeError();
return 0;
}
if (source_out) {
// *source_out = sockaddr_to_tuple(&source_addr);
}
return received;
}
int socketpool_socket_recv_into(socketpool_socket_obj_t *self,
const uint8_t *buf, uint32_t len) {
int received = 0;
bool timed_out = false;
if (self->num != -1) {
// LWIP Socket
uint64_t start_ticks = supervisor_ticks_ms64();
received = -1;
while (received == -1 &&
!timed_out) {
if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) {
timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms;
}
RUN_BACKGROUND_TASKS;
// received = lwip_recv(self->num, (void *)buf, len, 0);
// In non-blocking mode, fail instead of looping
if (received < 1 && self->timeout_ms == 0) {
if ((received == 0) || (errno == ENOTCONN)) {
self->connected = false;
return -MP_ENOTCONN;
}
return -MP_EAGAIN;
}
// Check this after going through the loop once so it can make
// progress while interrupted.
if (mp_hal_is_interrupted()) {
if (received == -1) {
return -MP_EAGAIN;
}
break;
}
}
} else {
return -MP_EBADF;
}
if (timed_out) {
return -ETIMEDOUT;
}
return received;
}
mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int received = socketpool_socket_recv_into(self, buf, len);
if (received < 0) {
mp_raise_OSError(-received);
}
return received;
}
int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int sent = -1;
if (self->num != -1) {
// LWIP Socket
// TODO: deal with potential failure/add timeout?
// sent = lwip_send(self->num, buf, len, 0);
} else {
sent = -MP_EBADF;
}
if (sent < 0) {
if (errno == ECONNRESET || errno == ENOTCONN) {
self->connected = false;
}
return -errno;
}
return sent;
}
mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int sent = socketpool_socket_send(self, buf, len);
if (sent < 0) {
mp_raise_OSError(-sent);
}
return sent;
}
mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t *self,
const char *host, size_t hostlen, uint32_t port, const uint8_t *buf, uint32_t len) {
// struct sockaddr_storage addr;
// resolve_host_or_throw(self, host, &addr, port);
// int bytes_sent = lwip_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, addr.s2_len);
// if (bytes_sent < 0) {
// mp_raise_BrokenPipeError();
// return 0;
// }
int bytes_sent = 0;
return bytes_sent;
}
void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms) {
self->timeout_ms = timeout_ms;
}
mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) {
return self->type;
}
int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) {
int err = 0; // lwip_setsockopt(self->num, level, optname, value, optlen);
if (err != 0) {
return -errno;
}
return 0;
}
bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) {
// struct timeval immediate = {0, 0};
// fd_set fds;
// FD_ZERO(&fds);
// FD_SET(self->num, &fds);
// int num_triggered = select(self->num + 1, &fds, NULL, &fds, &immediate);
// including returning true in the error case
// return num_triggered != 0;
return false;
}
bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) {
// struct timeval immediate = {0, 0};
// fd_set fds;
// FD_ZERO(&fds);
// FD_SET(self->num, &fds);
// int num_triggered = select(self->num + 1, NULL, &fds, &fds, &immediate);
// including returning true in the error case
// return num_triggered != 0;
return false;
}
void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock) {
*sock = *self;
self->connected = false;
self->num = -1;
}
void socketpool_socket_reset(socketpool_socket_obj_t *self) {
if (self->base.type == &socketpool_socket_type) {
return;
}
self->base.type = &socketpool_socket_type;
self->connected = false;
self->num = -1;
}

View file

@ -0,0 +1,29 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
#include "common-hal/socketpool/SocketPool.h"
typedef struct ssl_sslsocket_obj ssl_sslsocket_obj_t;
typedef struct {
mp_obj_base_t base;
int num;
int type;
int family;
int ipproto;
bool connected;
socketpool_socketpool_obj_t *pool;
ssl_sslsocket_obj_t *ssl_socket;
mp_uint_t timeout_ms;
} socketpool_socket_obj_t;
void socket_user_reset(void);
// Unblock workflow socket select thread (platform specific)
void socketpool_socket_poll_resume(void);

View file

@ -0,0 +1,121 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/socketpool/SocketPool.h"
#include "common-hal/socketpool/Socket.h"
#include "py/runtime.h"
#include "shared-bindings/wifi/__init__.h"
#include "common-hal/socketpool/__init__.h"
void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) {
if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) {
mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio"));
}
}
// common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking.
// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res) {
// // As of 2022, the version of lwip in esp-idf does not handle the
// // trailing-dot syntax of domain names, so emulate it.
// // Remove this once https://github.com/espressif/esp-idf/issues/10013 has
// // been implemented
// if (host) {
// size_t strlen_host = strlen(host);
// if (strlen_host && host[strlen_host - 1] == '.') {
// mp_obj_t nodot = mp_obj_new_str(host, strlen_host - 1);
// host = mp_obj_str_get_str(nodot);
// }
// }
// // char service_buf[6];
// // snprintf(service_buf, sizeof(service_buf), "%d", service);
// // return lwip_getaddrinfo(host, service_buf, hints, res);
// return 0;
// }
// static mp_obj_t format_address(const struct sockaddr *addr, int family) {
// char ip_str[IPADDR_STRLEN_MAX]; // big enough for any supported address type
// const struct sockaddr_in *a = (void *)addr;
// switch (family) {
// #if CIRCUITPY_SOCKETPOOL_IPV6
// case AF_INET6:
// inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str));
// break;
// #endif
// default:
// case AF_INET:
// inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str));
// break;
// }
// return mp_obj_new_str(ip_str, strlen(ip_str));
// }
// static mp_obj_t convert_sockaddr(const struct addrinfo *ai, int port) {
// #if CIRCUITPY_SOCKETPOOL_IPV6
// mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2;
// #else
// mp_int_t n_tuple = 2;
// #endif
// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL));
// result->items[0] = format_address(ai->ai_addr, ai->ai_family);
// result->items[1] = MP_OBJ_NEW_SMALL_INT(port);
// #if CIRCUITPY_SOCKETPOOL_IPV6
// if (ai->ai_family == AF_INET6) {
// const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr;
// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo);
// result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id);
// }
// #endif
// return result;
// }
// static mp_obj_t convert_addrinfo(const struct addrinfo *ai, int port) {
// MP_STATIC_ASSERT(AF_INET == SOCKETPOOL_AF_INET);
// #if CIRCUITPY_SOCKETPOOL_IPV6
// MP_STATIC_ASSERT(AF_INET6 == SOCKETPOOL_AF_INET6);
// #endif
// // MP_STATIC_ASSERT(AF_UNSPEC == SOCKETPOOL_AF_UNSPEC);
// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
// result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family);
// result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype);
// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol);
// result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_);
// result->items[4] = convert_sockaddr(ai, port);
// return result;
// }
mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags) {
// const struct addrinfo hints = {
// .ai_flags = flags,
// .ai_family = family,
// .ai_protocol = proto,
// .ai_socktype = type,
// };
// struct addrinfo *res = NULL;
// int err = socketpool_getaddrinfo_common(host, port, &hints, &res);
if (true) {
common_hal_socketpool_socketpool_raise_gaierror_noname();
}
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_obj_t result = mp_obj_new_list(0, NULL);
// for (struct addrinfo *ai = res; ai; ai = ai->ai_next) {
// mp_obj_list_append(result, convert_addrinfo(ai, port));
// }
nlr_pop();
// lwip_freeaddrinfo(res);
return result;
} else {
// lwip_freeaddrinfo(res);
nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val));
}
}

View file

@ -0,0 +1,13 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
} socketpool_socketpool_obj_t;

View file

@ -0,0 +1,13 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/socketpool/__init__.h"
#include "common-hal/socketpool/Socket.h"
void socketpool_user_reset(void) {
socket_user_reset();
}

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once

View file

@ -0,0 +1,146 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2021 microDev
//
// SPDX-License-Identifier: MIT
#include <string.h>
#include "py/mpstate.h"
#include "py/runtime.h"
#include "shared-bindings/wifi/Monitor.h"
#include "shared-bindings/wifi/Packet.h"
#define MONITOR_PAYLOAD_FCS_LEN (4)
#define MONITOR_QUEUE_TIMEOUT_TICK (0)
typedef struct {
void *payload;
unsigned channel;
uint32_t length;
signed rssi;
} monitor_packet_t;
// static void wifi_monitor_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type) {
// wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)recv_buf;
// // prepare packet
// monitor_packet_t packet = {
// .channel = pkt->rx_ctrl.channel,
// .length = pkt->rx_ctrl.sig_len,
// .rssi = pkt->rx_ctrl.rssi,
// };
// // for now, the monitor only dumps the length of the MISC type frame
// if (type != WIFI_PKT_MISC && !pkt->rx_ctrl.rx_state) {
// packet.length -= MONITOR_PAYLOAD_FCS_LEN;
// packet.payload = malloc(packet.length);
// if (packet.payload) {
// memcpy(packet.payload, pkt->payload, packet.length);
// wifi_monitor_obj_t *self = MP_STATE_VM(wifi_monitor_singleton);
// if (self->queue) {
// // send packet
// if (xQueueSendFromISR(self->queue, &packet, NULL) != pdTRUE) {
// self->lost++;
// free(packet.payload);
// ESP_LOGE(TAG, "packet queue full");
// }
// }
// } else {
// ESP_LOGE(TAG, "not enough memory for packet");
// }
// }
// }
void common_hal_wifi_monitor_construct(wifi_monitor_obj_t *self, uint8_t channel, size_t queue) {
// mp_rom_error_text_t monitor_mode_init_error = MP_ERROR_TEXT("monitor init failed");
// self->queue = xQueueCreate(queue, sizeof(monitor_packet_t));
// if (!self->queue) {
// mp_raise_RuntimeError(monitor_mode_init_error);
// }
// // start wifi promicuous mode
// wifi_promiscuous_filter_t wifi_filter = {
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT,
// };
// esp_wifi_set_promiscuous_filter(&wifi_filter);
// esp_wifi_set_promiscuous_rx_cb(wifi_monitor_cb);
// if (esp_wifi_set_promiscuous(true) != ESP_OK) {
// mp_raise_RuntimeError(monitor_mode_init_error);
// }
// esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
// self->channel = channel;
// self->queue_length = queue;
}
bool common_hal_wifi_monitor_deinited(void) {
// bool enabled;
// return (esp_wifi_get_promiscuous(&enabled) == ESP_ERR_WIFI_NOT_INIT) ? true : !enabled;
return true;
}
void common_hal_wifi_monitor_deinit(wifi_monitor_obj_t *self) {
if (common_hal_wifi_monitor_deinited()) {
return;
}
// // disable wifi promiscuous mode
// esp_wifi_set_promiscuous(false);
// // make sure to free all resources in the left items
// UBaseType_t left_items = uxQueueMessagesWaiting(self->queue);
// monitor_packet_t packet;
// while (left_items--) {
// xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK);
// free(packet.payload);
// }
// vQueueDelete(self->queue);
// self->queue = NULL;
}
void common_hal_wifi_monitor_set_channel(wifi_monitor_obj_t *self, uint8_t channel) {
self->channel = channel;
// esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}
mp_obj_t common_hal_wifi_monitor_get_channel(wifi_monitor_obj_t *self) {
return MP_OBJ_NEW_SMALL_INT(self->channel);
}
mp_obj_t common_hal_wifi_monitor_get_queue(wifi_monitor_obj_t *self) {
return mp_obj_new_int_from_uint(self->queue_length);
}
mp_obj_t common_hal_wifi_monitor_get_lost(wifi_monitor_obj_t *self) {
size_t lost = self->lost;
self->lost = 0;
return mp_obj_new_int_from_uint(lost);
}
mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self) {
return mp_obj_new_int_from_uint(0); // uxQueueMessagesWaiting(self->queue));
}
mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self) {
monitor_packet_t packet;
// if (xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK) != pdTRUE) {
// return (mp_obj_t)&mp_const_empty_dict_obj;
// }
mp_obj_dict_t *dict = MP_OBJ_TO_PTR(mp_obj_new_dict(4));
mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_CH), MP_OBJ_NEW_SMALL_INT(packet.channel));
mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_LEN), MP_OBJ_NEW_SMALL_INT(packet.length));
mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RAW), mp_obj_new_bytes(packet.payload, packet.length));
free(packet.payload);
mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RSSI), MP_OBJ_NEW_SMALL_INT(packet.rssi));
return MP_OBJ_FROM_PTR(dict);
}

View file

@ -0,0 +1,17 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2021 microDev
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
uint8_t channel;
size_t lost;
size_t queue_length;
// QueueHandle_t queue;
} wifi_monitor_obj_t;

View file

@ -0,0 +1,75 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <string.h>
#include "shared-bindings/wifi/Network.h"
#include "shared-bindings/wifi/AuthMode.h"
mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) {
const char *cstr = (const char *)self->scan_result.ssid;
return mp_obj_new_str(cstr, self->scan_result.ssid_length);
}
mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self) {
return mp_obj_new_bytes(self->scan_result.mac, self->scan_result.mac_length);
}
mp_obj_t common_hal_wifi_network_get_rssi(wifi_network_obj_t *self) {
return mp_obj_new_int(self->scan_result.rssi);
}
mp_obj_t common_hal_wifi_network_get_channel(wifi_network_obj_t *self) {
return mp_obj_new_int(self->scan_result.channel);
}
mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) {
// const char *cstr = (const char *)self->record.country.cc;
// 2 instead of strlen(cstr) as this gives us only the country-code
// return mp_obj_new_str(cstr, 2);
return mp_const_none;
}
mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) {
uint32_t authmode_mask = 0;
// switch (self->record.authmode) {
// case WIFI_AUTH_OPEN:
// authmode_mask = AUTHMODE_OPEN;
// break;
// case WIFI_AUTH_WEP:
// authmode_mask = AUTHMODE_WEP;
// break;
// case WIFI_AUTH_WPA_PSK:
// authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_PSK:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA_WPA2_PSK:
// authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_ENTERPRISE:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE;
// break;
// case WIFI_AUTH_WPA3_PSK:
// authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_WPA3_PSK:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK;
// break;
// default:
// break;
// }
mp_obj_t authmode_list = mp_obj_new_list(0, NULL);
if (authmode_mask != 0) {
for (uint8_t i = 0; i < 32; i++) {
if ((authmode_mask >> i) & 1) {
mp_obj_list_append(authmode_list, cp_enum_find(&wifi_authmode_type, 1 << i));
}
}
}
return authmode_list;
}

View file

@ -0,0 +1,16 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
#include <zephyr/net/wifi_mgmt.h>
typedef struct {
mp_obj_base_t base;
struct wifi_scan_result scan_result;
} wifi_network_obj_t;

View file

@ -0,0 +1,740 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/wifi/Radio.h"
#include "ports/zephyr-cp/common-hal/wifi/ScannedNetworks.h"
#include "shared-bindings/wifi/Network.h"
#include <string.h>
#include "common-hal/wifi/__init__.h"
#include "shared/runtime/interrupt_char.h"
#include "py/gc.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/ipaddress/IPv4Address.h"
#include "shared-bindings/wifi/ScannedNetworks.h"
#include "shared-bindings/wifi/AuthMode.h"
#include "shared-bindings/time/__init__.h"
#include "shared-module/ipaddress/__init__.h"
#include "common-hal/socketpool/__init__.h"
#include "bindings/zephyr_kernel/__init__.h"
#include <zephyr/kernel.h>
#include <zephyr/net/hostname.h>
#include <zephyr/net/wifi.h>
#include <zephyr/net/wifi_mgmt.h>
#if CIRCUITPY_MDNS
#include "common-hal/mdns/Server.h"
#endif
#define MAC_ADDRESS_LENGTH 6
// static void set_mode_station(wifi_radio_obj_t *self, bool state) {
// wifi_mode_t next_mode;
// if (state) {
// if (self->ap_mode) {
// next_mode = WIFI_MODE_APSTA;
// } else {
// next_mode = WIFI_MODE_STA;
// }
// } else {
// if (self->ap_mode) {
// next_mode = WIFI_MODE_AP;
// } else {
// next_mode = WIFI_MODE_NULL;
// }
// }
// esp_wifi_set_mode(next_mode);
// self->sta_mode = state;
// }
// static void set_mode_ap(wifi_radio_obj_t *self, bool state) {
// wifi_mode_t next_mode;
// if (state) {
// if (self->sta_mode) {
// next_mode = WIFI_MODE_APSTA;
// } else {
// next_mode = WIFI_MODE_AP;
// }
// } else {
// if (self->sta_mode) {
// next_mode = WIFI_MODE_STA;
// } else {
// next_mode = WIFI_MODE_NULL;
// }
// }
// esp_wifi_set_mode(next_mode);
// self->ap_mode = state;
// }
bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) {
return self->started;
}
void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) {
if (self->started && !enabled) {
if (self->current_scan != NULL) {
common_hal_wifi_radio_stop_scanning_networks(self);
}
// #if CIRCUITPY_MDNS
// mdns_server_deinit_singleton();
// #endif
printk("net_if_down\n");
CHECK_ZEPHYR_RESULT(net_if_down(self->sta_netif));
self->started = false;
return;
}
if (!self->started && enabled) {
printk("net_if_up\n");
CHECK_ZEPHYR_RESULT(net_if_up(self->sta_netif));
self->started = true;
self->current_scan = NULL;
// common_hal_wifi_radio_set_tx_power(self, CIRCUITPY_WIFI_DEFAULT_TX_POWER);
return;
}
}
mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self) {
const char *hostname = net_hostname_get();
return mp_obj_new_str(hostname, strlen(hostname));
}
void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *hostname) {
if (net_hostname_set((char *)hostname, strlen(hostname)) != 0) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to set hostname"));
}
}
mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self) {
uint8_t mac[MAC_ADDRESS_LENGTH];
// esp_wifi_get_mac(ESP_IF_WIFI_STA, mac);
return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH);
}
void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac) {
if (!self->sta_mode) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started"));
}
if ((mac[0] & 0b1) == 0b1) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address"));
}
// esp_wifi_set_mac(ESP_IF_WIFI_STA, mac);
}
mp_float_t common_hal_wifi_radio_get_tx_power(wifi_radio_obj_t *self) {
int8_t tx_power = 0;
// esp_wifi_get_max_tx_power(&tx_power);
return tx_power / 4.0f;
}
void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t tx_power) {
// esp_wifi_set_max_tx_power(tx_power * 4.0f);
}
mp_int_t common_hal_wifi_radio_get_listen_interval(wifi_radio_obj_t *self) {
// wifi_config_t *config = &self->sta_config;
// return config->sta.listen_interval;
return 0;
}
void common_hal_wifi_radio_set_listen_interval(wifi_radio_obj_t *self, const mp_int_t listen_interval) {
// wifi_config_t *config = &self->sta_config;
// config->sta.listen_interval = listen_interval;
// if (listen_interval == 1) {
// esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
// } else if (listen_interval > 1) {
// esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
// } else {
// esp_wifi_set_ps(WIFI_PS_NONE);
// }
// esp_wifi_set_config(ESP_IF_WIFI_STA, config);
}
mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self) {
uint8_t mac[MAC_ADDRESS_LENGTH];
// esp_wifi_get_mac(ESP_IF_WIFI_AP, mac);
return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH);
}
void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint8_t *mac) {
if (!self->ap_mode) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started"));
}
if ((mac[0] & 0b1) == 0b1) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address"));
}
// esp_wifi_set_mac(ESP_IF_WIFI_AP, mac);
}
mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self, uint8_t start_channel, uint8_t stop_channel) {
printk("common_hal_wifi_radio_start_scanning_networks\n");
if (self->current_scan != NULL) {
printk("Already scanning for wifi networks\n");
mp_raise_RuntimeError(MP_ERROR_TEXT("Already scanning for wifi networks"));
}
if (!common_hal_wifi_radio_get_enabled(self)) {
printk("wifi is not enabled\n");
mp_raise_RuntimeError(MP_ERROR_TEXT("wifi is not enabled"));
}
wifi_scannednetworks_obj_t *scan = mp_obj_malloc(wifi_scannednetworks_obj_t, &wifi_scannednetworks_type);
self->current_scan = scan;
scan->current_channel_index = 0;
scan->start_channel = start_channel;
scan->end_channel = stop_channel;
scan->done = false;
scan->channel_scan_in_progress = false;
scan->netif = self->sta_netif;
k_msgq_init(&scan->msgq, scan->msgq_buffer, sizeof(struct wifi_scan_result), MAX_BUFFERED_SCAN_RESULTS);
k_fifo_init(&scan->fifo);
k_poll_event_init(&scan->events[0],
K_POLL_TYPE_SEM_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&mp_interrupt_sem);
k_poll_event_init(&scan->events[1],
K_POLL_TYPE_MSGQ_DATA_AVAILABLE,
K_POLL_MODE_NOTIFY_ONLY,
&scan->msgq);
wifi_scannednetworks_scan_next_channel(scan);
printk("common_hal_wifi_radio_start_scanning_networks done %p\n", scan);
return scan;
}
void common_hal_wifi_radio_stop_scanning_networks(wifi_radio_obj_t *self) {
printk("common_hal_wifi_radio_stop_scanning_networks\n");
// Return early if self->current_scan is NULL to avoid hang
if (self->current_scan == NULL) {
return;
}
// Free the memory used to store the found aps.
wifi_scannednetworks_deinit(self->current_scan);
self->current_scan = NULL;
}
void common_hal_wifi_radio_start_station(wifi_radio_obj_t *self) {
// set_mode_station(self, true);
}
void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) {
// set_mode_station(self, false);
}
void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, uint32_t authmode, uint8_t max_connections) {
// set_mode_ap(self, true);
// uint8_t esp_authmode = 0;
// switch (authmode) {
// case AUTHMODE_OPEN:
// esp_authmode = WIFI_AUTH_OPEN;
// break;
// case AUTHMODE_WPA | AUTHMODE_PSK:
// esp_authmode = WIFI_AUTH_WPA_PSK;
// break;
// case AUTHMODE_WPA2 | AUTHMODE_PSK:
// esp_authmode = WIFI_AUTH_WPA2_PSK;
// break;
// case AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK:
// esp_authmode = WIFI_AUTH_WPA_WPA2_PSK;
// break;
// default:
// mp_arg_error_invalid(MP_QSTR_authmode);
// break;
// }
// wifi_config_t *config = &self->ap_config;
// memcpy(&config->ap.ssid, ssid, ssid_len);
// config->ap.ssid[ssid_len] = 0;
// memcpy(&config->ap.password, password, password_len);
// config->ap.password[password_len] = 0;
// config->ap.channel = channel;
// config->ap.authmode = esp_authmode;
// mp_arg_validate_int_range(max_connections, 0, 10, MP_QSTR_max_connections);
// config->ap.max_connection = max_connections;
// esp_wifi_set_config(WIFI_IF_AP, config);
}
bool common_hal_wifi_radio_get_ap_active(wifi_radio_obj_t *self) {
// return self->ap_mode && esp_netif_is_netif_up(self->ap_netif);
return false;
}
void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self) {
// set_mode_ap(self, false);
}
mp_obj_t common_hal_wifi_radio_get_stations_ap(wifi_radio_obj_t *self) {
// wifi_sta_list_t esp_sta_list;
// esp_err_t result;
// result = esp_wifi_ap_get_sta_list(&esp_sta_list);
// if (result != ESP_OK) {
// return mp_const_none;
// }
// esp_netif_pair_mac_ip_t mac_ip_pair[esp_sta_list.num];
// for (int i = 0; i < esp_sta_list.num; i++) {
// memcpy(mac_ip_pair[i].mac, esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH);
// mac_ip_pair[i].ip.addr = 0;
// }
// result = esp_netif_dhcps_get_clients_by_mac(self->ap_netif, esp_sta_list.num, mac_ip_pair);
// if (result != ESP_OK) {
// return mp_const_none;
// }
mp_obj_t mp_sta_list = mp_obj_new_list(0, NULL);
// for (int i = 0; i < esp_sta_list.num; i++) {
// mp_obj_t elems[3] = {
// mp_obj_new_bytes(esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH),
// MP_OBJ_NEW_SMALL_INT(esp_sta_list.sta[i].rssi),
// mp_const_none
// };
// if (mac_ip_pair[i].ip.addr) {
// elems[2] = common_hal_ipaddress_new_ipv4address(mac_ip_pair[i].ip.addr);
// }
// mp_obj_list_append(mp_sta_list, namedtuple_make_new((const mp_obj_type_t *)&wifi_radio_station_type, 3, 0, elems));
// }
return mp_sta_list;
}
wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len) {
if (!common_hal_wifi_radio_get_enabled(self)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("wifi is not enabled"));
}
// wifi_config_t *config = &self->sta_config;
// size_t timeout_ms = timeout * 1000;
// uint32_t start_time = common_hal_time_monotonic_ms();
// uint32_t end_time = start_time + timeout_ms;
// EventBits_t bits;
// // can't block since both bits are false after wifi_init
// // both bits are true after an existing connection stops
// bits = xEventGroupWaitBits(self->event_group_handle,
// WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT,
// pdTRUE,
// pdTRUE,
// 0);
// bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) &&
// !((bits & WIFI_DISCONNECTED_BIT) != 0);
// if (connected) {
// // SSIDs are up to 32 bytes. Assume it is null terminated if it is less.
// if (memcmp(ssid, config->sta.ssid, ssid_len) == 0 &&
// (ssid_len == 32 || strlen((const char *)config->sta.ssid) == ssid_len)) {
// // Already connected to the desired network.
// return WIFI_RADIO_ERROR_NONE;
// } else {
// xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
// // Trying to switch networks so disconnect first.
// esp_wifi_disconnect();
// do {
// RUN_BACKGROUND_TASKS;
// bits = xEventGroupWaitBits(self->event_group_handle,
// WIFI_DISCONNECTED_BIT,
// pdTRUE,
// pdTRUE,
// 0);
// } while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted());
// }
// }
// // explicitly clear bits since xEventGroupWaitBits may have timed out
// xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT);
// xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
// set_mode_station(self, true);
// memcpy(&config->sta.ssid, ssid, ssid_len);
// if (ssid_len < 32) {
// config->sta.ssid[ssid_len] = 0;
// }
// memcpy(&config->sta.password, password, password_len);
// config->sta.password[password_len] = 0;
// config->sta.channel = channel;
// // From esp_wifi_types.h:
// // Generally, station_config.bssid_set needs to be 0; and it needs
// // to be 1 only when users need to check the MAC address of the AP
// if (bssid_len > 0) {
// memcpy(&config->sta.bssid, bssid, bssid_len);
// config->sta.bssid[bssid_len] = 0;
// config->sta.bssid_set = true;
// } else {
// config->sta.bssid_set = false;
// }
// // If channel is 0 (default/unset) and BSSID is not given, do a full scan instead of fast scan
// // This will ensure that the best AP in range is chosen automatically
// if ((config->sta.bssid_set == 0) && (config->sta.channel == 0)) {
// config->sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
// } else {
// config->sta.scan_method = WIFI_FAST_SCAN;
// }
// esp_wifi_set_config(ESP_IF_WIFI_STA, config);
// self->starting_retries = 5;
// self->retries_left = 5;
// esp_wifi_connect();
// do {
// RUN_BACKGROUND_TASKS;
// bits = xEventGroupWaitBits(self->event_group_handle,
// WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT,
// pdTRUE,
// pdTRUE,
// 0);
// // Don't retry anymore if we're over our time budget.
// if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) {
// self->retries_left = 0;
// }
// } while ((bits & (WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT)) == 0 && !mp_hal_is_interrupted());
// if ((bits & WIFI_DISCONNECTED_BIT) != 0) {
// if (
// (self->last_disconnect_reason == WIFI_REASON_AUTH_FAIL) ||
// (self->last_disconnect_reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) ||
// (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) ||
// (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD)
// ) {
// return WIFI_RADIO_ERROR_AUTH_FAIL;
// } else if (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND) {
// return WIFI_RADIO_ERROR_NO_AP_FOUND;
// }
// return self->last_disconnect_reason;
// } else {
// // We're connected, allow us to retry if we get disconnected.
// self->retries_left = self->starting_retries;
// }
return WIFI_RADIO_ERROR_NONE;
}
bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self) {
// return self->sta_mode && esp_netif_is_netif_up(self->netif);
return false;
}
mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
return mp_const_none;
// }
// // Make sure the interface is in STA mode
// if (!self->sta_mode) {
// return mp_const_none;
// }
// wifi_network_obj_t *ap_info = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type);
// // From esp_wifi.h, the possible return values (typos theirs):
// // ESP_OK: succeed
// // ESP_ERR_WIFI_CONN: The station interface don't initialized
// // ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status
// if (esp_wifi_sta_get_ap_info(&self->ap_info.record) != ESP_OK) {
// return mp_const_none;
// } else {
// if (strlen(self->ap_info.record.country.cc) == 0) {
// // Workaround to fill country related information in ap_info until ESP-IDF carries a fix
// // esp_wifi_sta_get_ap_info does not appear to fill wifi_country_t (e.g. country.cc) details
// // (IDFGH-4437) #6267
// // Note: It is possible that Wi-Fi APs don't have a CC set, then even after this workaround
// // the element would remain empty.
// memset(&self->ap_info.record.country, 0, sizeof(wifi_country_t));
// if (esp_wifi_get_country(&self->ap_info.record.country) != ESP_OK) {
// return mp_const_none;
// }
// }
// memcpy(&ap_info->record, &self->ap_info.record, sizeof(wifi_ap_record_t));
// return MP_OBJ_FROM_PTR(ap_info);
// }
}
mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.gw.addr);
}
mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->ap_netif)) {
return mp_const_none;
// }
// esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.gw.addr);
}
mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.netmask.addr);
}
mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->ap_netif)) {
return mp_const_none;
// }
// esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr);
}
// static mp_obj_t common_hal_wifi_radio_get_addresses_netif(wifi_radio_obj_t *self, esp_netif_t *netif) {
// if (!esp_netif_is_netif_up(netif)) {
// return mp_const_empty_tuple;
// }
// esp_netif_ip_info_t ip_info;
// esp_netif_get_ip_info(netif, &ip_info);
// int n_addresses4 = ip_info.ip.addr != INADDR_NONE;
// #if CIRCUITPY_SOCKETPOOL_IPV6
// esp_ip6_addr_t addresses[LWIP_IPV6_NUM_ADDRESSES];
// int n_addresses6 = esp_netif_get_all_ip6(netif, &addresses[0]);
// #else
// int n_addresses6 = 0;
// #endif
// int n_addresses = n_addresses4 + n_addresses6;
// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_addresses, NULL));
// #if CIRCUITPY_SOCKETPOOL_IPV6
// for (int i = 0; i < n_addresses6; i++) {
// result->items[i] = espaddr6_to_str(&addresses[i]);
// }
// #endif
// if (n_addresses4) {
// result->items[n_addresses6] = espaddr4_to_str(&ip_info.ip);
// }
// return MP_OBJ_FROM_PTR(result);
// return mp_const_empty_tuple;
// }
mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) {
// return common_hal_wifi_radio_get_addresses_netif(self, self->netif);
return mp_const_none;
}
mp_obj_t common_hal_wifi_radio_get_addresses_ap(wifi_radio_obj_t *self) {
// return common_hal_wifi_radio_get_addresses_netif(self, self->ap_netif);
return mp_const_none;
}
uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return 0;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return self->ip_info.ip.addr;
return 0;
}
mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.ip.addr);
return mp_const_none;
}
mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->ap_netif)) {
// return mp_const_none;
// }
// esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.ip.addr);
return mp_const_none;
}
mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return mp_const_none;
// }
// esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info);
// if (self->dns_info.ip.type != ESP_IPADDR_TYPE_V4) {
// return mp_const_none;
// }
// // dns_info is of type esp_netif_dns_info_t, which is just ever so slightly
// // different than esp_netif_ip_info_t used for
// // common_hal_wifi_radio_get_ipv4_address (includes both ipv4 and 6),
// // so some extra jumping is required to get to the actual address
// return common_hal_ipaddress_new_ipv4address(self->dns_info.ip.u_addr.ip4.addr);
return mp_const_none;
}
void common_hal_wifi_radio_set_ipv4_dns(wifi_radio_obj_t *self, mp_obj_t ipv4_dns_addr) {
// esp_netif_dns_info_t dns_addr;
// ipaddress_ipaddress_to_esp_idf_ip4(ipv4_dns_addr, &dns_addr.ip.u_addr.ip4);
// esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_addr);
}
void common_hal_wifi_radio_start_dhcp_client(wifi_radio_obj_t *self, bool ipv4, bool ipv6) {
// if (ipv4) {
// esp_netif_dhcpc_start(self->netif);
// } else {
// esp_netif_dhcpc_stop(self->netif);
// }
// #if LWIP_IPV6_DHCP6
// if (ipv6) {
// esp_netif_create_ip6_linklocal(self->netif);
// dhcp6_enable_stateless(esp_netif_get_netif_impl(self->netif));
// } else {
// dhcp6_disable(esp_netif_get_netif_impl(self->netif));
// }
// #else
// if (ipv6) {
// mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_ipv6);
// }
// #endif
}
void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self) {
// esp_netif_dhcpc_stop(self->netif);
// #if LWIP_IPV6_DHCP6
// dhcp6_disable(esp_netif_get_netif_impl(self->netif));
// #endif
}
void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) {
// esp_netif_dhcps_start(self->ap_netif);
}
void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) {
// esp_netif_dhcps_stop(self->ap_netif);
}
void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) {
// common_hal_wifi_radio_stop_dhcp_client(self); // Must stop station DHCP to set a manual address
// esp_netif_ip_info_t ip_info;
// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip);
// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask);
// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw);
// esp_netif_set_ip_info(self->netif, &ip_info);
// if (ipv4_dns != MP_OBJ_NULL) {
// common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns);
// }
}
void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) {
// common_hal_wifi_radio_stop_dhcp_server(self); // Must stop access point DHCP to set a manual address
// esp_netif_ip_info_t ip_info;
// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip);
// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask);
// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw);
// esp_netif_set_ip_info(self->ap_netif, &ip_info);
// common_hal_wifi_radio_start_dhcp_server(self); // restart access point DHCP
}
// static void ping_success_cb(esp_ping_handle_t hdl, void *args) {
// wifi_radio_obj_t *self = (wifi_radio_obj_t *)args;
// esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &self->ping_elapsed_time, sizeof(self->ping_elapsed_time));
// }
mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) {
// esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
// ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr);
// ping_config.count = 1;
// // We must fetch ping information using the callback mechanism, because the session storage is freed when
// // the ping session is done, even before esp_ping_delete_session().
// esp_ping_callbacks_t ping_callbacks = {
// .on_ping_success = ping_success_cb,
// .cb_args = (void *)self,
// };
// size_t timeout_ms = timeout * 1000;
// // ESP-IDF creates a task to do the ping session. It shuts down when done, but only after a one second delay.
// // Calling common_hal_wifi_radio_ping() too fast will cause resource exhaustion.
// esp_ping_handle_t ping;
// if (esp_ping_new_session(&ping_config, &ping_callbacks, &ping) != ESP_OK) {
// // Wait for old task to go away and then try again.
// // Empirical testing shows we have to wait at least two seconds, despite the task
// // having a one-second timeout.
// common_hal_time_delay_ms(2000);
// // Return if interrupted now, to show the interruption as KeyboardInterrupt instead of the
// // IDF error.
// if (mp_hal_is_interrupted()) {
// return (uint32_t)(-1);
// }
// CHECK_ESP_RESULT(esp_ping_new_session(&ping_config, &ping_callbacks, &ping));
// }
// // Use all ones as a flag that the elapsed time was not set (ping failed or timed out).
// self->ping_elapsed_time = (uint32_t)(-1);
// esp_ping_start(ping);
// uint32_t start_time = common_hal_time_monotonic_ms();
// while ((self->ping_elapsed_time == (uint32_t)(-1)) &&
// (common_hal_time_monotonic_ms() - start_time < timeout_ms) &&
// !mp_hal_is_interrupted()) {
// RUN_BACKGROUND_TASKS;
// }
// esp_ping_stop(ping);
// esp_ping_delete_session(ping);
// return (mp_int_t)self->ping_elapsed_time;
return 0;
}
void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self) {
// Only bother to scan the actual object references.
gc_collect_ptr(self->current_scan);
}
mp_obj_t common_hal_wifi_radio_get_dns(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return mp_const_empty_tuple;
// }
// esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info);
// if (self->dns_info.ip.type == ESP_IPADDR_TYPE_V4 && self->dns_info.ip.u_addr.ip4.addr == INADDR_NONE) {
// return mp_const_empty_tuple;
// }
// mp_obj_t args[] = {
// espaddr_to_str(&self->dns_info.ip),
// };
// return mp_obj_new_tuple(1, args);
return mp_const_empty_tuple;
}
void common_hal_wifi_radio_set_dns(wifi_radio_obj_t *self, mp_obj_t dns_addrs_obj) {
// mp_int_t len = mp_obj_get_int(mp_obj_len(dns_addrs_obj));
// mp_arg_validate_length_max(len, 1, MP_QSTR_dns);
// esp_netif_dns_info_t dns_info;
// if (len == 0) {
// // clear DNS server
// dns_info.ip.type = ESP_IPADDR_TYPE_V4;
// dns_info.ip.u_addr.ip4.addr = INADDR_NONE;
// } else {
// mp_obj_t dns_addr_obj = mp_obj_subscr(dns_addrs_obj, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL);
// struct sockaddr_storage addr_storage;
// socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(dns_addr_obj), &addr_storage, 1);
// sockaddr_to_espaddr(&addr_storage, &dns_info.ip);
// }
// esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_info);
}

View file

@ -0,0 +1,43 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
#include "shared-bindings/wifi/ScannedNetworks.h"
#include "shared-bindings/wifi/Network.h"
#include <zephyr/net/net_if.h>
// Event bits for the Radio event group.
#define WIFI_SCAN_DONE_BIT BIT0
#define WIFI_CONNECTED_BIT BIT1
#define WIFI_DISCONNECTED_BIT BIT2
typedef struct {
mp_obj_base_t base;
wifi_scannednetworks_obj_t *current_scan;
// StaticEventGroup_t event_group;
// EventGroupHandle_t event_group_handle;
// wifi_config_t sta_config;
// wifi_network_obj_t ap_info;
// esp_netif_ip_info_t ip_info;
// esp_netif_dns_info_t dns_info;
struct net_if *sta_netif;
// uint32_t ping_elapsed_time;
// wifi_config_t ap_config;
// esp_netif_ip_info_t ap_ip_info;
struct net_if *ap_netif;
bool started;
bool ap_mode;
bool sta_mode;
uint8_t retries_left;
uint8_t starting_retries;
uint8_t last_disconnect_reason;
} wifi_radio_obj_t;
extern void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self);

View file

@ -0,0 +1,122 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec
// SPDX-FileCopyrightText: Copyright (c) 2017 Glenn Ruben Bakke
//
// SPDX-License-Identifier: MIT
#include <string.h>
#include "shared/runtime/interrupt_char.h"
#include "py/gc.h"
#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/wifi/__init__.h"
#include "shared-bindings/wifi/Network.h"
#include "shared-bindings/wifi/Radio.h"
#include "shared-bindings/wifi/ScannedNetworks.h"
#include <zephyr/kernel.h>
#include <zephyr/net/wifi_mgmt.h>
void wifi_scannednetworks_scan_result(wifi_scannednetworks_obj_t *self, struct wifi_scan_result *result) {
if (k_msgq_put(&self->msgq, result, K_NO_WAIT) != 0) {
printk("Dropping scan result!\n");
}
}
static void wifi_scannednetworks_done(wifi_scannednetworks_obj_t *self) {
self->done = true;
}
static bool wifi_scannednetworks_wait_for_scan(wifi_scannednetworks_obj_t *self) {
return !mp_hal_is_interrupted();
}
mp_obj_t common_hal_wifi_scannednetworks_next(wifi_scannednetworks_obj_t *self) {
if (self->done) {
return mp_const_none;
}
// If we don't have any results queued, then wait until we do.
while (k_fifo_is_empty(&self->fifo) && k_msgq_num_used_get(&self->msgq) == 0) {
k_poll(self->events, ARRAY_SIZE(self->events), K_FOREVER);
if (mp_hal_is_interrupted()) {
wifi_scannednetworks_done(self);
}
if (k_msgq_num_used_get(&self->msgq) > 0) {
// We found something.
break;
}
int signaled;
int result;
k_poll_signal_check(&self->channel_done, &signaled, &result);
if (signaled) {
wifi_scannednetworks_scan_next_channel(self);
}
if (self->done) {
return mp_const_none;
}
}
// Copy everything out of the message queue into the FIFO because it's a
// fixed size.
while (k_msgq_num_used_get(&self->msgq) > 0) {
wifi_network_obj_t *entry = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type);
k_msgq_get(&self->msgq, &entry->scan_result, K_NO_WAIT);
// This will use the base python object space for the linked list. We
// need to reset it before returning this memory as a Python object.
k_fifo_put(&self->fifo, entry);
}
wifi_network_obj_t *entry = k_fifo_get(&self->fifo, K_NO_WAIT);
entry->base.type = &wifi_network_type;
return MP_OBJ_FROM_PTR(entry);
}
// We don't do a linear scan so that we look at a variety of spectrum up front.
static uint8_t scan_pattern[] = {6, 1, 11, 3, 9, 13, 2, 4, 8, 12, 5, 7, 10, 14, 0};
void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self) {
// There is no channel 0, so use that as a flag to indicate we've run out of channels to scan.
uint8_t next_channel = 0;
while (self->current_channel_index < sizeof(scan_pattern)) {
next_channel = scan_pattern[self->current_channel_index];
self->current_channel_index++;
// Scan only channels that are in the specified range.
if (self->start_channel <= next_channel && next_channel <= self->end_channel) {
break;
}
}
k_poll_signal_init(&self->channel_done);
k_poll_event_init(&self->events[2],
K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&self->channel_done);
struct wifi_scan_params params = { 0 };
params.band_chan[0].band = WIFI_FREQ_BAND_2_4_GHZ;
params.band_chan[0].channel = next_channel;
if (next_channel == 0) {
wifi_scannednetworks_done(self);
} else {
int res = net_mgmt(NET_REQUEST_WIFI_SCAN, self->netif, &params, sizeof(params));
if (res != 0) {
printk("Failed to start wifi scan %d\n", res);
raise_zephyr_error(res);
wifi_scannednetworks_done(self);
} else {
self->channel_scan_in_progress = true;
}
}
}
void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self) {
// Free any results we don't need.
while (!k_fifo_is_empty(&self->fifo)) {
wifi_network_obj_t *entry = k_fifo_get(&self->fifo, K_NO_WAIT);
m_free(entry);
}
wifi_scannednetworks_done(self);
}

View file

@ -0,0 +1,43 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec
//
// SPDX-License-Identifier: MIT
#pragma once
#include <stdint.h>
#include "py/obj.h"
#include <zephyr/kernel.h>
#include <zephyr/net/wifi_mgmt.h>
#define MAX_BUFFERED_SCAN_RESULTS 10
typedef struct {
mp_obj_base_t base;
uint8_t current_channel_index;
struct k_poll_signal channel_done;
struct k_poll_event events[3];
// Hold results as they move from the callback to the CP thread.
char msgq_buffer[MAX_BUFFERED_SCAN_RESULTS * sizeof(struct wifi_scan_result)];
struct k_msgq msgq;
// Buffer the scan results before we return them. They are stored on the CP heap.
struct k_fifo fifo;
// Limits on what channels to scan.
uint8_t start_channel;
uint8_t end_channel; // Inclusive
struct net_if *netif;
bool done;
bool channel_scan_in_progress;
} wifi_scannednetworks_obj_t;
void wifi_scannednetworks_scan_result(wifi_scannednetworks_obj_t *self, struct wifi_scan_result *result);
void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self);
void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self);

View file

@ -0,0 +1,460 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "common-hal/wifi/__init__.h"
#include "shared-bindings/wifi/__init__.h"
#include "shared-bindings/ipaddress/IPv4Address.h"
#include "shared-bindings/wifi/Monitor.h"
#include "shared-bindings/wifi/Radio.h"
#include "bindings/zephyr_kernel/__init__.h"
#include "common-hal/socketpool/__init__.h"
#include "py/gc.h"
#include "py/mpstate.h"
#include "py/runtime.h"
wifi_radio_obj_t common_hal_wifi_radio_obj;
#include "supervisor/port.h"
#include "supervisor/workflow.h"
#if CIRCUITPY_STATUS_BAR
#include "supervisor/shared/status_bar.h"
#endif
#include <zephyr/kernel.h>
#include <zephyr/net/wifi_mgmt.h>
#define MAC_ADDRESS_LENGTH 6
static void schedule_background_on_cp_core(void *arg) {
#if CIRCUITPY_STATUS_BAR
supervisor_status_bar_request_update(false);
#endif
// CircuitPython's VM is run in a separate FreeRTOS task from wifi callbacks. So, we have to
// notify the main task every time in case it's waiting for us.
port_wake_main_task();
}
static struct net_mgmt_event_callback wifi_cb;
static struct net_mgmt_event_callback ipv4_cb;
static void _event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) {
wifi_radio_obj_t *self = &common_hal_wifi_radio_obj;
printk("_event_handler cb %p event %08x if %p\n", cb, mgmt_event, iface);
switch (mgmt_event) {
case NET_EVENT_WIFI_SCAN_RESULT:
printk("NET_EVENT_WIFI_SCAN_RESULT\n");
struct wifi_scan_result *result = (struct wifi_scan_result *)cb->info;
if (self->current_scan != NULL) {
wifi_scannednetworks_scan_result(self->current_scan, result);
}
break;
case NET_EVENT_WIFI_SCAN_DONE:
printk("NET_EVENT_WIFI_SCAN_DONE\n");
if (self->current_scan != NULL) {
k_poll_signal_raise(&self->current_scan->channel_done, 0);
}
break;
case NET_EVENT_WIFI_CONNECT_RESULT:
printk("NET_EVENT_WIFI_CONNECT_RESULT\n");
break;
case NET_EVENT_WIFI_DISCONNECT_RESULT:
printk("NET_EVENT_WIFI_DISCONNECT_RESULT\n");
break;
case NET_EVENT_WIFI_IFACE_STATUS:
printk("NET_EVENT_WIFI_IFACE_STATUS\n");
break;
case NET_EVENT_WIFI_TWT:
printk("NET_EVENT_WIFI_TWT\n");
break;
case NET_EVENT_WIFI_TWT_SLEEP_STATE:
printk("NET_EVENT_WIFI_TWT_SLEEP_STATE\n");
break;
case NET_EVENT_WIFI_RAW_SCAN_RESULT:
printk("NET_EVENT_WIFI_RAW_SCAN_RESULT\n");
break;
case NET_EVENT_WIFI_DISCONNECT_COMPLETE:
printk("NET_EVENT_WIFI_DISCONNECT_COMPLETE\n");
break;
case NET_EVENT_WIFI_SIGNAL_CHANGE:
printk("NET_EVENT_WIFI_SIGNAL_CHANGE\n");
break;
case NET_EVENT_WIFI_NEIGHBOR_REP_COMP:
printk("NET_EVENT_WIFI_NEIGHBOR_REP_COMP\n");
break;
case NET_EVENT_WIFI_AP_ENABLE_RESULT:
printk("NET_EVENT_WIFI_AP_ENABLE_RESULT\n");
break;
case NET_EVENT_WIFI_AP_DISABLE_RESULT:
printk("NET_EVENT_WIFI_AP_DISABLE_RESULT\n");
break;
case NET_EVENT_WIFI_AP_STA_CONNECTED:
printk("NET_EVENT_WIFI_AP_STA_CONNECTED\n");
break;
case NET_EVENT_WIFI_AP_STA_DISCONNECTED:
printk("NET_EVENT_WIFI_AP_STA_DISCONNECTED\n");
break;
}
}
// static void event_handler(void *arg, esp_event_base_t event_base,
// int32_t event_id, void *event_data) {
// // This runs on the PRO CORE! It cannot share CP interrupt enable/disable
// // directly.
// wifi_radio_obj_t *radio = arg;
// if (event_base == WIFI_EVENT) {
// switch (event_id) {
// case WIFI_EVENT_SCAN_DONE:
// ESP_LOGW(TAG, "scan");
// xEventGroupSetBits(radio->event_group_handle, WIFI_SCAN_DONE_BIT);
// break;
// case WIFI_EVENT_AP_START:
// ESP_LOGW(TAG, "ap start");
// break;
// case WIFI_EVENT_AP_STOP:
// ESP_LOGW(TAG, "ap stop");
// break;
// case WIFI_EVENT_AP_STACONNECTED:
// break;
// case WIFI_EVENT_AP_STADISCONNECTED:
// break;
// case WIFI_EVENT_STA_START:
// ESP_LOGW(TAG, "sta start");
// break;
// case WIFI_EVENT_STA_STOP:
// ESP_LOGW(TAG, "sta stop");
// break;
// case WIFI_EVENT_STA_CONNECTED:
// ESP_LOGW(TAG, "connected");
// break;
// case WIFI_EVENT_STA_DISCONNECTED: {
// ESP_LOGW(TAG, "disconnected");
// wifi_event_sta_disconnected_t *d = (wifi_event_sta_disconnected_t *)event_data;
// uint8_t reason = d->reason;
// ESP_LOGW(TAG, "reason %d 0x%02x", reason, reason);
// if (radio->retries_left > 0 &&
// reason != WIFI_REASON_AUTH_FAIL &&
// reason != WIFI_REASON_NO_AP_FOUND &&
// reason != WIFI_REASON_ASSOC_LEAVE) {
// radio->retries_left--;
// ESP_LOGI(TAG, "Retrying connect. %d retries remaining", radio->retries_left);
// esp_wifi_connect();
// return;
// }
// radio->last_disconnect_reason = reason;
// xEventGroupSetBits(radio->event_group_handle, WIFI_DISCONNECTED_BIT);
// break;
// }
// // Cases to handle later.
// // case WIFI_EVENT_STA_AUTHMODE_CHANGE:
// default: {
// ESP_LOGW(TAG, "event %ld 0x%02ld", event_id, event_id);
// break;
// }
// }
// }
// if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
// ESP_LOGW(TAG, "got ip");
// radio->retries_left = radio->starting_retries;
// xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT);
// }
// // Use IPC to ensure we run schedule background on the same core as CircuitPython.
// #if defined(CONFIG_FREERTOS_UNICORE) && CONFIG_FREERTOS_UNICORE
// schedule_background_on_cp_core(NULL);
// #else
// // This only blocks until the start of the function. That's ok since the PRO
// // core shouldn't care what we do.
// esp_ipc_call(CONFIG_ESP_MAIN_TASK_AFFINITY, schedule_background_on_cp_core, NULL);
// #endif
// }
static bool wifi_inited;
static bool wifi_ever_inited;
static bool wifi_user_initiated;
void common_hal_wifi_init(bool user_initiated) {
wifi_radio_obj_t *self = &common_hal_wifi_radio_obj;
printk("common_hal_wifi_init\n");
if (wifi_inited) {
if (user_initiated && !wifi_user_initiated) {
common_hal_wifi_radio_set_enabled(self, true);
}
return;
}
wifi_inited = true;
wifi_user_initiated = user_initiated;
self->base.type = &wifi_radio_type;
// struct net_if *default_iface = net_if_get_default();
// printk("default interface %p\n", default_iface);
// printk("listing network interfaces\n");
// for (int i = 0; i < 10; i++) {
// struct net_if* iface = net_if_get_by_index(i);
// if (iface == NULL) {
// printk("iface %d is NULL\n", i);
// continue;
// }
// char name[32];
// net_if_get_name(iface, name, 32);
// printk("iface %d %s\n", i, name);
// }
self->sta_netif = net_if_get_wifi_sta();
self->ap_netif = net_if_get_wifi_sap();
printk("sta_netif %p\n", self->sta_netif);
printk("ap_netif %p\n", self->ap_netif);
struct wifi_iface_status status = { 0 };
if (self->sta_netif != NULL) {
CHECK_ZEPHYR_RESULT(net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, self->sta_netif, &status,
sizeof(struct wifi_iface_status)));
if (net_if_is_up(self->sta_netif)) {
printk("STA is up\n");
} else {
printk("STA is down\n");
}
if (net_if_is_carrier_ok(self->sta_netif)) {
printk("STA carrier is ok\n");
} else {
printk("STA carrier is not ok\n");
}
if (net_if_is_dormant(self->sta_netif)) {
printk("STA is dormant\n");
} else {
printk("STA is not dormant\n");
}
}
if (self->ap_netif != NULL) {
int res = net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, self->ap_netif, &status,
sizeof(struct wifi_iface_status));
printk("AP status request response %d\n", res);
if (net_if_is_up(self->ap_netif)) {
printk("AP is up\n");
} else {
printk("AP is down\n");
}
if (net_if_is_carrier_ok(self->ap_netif)) {
printk("AP carrier is ok\n");
} else {
printk("AP carrier is not ok\n");
}
if (net_if_is_dormant(self->ap_netif)) {
printk("AP is dormant\n");
} else {
printk("AP is not dormant\n");
}
}
// self->started = false;
// // Even though we just called esp_netif_create_default_wifi_sta,
// // station mode isn't actually ready for use until esp_wifi_set_mode()
// // is called and the configuration is loaded via esp_wifi_set_config().
// // Set both convenience flags to false so it's not forgotten.
// self->sta_mode = 0;
// self->ap_mode = 0;
net_mgmt_init_event_callback(&wifi_cb, _event_handler,
NET_EVENT_WIFI_SCAN_DONE |
NET_EVENT_WIFI_CONNECT_RESULT |
NET_EVENT_WIFI_DISCONNECT_RESULT |
NET_EVENT_WIFI_TWT |
NET_EVENT_WIFI_RAW_SCAN_RESULT |
NET_EVENT_WIFI_AP_ENABLE_RESULT |
NET_EVENT_WIFI_AP_DISABLE_RESULT |
NET_EVENT_WIFI_AP_STA_CONNECTED |
NET_EVENT_WIFI_AP_STA_DISCONNECTED);
net_mgmt_init_event_callback(&ipv4_cb, _event_handler, NET_EVENT_IPV4_ADDR_ADD);
net_mgmt_add_event_callback(&wifi_cb);
net_mgmt_add_event_callback(&ipv4_cb);
// Set the default hostname capped at NET_HOSTNAME_MAX_LEN characters. We trim off
// the start of the board name (likely manufacturer) because the end is
// often more unique to the board.
size_t board_len = MIN(NET_HOSTNAME_MAX_LEN - ((MAC_ADDRESS_LENGTH * 2) + 6), strlen(CIRCUITPY_BOARD_ID));
size_t board_trim = strlen(CIRCUITPY_BOARD_ID) - board_len;
// Avoid double _ in the hostname.
if (CIRCUITPY_BOARD_ID[board_trim] == '_') {
board_trim++;
}
char cpy_default_hostname[board_len + (MAC_ADDRESS_LENGTH * 2) + 6];
struct net_linkaddr *mac = net_if_get_link_addr(self->sta_netif);
if (mac->len < MAC_ADDRESS_LENGTH) {
printk("MAC address too short");
}
snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3], mac->addr[4], mac->addr[5]);
if (net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname)) != 0) {
printk("setting hostname failed\n");
}
// set station mode to avoid the default SoftAP
common_hal_wifi_radio_start_station(self);
// start wifi
common_hal_wifi_radio_set_enabled(self, true);
printk("common_hal_wifi_init done\n");
}
void wifi_user_reset(void) {
if (wifi_user_initiated) {
wifi_reset();
wifi_user_initiated = false;
}
}
void wifi_reset(void) {
printk("wifi_reset\n");
if (!wifi_inited) {
return;
}
common_hal_wifi_monitor_deinit(MP_STATE_VM(wifi_monitor_singleton));
wifi_radio_obj_t *radio = &common_hal_wifi_radio_obj;
common_hal_wifi_radio_set_enabled(radio, false);
// #ifndef CONFIG_IDF_TARGET_ESP32
// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT,
// ESP_EVENT_ANY_ID,
// radio->handler_instance_all_wifi));
// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT,
// IP_EVENT_STA_GOT_IP,
// radio->handler_instance_got_ip));
// ESP_ERROR_CHECK(esp_wifi_deinit());
// esp_netif_destroy(radio->netif);
// radio->netif = NULL;
// esp_netif_destroy(radio->ap_netif);
// radio->ap_netif = NULL;
// wifi_inited = false;
// #endif
supervisor_workflow_request_background();
}
// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) {
// if (mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) {
// ipaddress_ipaddress_to_esp_idf_ip4(ip_address, (esp_ip4_addr_t *)esp_ip_address);
// #if LWIP_IPV6
// esp_ip_address->type = IPADDR_TYPE_V4;
// #endif
// } else {
// struct sockaddr_storage addr_storage;
// socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(ip_address), &addr_storage, 1);
// sockaddr_to_espaddr(&addr_storage, (esp_ip_addr_t *)esp_ip_address);
// }
// }
// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address) {
// if (!mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) {
// mp_raise_ValueError(MP_ERROR_TEXT("Only IPv4 addresses supported"));
// }
// mp_obj_t packed = common_hal_ipaddress_ipv4address_get_packed(ip_address);
// size_t len;
// const char *bytes = mp_obj_str_get_data(packed, &len);
// esp_netif_set_ip4_addr(esp_ip_address, bytes[0], bytes[1], bytes[2], bytes[3]);
// }
void common_hal_wifi_gc_collect(void) {
common_hal_wifi_radio_gc_collect(&common_hal_wifi_radio_obj);
}
// static mp_obj_t espaddrx_to_str(const void *espaddr, uint8_t esptype) {
// char buf[IPADDR_STRLEN_MAX];
// inet_ntop(esptype == ESP_IPADDR_TYPE_V6 ? AF_INET6 : AF_INET, espaddr, buf, sizeof(buf));
// return mp_obj_new_str(buf, strlen(buf));
// }
// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr) {
// return espaddrx_to_str(espaddr, espaddr->type);
// }
// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr) {
// return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V4);
// }
// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr) {
// return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V6);
// }
// mp_obj_t sockaddr_to_str(const struct sockaddr_storage *sockaddr) {
// char buf[IPADDR_STRLEN_MAX];
// #if CIRCUITPY_SOCKETPOOL_IPV6
// if (sockaddr->ss_family == AF_INET6) {
// const struct sockaddr_in6 *addr6 = (const void *)sockaddr;
// inet_ntop(AF_INET6, &addr6->sin6_addr, buf, sizeof(buf));
// } else
// #endif
// {
// const struct sockaddr_in *addr = (const void *)sockaddr;
// inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf));
// }
// return mp_obj_new_str(buf, strlen(buf));
// }
// mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *sockaddr) {
// mp_obj_t args[4] = {
// sockaddr_to_str(sockaddr),
// };
// int n = 2;
// #if CIRCUITPY_SOCKETPOOL_IPV6
// if (sockaddr->ss_family == AF_INET6) {
// const struct sockaddr_in6 *addr6 = (const void *)sockaddr;
// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr6->sin6_port));
// args[2] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_flowinfo);
// args[3] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_scope_id);
// n = 4;
// } else
// #endif
// {
// const struct sockaddr_in *addr = (const void *)sockaddr;
// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr->sin_port));
// }
// return mp_obj_new_tuple(n, args);
// }
// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr) {
// #if CIRCUITPY_SOCKETPOOL_IPV6
// MP_STATIC_ASSERT(IPADDR_TYPE_V4 == ESP_IPADDR_TYPE_V4);
// MP_STATIC_ASSERT(IPADDR_TYPE_V6 == ESP_IPADDR_TYPE_V6);
// MP_STATIC_ASSERT(sizeof(ip_addr_t) == sizeof(esp_ip_addr_t));
// MP_STATIC_ASSERT(offsetof(ip_addr_t, u_addr) == offsetof(esp_ip_addr_t, u_addr));
// MP_STATIC_ASSERT(offsetof(ip_addr_t, type) == offsetof(esp_ip_addr_t, type));
// if (sockaddr->ss_family == AF_INET6) {
// const struct sockaddr_in6 *addr6 = (const void *)sockaddr;
// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip6.addr) == sizeof(addr6->sin6_addr));
// memcpy(&espaddr->u_addr.ip6.addr, &addr6->sin6_addr, sizeof(espaddr->u_addr.ip6.addr));
// espaddr->u_addr.ip6.zone = addr6->sin6_scope_id;
// espaddr->type = ESP_IPADDR_TYPE_V6;
// } else
// #endif
// {
// const struct sockaddr_in *addr = (const void *)sockaddr;
// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip4.addr) == sizeof(addr->sin_addr));
// memcpy(&espaddr->u_addr.ip4.addr, &addr->sin_addr, sizeof(espaddr->u_addr.ip4.addr));
// espaddr->type = ESP_IPADDR_TYPE_V4;
// }
// }
// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port) {
// #if CIRCUITPY_SOCKETPOOL_IPV6
// if (espaddr->type == ESP_IPADDR_TYPE_V6) {
// struct sockaddr_in6 *addr6 = (void *)sockaddr;
// memcpy(&addr6->sin6_addr, &espaddr->u_addr.ip6.addr, sizeof(espaddr->u_addr.ip6.addr));
// addr6->sin6_scope_id = espaddr->u_addr.ip6.zone;
// } else
// #endif
// {
// struct sockaddr_in *addr = (void *)sockaddr;
// memcpy(&addr->sin_addr, &espaddr->u_addr.ip4.addr, sizeof(espaddr->u_addr.ip4.addr));
// }
// }

View file

@ -0,0 +1,24 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
struct sockaddr_storage;
void wifi_reset(void);
// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address);
// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address);
mp_obj_t sockaddr_to_str(const struct sockaddr_storage *addr);
mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *addr);
// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr);
// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr);
// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr);
// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr);
// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port);

View file

@ -0,0 +1,92 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec
// SPDX-FileCopyrightText: Copyright (c) 2017 hathach
// SPDX-FileCopyrightText: Copyright (c) 2016 Sandeep Mistry All right reserved.
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/busio/I2C.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/tick.h"
#include "py/mperrno.h"
#include "py/runtime.h"
void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
// never_reset_pin_number(self->scl_pin_number);
// never_reset_pin_number(self->sda_pin_number);
}
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) {
}
bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) {
// return self->sda_pin_number == NO_PIN;
return true;
}
void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) {
if (common_hal_busio_i2c_deinited(self)) {
return;
}
// nrfx_twim_uninit(&self->twim_peripheral->twim);
// reset_pin_number(self->sda_pin_number);
// reset_pin_number(self->scl_pin_number);
// self->twim_peripheral->in_use = false;
// common_hal_busio_i2c_mark_deinit(self);
}
void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) {
// self->sda_pin_number = NO_PIN;
}
// nrfx_twim_tx doesn't support 0-length data so we fall back to the hal API
bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
bool found = true;
return found;
}
bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) {
if (common_hal_busio_i2c_deinited(self)) {
return false;
}
bool grabbed_lock = false;
return grabbed_lock;
}
bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) {
return self->has_lock;
}
void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) {
self->has_lock = false;
}
uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) {
return 0;
}
uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) {
if (len == 0) {
return 0;
}
}
uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr,
uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) {
uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false);
if (result != 0) {
return result;
}
return common_hal_busio_i2c_read(self, addr, in_data, in_len);
}

View file

@ -0,0 +1,17 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
// twim_peripheral_t *twim_peripheral;
bool has_lock;
uint8_t scl_pin_number;
uint8_t sda_pin_number;
} busio_i2c_obj_t;

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC
//
// SPDX-License-Identifier: MIT
// No zephyr_i2c module functions.

View file

@ -0,0 +1,45 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "bindings/zephyr_kernel/__init__.h"
#include "py/runtime.h"
#include <errno.h>
void raise_zephyr_error(int err) {
switch (-err) {
case EALREADY:
printk("EALREADY\n");
break;
case EDESTADDRREQ:
printk("EDESTADDRREQ\n");
break;
case EMSGSIZE:
printk("EMSGSIZE\n");
break;
case EPROTONOSUPPORT:
printk("EPROTONOSUPPORT\n");
break;
case EADDRNOTAVAIL:
printk("EADDRNOTAVAIL\n");
break;
case ENETRESET:
printk("ENETRESET\n");
break;
case EISCONN:
printk("EISCONN\n");
break;
case ENOTCONN:
printk("ENOTCONN\n");
break;
case ENOTSUP:
printk("ENOTSUP\n");
break;
default:
printk("Zephyr error %d\n", err);
}
}

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once

View file

@ -0,0 +1,130 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "shared-bindings/microcontroller/__init__.h"
#include "bindings/zephyr_serial/UART.h"
#include "shared/runtime/interrupt_char.h"
#include "py/mpconfig.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "py/stream.h"
#include <stdatomic.h>
#include <string.h>
#include <zephyr/drivers/uart.h>
/*
* Read characters from UART until line end is detected. Afterwards push the
* data to the message queue.
*/
static void serial_cb(const struct device *dev, void *user_data) {
zephyr_serial_uart_obj_t *self = (zephyr_serial_uart_obj_t *)user_data;
uint8_t c;
if (!uart_irq_update(dev)) {
return;
}
if (!uart_irq_rx_ready(dev)) {
return;
}
/* read until FIFO empty */
while (uart_fifo_read(dev, &c, 1) == 1) {
if (mp_interrupt_char == c) {
zephyr_serial_uart_clear_rx_buffer(self);
mp_sched_keyboard_interrupt();
} else if (!self->rx_paused) {
if (k_msgq_put(&self->msgq, &c, K_NO_WAIT) != 0) {
self->rx_paused = true;
}
}
}
}
void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self) {
}
void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) {
self->uart_device = uart_device;
int ret = uart_irq_callback_user_data_set(uart_device, serial_cb, self);
k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size);
if (ret < 0) {
if (ret == -ENOTSUP) {
printk("Interrupt-driven UART API support not enabled\n");
} else if (ret == -ENOSYS) {
printk("UART device does not support interrupt-driven API\n");
} else {
printk("Error setting UART callback: %d\n", ret);
}
return;
}
self->timeout = K_USEC(100);
uart_irq_rx_enable(uart_device);
}
bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self) {
return !device_is_ready(self->uart_device);
}
void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self) {
}
// Read characters.
size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) {
size_t count = 0;
while (count < len && k_msgq_get(&self->msgq, data + count, self->timeout) == 0) {
count++;
}
if (count > 0) {
self->rx_paused = false;
}
return count;
}
// Write characters.
size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
for (int i = 0; i < len; i++) {
uart_poll_out(self->uart_device, data[i]);
}
return len;
}
uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self) {
return 0;
}
void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate) {
}
mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self) {
return 0;
}
void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout) {
}
uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self) {
return k_msgq_num_used_get(&self->msgq);
}
void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self) {
k_msgq_purge(&self->msgq);
}
bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self) {
return true;
}

View file

@ -0,0 +1,22 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
#include <zephyr/kernel.h>
typedef struct {
mp_obj_base_t base;
const struct device *uart_device;
struct k_msgq msgq;
k_timeout_t timeout;
bool rx_paused; // set by irq if no space in rbuf
} zephyr_serial_uart_obj_t;

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC
//
// SPDX-License-Identifier: MIT
// No zephyr_serial module functions.

View file

@ -0,0 +1,74 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec
//
// SPDX-License-Identifier: MIT
#include <string.h>
#include "shared-bindings/busio/SPI.h"
#include "py/mperrno.h"
#include "py/runtime.h"
void spi_reset(void) {
}
void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) {
}
void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) {
}
bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) {
}
void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
if (common_hal_busio_spi_deinited(self)) {
return;
}
}
bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
return true;
}
bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) {
if (common_hal_busio_spi_deinited(self)) {
return false;
}
bool grabbed_lock = false;
return grabbed_lock;
}
bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) {
return self->has_lock;
}
void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
self->has_lock = false;
}
bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) {
return true;
}
bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) {
return true;
}
bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) {
return true;
}
uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) {
}
uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) {
return 0;
}
uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) {
return 0;
}

View file

@ -0,0 +1,20 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft
//
// SPDX-License-Identifier: MIT
#pragma once
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
// const spim_peripheral_t *spim_peripheral;
bool has_lock;
uint8_t clock_pin_number;
uint8_t MOSI_pin_number;
uint8_t MISO_pin_number;
} busio_spi_obj_t;
void spi_reset(void);

View file

@ -0,0 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC
//
// SPDX-License-Identifier: MIT
// No zephyr_spi module functions.

View file

@ -0,0 +1,10 @@
def find_mpconfigboard(portdir, board_id):
next_underscore = board_id.find("_")
while next_underscore != -1:
vendor = board_id[:next_underscore]
board = board_id[next_underscore + 1 :]
p = portdir / f"boards/{vendor}/{board}/circuitpython.toml"
if p.exists():
return p
next_underscore = board_id.find("_", next_underscore + 1)
return None

View file

@ -0,0 +1,613 @@
import asyncio
import colorlog
import sys
import logging
import os
import pathlib
import tomllib
import tomlkit
import yaml
import pickle
import cpbuild
import board_tools
logger = logging.getLogger(__name__)
# print("hello zephyr", sys.argv)
# print(os.environ)
cmake_args = {}
for var in sys.argv[1:]:
key, value = var.split("=", 1)
cmake_args[key] = value
# Path to ports/zephyr-cp
portdir = pathlib.Path(cmake_args["PORT_SRC_DIR"])
# Path to CP root
srcdir = portdir.parent.parent
# Path to where CMake wants to put our build output.
builddir = pathlib.Path.cwd()
zephyrdir = portdir / "zephyr"
# Path to where CMake puts Zephyr's build output.
zephyrbuilddir = builddir / ".." / ".." / ".." / "zephyr"
sys.path.append(str(portdir / "zephyr/scripts/dts/python-devicetree/src/"))
from zephyr2cp import zephyr_dts_to_cp_board
compiler = cpbuild.Compiler(srcdir, builddir, cmake_args)
ALWAYS_ON_MODULES = ["sys", "collections"]
DEFAULT_MODULES = [
"time",
"os",
"microcontroller",
"struct",
"array",
"json",
"random",
"digitalio",
"zephyr_serial",
]
MPCONFIG_FLAGS = ["ulab", "nvm", "displayio", "warnings", "alarm", "array", "json"]
async def preprocess_and_split_defs(compiler, source_file, build_path, flags):
build_file = source_file.with_suffix(".pp")
build_file = build_path / (build_file.relative_to(srcdir))
await compiler.preprocess(source_file, build_file, flags=flags)
async with asyncio.TaskGroup() as tg:
for mode in ("qstr", "module", "root_pointer"):
split_file = build_file.relative_to(build_path).with_suffix(f".{mode}")
split_file = build_path / "genhdr" / mode / split_file
split_file.parent.mkdir(exist_ok=True, parents=True)
tg.create_task(
cpbuild.run_command(
[
"python",
srcdir / "py/makeqstrdefs.py",
"split",
mode,
build_file,
build_path / "genhdr" / mode,
split_file,
],
srcdir,
)
)
async def collect_defs(mode, build_path):
output_file = build_path / f"{mode}defs.collected"
splitdir = build_path / "genhdr" / mode
await cpbuild.run_command(
["cat", "-s", *splitdir.glob(f"**/*.{mode}"), ">", output_file],
splitdir,
)
return output_file
async def generate_qstr_headers(build_path, compiler, flags, translation):
collected = await collect_defs("qstr", build_path)
generated = build_path / "genhdr" / "qstrdefs.generated.h"
await cpbuild.run_command(
["python", srcdir / "py" / "makeqstrdata.py", collected, ">", generated],
srcdir,
)
compression_level = 9
# TODO: Do this alongside qstr stuff above.
await cpbuild.run_command(
[
"python",
srcdir / "tools" / "msgfmt.py",
"-o",
build_path / f"{translation}.mo",
srcdir / "locale" / f"{translation}.po",
],
srcdir,
)
await cpbuild.run_command(
[
"python",
srcdir / "py" / "maketranslationdata.py",
"--compression_filename",
build_path / "genhdr" / "compressed_translations.generated.h",
"--translation",
build_path / f"{translation}.mo",
"--translation_filename",
build_path / f"translations-{translation}.c",
"--qstrdefs_filename",
generated,
"--compression_level",
compression_level,
generated,
],
srcdir,
)
async def generate_module_header(build_path):
collected = await collect_defs("module", build_path)
await cpbuild.run_command(
[
"python",
srcdir / "py" / "makemoduledefs.py",
collected,
">",
build_path / "genhdr" / "moduledefs.h",
],
srcdir,
)
async def generate_root_pointer_header(build_path):
collected = await collect_defs("root_pointer", build_path)
await cpbuild.run_command(
[
"python",
srcdir / "py" / "make_root_pointers.py",
collected,
">",
build_path / "genhdr" / "root_pointers.h",
],
srcdir,
)
TINYUSB_SETTINGS = {
"": {
"CFG_TUSB_MCU": "OPT_MCU_MIMXRT10XX",
"CFG_TUD_CDC_RX_BUFSIZE": 640,
"CFG_TUD_CDC_TX_BUFSIZE": 512,
},
"stm32u575xx": {"CFG_TUSB_MCU": "OPT_MCU_STM32U5"},
"nrf52840": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"},
"nrf5340": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"},
# "r7fa8d1bhecbd": {"CFG_TUSB_MCU": "OPT_MCU_RAXXX", "USB_HIGHSPEED": "1", "USBHS_USB_INT_RESUME_IRQn": "54", "USBFS_INT_IRQn": "54", "CIRCUITPY_USB_DEVICE_INSTANCE": "1"},
# ifeq ($(CHIP_FAMILY),$(filter $(CHIP_FAMILY),MIMXRT1011 MIMXRT1015))
# CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=64 -DCFG_TUD_MSC_BUFSIZE=512
# else
# CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024
# endif
}
TINYUSB_SOURCE = {
"stm32u575xx": [
"src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c",
"src/portable/synopsys/dwc2/dcd_dwc2.c",
"src/portable/synopsys/dwc2/hcd_dwc2.c",
"src/portable/synopsys/dwc2/dwc2_common.c",
],
"nrf52840": [
"src/portable/nordic/nrf5x/dcd_nrf5x.c",
],
"nrf5340": [
"src/portable/nordic/nrf5x/dcd_nrf5x.c",
],
# "r7fa8d1bhecbd": [
# "src/portable/renesas/rusb2/dcd_rusb2.c",
# "src/portable/renesas/rusb2/hcd_rusb2.c",
# "src/portable/renesas/rusb2/rusb2_common.c",
# ],
}
async def build_circuitpython():
circuitpython_flags = ["-DCIRCUITPY"]
port_flags = []
enable_mpy_native = False
full_build = False
usb_host = False
tusb_mem_align = 4
board = cmake_args["BOARD_ALIAS"]
if not board:
board = cmake_args["BOARD"]
translation = cmake_args["TRANSLATION"]
if not translation:
translation = "en_US"
for module in ALWAYS_ON_MODULES:
circuitpython_flags.append(f"-DCIRCUITPY_{module.upper()}=1")
lto = cmake_args.get("LTO", "n") == "y"
circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}")
circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}")
circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}")
circuitpython_flags.append(f'-DCIRCUITPY_BOARD_ID=\\"{board}\\"')
circuitpython_flags.append(f"-DCIRCUITPY_TUSB_MEM_ALIGN={tusb_mem_align}")
circuitpython_flags.append(f"-DCIRCUITPY_TRANSLATE_OBJECT={1 if lto else 0}")
circuitpython_flags.append("-DINTERNAL_FLASH_FILESYSTEM")
circuitpython_flags.append("-DLONGINT_IMPL_MPZ")
circuitpython_flags.append("-DCIRCUITPY_SSL_MBEDTLS")
circuitpython_flags.append('-DFFCONF_H=\\"lib/oofatfs/ffconf.h\\"')
circuitpython_flags.extend(("-I", srcdir))
circuitpython_flags.extend(("-I", srcdir / "lib/tinyusb/src"))
circuitpython_flags.extend(("-I", srcdir / "supervisor/shared/usb"))
circuitpython_flags.extend(("-I", builddir))
circuitpython_flags.extend(("-I", portdir))
# circuitpython_flags.extend(("-I", srcdir / "ports" / port / "peripherals"))
# circuitpython_flags.extend(("-I", build_path / board_id))
genhdr = builddir / "genhdr"
genhdr.mkdir(exist_ok=True, parents=True)
version_header = genhdr / "mpversion.h"
async with asyncio.TaskGroup() as tg:
tg.create_task(
cpbuild.run_command(
[
"python",
srcdir / "py" / "makeversionhdr.py",
version_header,
"&&",
"touch",
version_header,
],
srcdir,
check_hash=[version_header],
)
)
board_autogen_task = tg.create_task(zephyr_dts_to_cp_board(builddir, zephyrbuilddir))
board_info = board_autogen_task.result()
mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board)
mpconfigboard = {
"USB_VID": 0x1209,
"USB_PID": 0x000C,
}
if mpconfigboard_fn is None:
mpconfigboard_fn = (
portdir / "boards" / board_info["vendor_id"] / board / "circuitpython.toml"
)
logging.warning(
f"Could not find board config at: boards/{board_info['vendor_id']}/{board}"
)
elif mpconfigboard_fn.exists():
with mpconfigboard_fn.open("rb") as f:
mpconfigboard = tomllib.load(f)
autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml"
enabled_modules = set(DEFAULT_MODULES)
module_reasons = {}
if board_info["wifi"]:
enabled_modules.add("wifi")
module_reasons["wifi"] = "Zephyr board has wifi"
if board_info["flash_count"] > 0:
enabled_modules.add("storage")
module_reasons["storage"] = "Zephyr board has flash"
if "wifi" in enabled_modules:
enabled_modules.add("socketpool")
enabled_modules.add("ssl")
module_reasons["socketpool"] = "Zephyr networking enabled"
module_reasons["ssl"] = "Zephyr networking enabled"
circuitpython_flags.extend(board_info["cflags"])
supervisor_source = [
"main.c",
"extmod/vfs_fat.c",
"lib/tlsf/tlsf.c",
portdir / "background.c",
portdir / "common-hal/microcontroller/__init__.c",
portdir / "common-hal/microcontroller/Pin.c",
portdir / "common-hal/microcontroller/Processor.c",
portdir / "common-hal/os/__init__.c",
"supervisor/stub/misc.c",
"shared/readline/readline.c",
"shared/runtime/context_manager_helpers.c",
"shared/runtime/pyexec.c",
"shared/runtime/interrupt_char.c",
"shared/runtime/stdout_helpers.c",
"shared/runtime/sys_stdio_mphal.c",
"shared-bindings/board/__init__.c",
"shared-bindings/supervisor/Runtime.c",
"shared-bindings/microcontroller/Pin.c",
"shared-bindings/util.c",
"shared-module/board/__init__.c",
"extmod/vfs_reader.c",
"extmod/vfs_blockdev.c",
"extmod/vfs_fat_file.c",
]
top = srcdir
supervisor_source = [pathlib.Path(p) for p in supervisor_source]
supervisor_source.extend(board_info["source_files"])
supervisor_source.extend(top.glob("supervisor/shared/*.c"))
supervisor_source.append(top / "supervisor/shared/translate/translate.c")
# if web_workflow:
# supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c"))
usb_num_endpoint_pairs = board_info.get("usb_num_endpoint_pairs", 0)
soc = board_info["soc"]
usb_ok = usb_num_endpoint_pairs > 0 and soc in TINYUSB_SETTINGS
circuitpython_flags.append(f"-DCIRCUITPY_TINYUSB={1 if usb_ok else 0}")
circuitpython_flags.append(f"-DCIRCUITPY_USB_DEVICE={1 if usb_ok else 0}")
tinyusb_files = []
if usb_ok:
enabled_modules.add("usb_cdc")
for setting in TINYUSB_SETTINGS[soc]:
circuitpython_flags.append(f"-D{setting}={TINYUSB_SETTINGS[soc][setting]}")
tinyusb_files.extend((top / "lib" / "tinyusb" / path for path in TINYUSB_SOURCE[soc]))
for macro in ("USB_PID", "USB_VID"):
circuitpython_flags.append(f"-D{macro}=0x{mpconfigboard.get(macro):04x}")
for macro, limit, value in (
("USB_PRODUCT", 16, board_info["name"]),
("USB_MANUFACTURER", 8, board_info["vendor"]),
):
circuitpython_flags.append(f"-D{macro}='\"{value}\"'")
circuitpython_flags.append(f"-D{macro}_{limit}='\"{value[:limit]}\"'")
usb_interface_name = "CircuitPython"
circuitpython_flags.append("-DCFG_TUSB_OS=OPT_OS_ZEPHYR")
circuitpython_flags.append(f"-DUSB_INTERFACE_NAME='\"{usb_interface_name}\"'")
circuitpython_flags.append(f"-DUSB_NUM_ENDPOINT_PAIRS={usb_num_endpoint_pairs}")
for direction in ("IN", "OUT"):
circuitpython_flags.append(f"-DUSB_NUM_{direction}_ENDPOINTS={usb_num_endpoint_pairs}")
# USB is special because it doesn't have a matching module.
msc_enabled = board_info["flash_count"] > 0
if msc_enabled:
circuitpython_flags.append("-DCFG_TUD_MSC_BUFSIZE=1024")
circuitpython_flags.append("-DCIRCUITPY_USB_MSC_ENABLED_DEFAULT=1")
tinyusb_files.append(top / "lib/tinyusb/src/class/msc/msc_device.c")
supervisor_source.append(top / "supervisor/shared/usb/usb_msc_flash.c")
circuitpython_flags.append(f"-DCIRCUITPY_USB_MSC={1 if msc_enabled else 0}")
if "usb_cdc" in enabled_modules:
tinyusb_files.extend(top.glob("lib/tinyusb/*.c"))
tinyusb_files.append(top / "lib/tinyusb/src/class/cdc/cdc_device.c")
circuitpython_flags.append("-DCFG_TUD_CDC_RX_BUFSIZE=640")
circuitpython_flags.append("-DCFG_TUD_CDC_TX_BUFSIZE=512")
circuitpython_flags.append("-DCFG_TUD_CDC=2")
circuitpython_flags.append("-DCIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT=1")
circuitpython_flags.append("-DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=0")
if "usb_hid_enabled_default" not in mpconfigboard:
mpconfigboard["usb_hid_enabled_default"] = usb_num_endpoint_pairs >= 5
if "usb_midi_enabled_default" not in mpconfigboard:
mpconfigboard["usb_midi_enabled_default"] = usb_num_endpoint_pairs >= 8
tinyusb_files.extend(
(top / "lib/tinyusb/src/common/tusb_fifo.c", top / "lib/tinyusb/src/tusb.c")
)
supervisor_source.extend(
(portdir / "supervisor/usb.c", top / "supervisor/shared/usb/usb.c")
)
tinyusb_files.extend(
(
top / "lib/tinyusb/src/device/usbd.c",
top / "lib/tinyusb/src/device/usbd_control.c",
)
)
supervisor_source.extend(
(top / "supervisor/shared/usb/usb_desc.c", top / "supervisor/shared/usb/usb_device.c")
)
elif usb_num_endpoint_pairs > 0:
module_reasons["usb_cdc"] = f"No TinyUSB settings for {soc}"
circuitpython_flags.append(f"-DCIRCUITPY_PORT_SERIAL={0 if usb_ok else 1}")
# ifeq ($(CIRCUITPY_USB_HID), 1)
# SRC_SUPERVISOR += \
# lib/tinyusb/src/class/hid/hid_device.c \
# shared-bindings/usb_hid/__init__.c \
# shared-bindings/usb_hid/Device.c \
# shared-module/usb_hid/__init__.c \
# shared-module/usb_hid/Device.c \
# endif
# ifeq ($(CIRCUITPY_USB_MIDI), 1)
# SRC_SUPERVISOR += \
# lib/tinyusb/src/class/midi/midi_device.c \
# shared-bindings/usb_midi/__init__.c \
# shared-bindings/usb_midi/PortIn.c \
# shared-bindings/usb_midi/PortOut.c \
# shared-module/usb_midi/__init__.c \
# shared-module/usb_midi/PortIn.c \
# shared-module/usb_midi/PortOut.c \
# endif
# ifeq ($(CIRCUITPY_USB_VIDEO), 1)
# SRC_SUPERVISOR += \
# shared-bindings/usb_video/__init__.c \
# shared-module/usb_video/__init__.c \
# shared-bindings/usb_video/USBFramebuffer.c \
# shared-module/usb_video/USBFramebuffer.c \
# lib/tinyusb/src/class/video/video_device.c \
# CFLAGS += -DCFG_TUD_VIDEO=1 -DCFG_TUD_VIDEO_STREAMING=1 -DCFG_TUD_VIDEO_STREAMING_EP_BUFSIZE=256 -DCFG_TUD_VIDEO_STREAMING_BULK=1
# endif
# ifeq ($(CIRCUITPY_USB_VENDOR), 1)
# SRC_SUPERVISOR += \
# lib/tinyusb/src/class/vendor/vendor_device.c \
# endif
# ifeq ($(CIRCUITPY_TINYUSB_HOST), 1)
# SRC_SUPERVISOR += \
# lib/tinyusb/src/host/hub.c \
# lib/tinyusb/src/host/usbh.c \
# endif
# ifeq ($(CIRCUITPY_USB_KEYBOARD_WORKFLOW), 1)
# SRC_SUPERVISOR += \
# lib/tinyusb/src/class/hid/hid_host.c \
# supervisor/shared/usb/host_keyboard.c \
# endif
if "ssl" in enabled_modules:
# TODO: Figure out how to get these paths from zephyr
circuitpython_flags.append('-DMBEDTLS_CONFIG_FILE=\\"config-tls-generic.h\\"')
circuitpython_flags.extend(
("-isystem", portdir / "modules" / "crypto" / "tinycrypt" / "lib" / "include")
)
circuitpython_flags.extend(
("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include")
)
circuitpython_flags.extend(
("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "configs")
)
circuitpython_flags.extend(
("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include")
)
circuitpython_flags.extend(("-isystem", zephyrdir / "modules" / "mbedtls" / "configs"))
supervisor_source.append(top / "lib" / "mbedtls_config" / "crt_bundle.c")
# Make sure all modules have a setting by filling in defaults.
hal_source = []
autogen_board_info = tomlkit.document()
autogen_board_info.add(
tomlkit.comment(
"This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info."
)
)
autogen_board_info.add("name", board_info["vendor"] + " " + board_info["name"])
autogen_modules = tomlkit.table()
autogen_board_info.add("modules", autogen_modules)
for module in sorted(
list(top.glob("shared-bindings/*")) + list(portdir.glob("bindings/*")),
key=lambda x: x.name,
):
if not module.is_dir():
continue
enabled = module.name in enabled_modules
# print(f"Module {module.name} enabled: {enabled}")
v = tomlkit.item(enabled)
if module.name in module_reasons:
v.comment(module_reasons[module.name])
autogen_modules.add(module.name, v)
circuitpython_flags.append(f"-DCIRCUITPY_{module.name.upper()}={1 if enabled else 0}")
if enabled:
hal_source.extend(portdir.glob(f"bindings/{module.name}/*.c"))
hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c"))
hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c"))
hal_source.extend(top.glob(f"shared-module/{module.name}/*.c"))
if os.environ.get("CI", "false") == "true":
# Fail the build if it isn't up to date.
if (
not autogen_board_info_fn.exists()
or autogen_board_info_fn.read_text() != tomlkit.dumps(autogen_board_info)
):
logger.error(f"autogen_board_info.toml is out of date.")
raise RuntimeError(
f"autogen_board_info.toml is missing or out of date. Please run `make BOARD={board}` locally and commit {autogen_board_info_fn}."
)
elif autogen_board_info_fn.parent.exists():
autogen_board_info_fn.write_text(tomlkit.dumps(autogen_board_info))
for mpflag in MPCONFIG_FLAGS:
enabled = mpflag in DEFAULT_MODULES
circuitpython_flags.append(f"-DCIRCUITPY_{mpflag.upper()}={1 if enabled else 0}")
source_files = supervisor_source + hal_source + ["extmod/vfs.c"]
assembly_files = []
for file in top.glob("py/*.c"):
source_files.append(file)
qstr_flags = "-DNO_QSTR"
async with asyncio.TaskGroup() as tg:
extra_source_flags = {}
for source_file in source_files:
tg.create_task(
preprocess_and_split_defs(
compiler,
top / source_file,
builddir,
[qstr_flags, *circuitpython_flags, *port_flags],
)
)
if "ssl" in enabled_modules:
crt_bundle = builddir / "x509_crt_bundle.S"
roots_pem = srcdir / "lib/certificates/data/roots.pem"
generator = srcdir / "tools/gen_crt_bundle.py"
tg.create_task(
cpbuild.run_command(
[
"python",
generator,
"-i",
roots_pem,
"-o",
crt_bundle,
"--asm",
],
srcdir,
)
)
assembly_files.append(crt_bundle)
async with asyncio.TaskGroup() as tg:
board_build = builddir
tg.create_task(
generate_qstr_headers(
board_build, compiler, [qstr_flags, *circuitpython_flags, *port_flags], translation
)
)
tg.create_task(generate_module_header(board_build))
tg.create_task(generate_root_pointer_header(board_build))
# This file is generated by the QSTR/translation process.
source_files.append(builddir / f"translations-{translation}.c")
# These files don't include unique QSTRs. They just need to be compiled.
source_files.append(portdir / "supervisor" / "flash.c")
source_files.append(portdir / "supervisor" / "port.c")
source_files.append(portdir / "supervisor" / "serial.c")
source_files.append(srcdir / "lib" / "oofatfs" / "ff.c")
source_files.append(srcdir / "lib" / "oofatfs" / "ffunicode.c")
source_files.append(srcdir / "extmod" / "vfs_fat_diskio.c")
source_files.append(srcdir / "shared/timeutils/timeutils.c")
source_files.append(srcdir / "shared-module/time/__init__.c")
source_files.append(srcdir / "shared-module/os/__init__.c")
source_files.append(srcdir / "shared-module/supervisor/__init__.c")
source_files.append(portdir / "bindings/zephyr_kernel/__init__.c")
source_files.append(portdir / "common-hal/zephyr_kernel/__init__.c")
# source_files.append(srcdir / "ports" / port / "peripherals" / "nrf" / "nrf52840" / "pins.c")
assembly_files.append(srcdir / "ports/nordic/supervisor/cpu.s")
source_files.extend(assembly_files)
source_files.extend(tinyusb_files)
objects = []
async with asyncio.TaskGroup() as tg:
for source_file in source_files:
source_file = top / source_file
build_file = source_file.with_suffix(".o")
object_file = builddir / (build_file.relative_to(top))
objects.append(object_file)
tg.create_task(
compiler.compile(source_file, object_file, [*circuitpython_flags, *port_flags])
)
await compiler.archive(objects, pathlib.Path(cmake_args["OUTPUT_FILE"]))
async def main():
try:
await build_circuitpython()
except* RuntimeError as e:
logger.error(e)
sys.exit(len(e.exceptions))
handler = colorlog.StreamHandler()
handler.setFormatter(colorlog.ColoredFormatter("%(log_color)s%(levelname)s:%(name)s:%(message)s"))
logging.basicConfig(level=logging.INFO, handlers=[handler])
asyncio.run(main())

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,436 @@
import asyncio
import inspect
import logging
import os
import pathlib
import shlex
import time
import hashlib
import atexit
import json
import re
import sys
logger = logging.getLogger(__name__)
shared_semaphore = None
trace_entries = []
LAST_BUILD_TIMES = {}
ALREADY_RUN = {}
_last_build_times = pathlib.Path("last_build_times.json")
if _last_build_times.exists():
with open(_last_build_times) as f:
LAST_BUILD_TIMES = json.load(f)
logger.info("Build times loaded.")
else:
logger.warn(
"No last build times found. This is normal if you're running this for the first time."
)
def save_trace():
with open("trace.json", "w") as f:
json.dump(trace_entries, f)
with open("last_build_times.json", "w") as f:
json.dump(LAST_BUILD_TIMES, f)
logger.info("wrote trace %s", pathlib.Path(".").absolute() / "trace.json")
logger.info("wrote build times %s", pathlib.Path(".").absolute() / "last_build_times.json")
atexit.register(save_trace)
class _TokenProtocol(asyncio.Protocol):
def __init__(self, client):
self.client = client
def data_received(self, data):
# Data can be multiple tokens at once.
for i, _ in enumerate(data):
self.client.new_token(data[i : i + 1])
class _MakeJobClient:
def __init__(self, fifo_path=None, read_fd=None, write_fd=None):
self.fifo_path = fifo_path
if fifo_path is not None:
self.writer = open(fifo_path, "wb")
self.reader = open(fifo_path, "rb")
self.tokens_in_use = []
self.pending_futures = []
self.read_transport: asyncio.ReadTransport | None = None
self.read_protocol = None
self.started = None
def new_token(self, token):
# Keep a token and reuse it. Ignore cancelled Futures.
if self.pending_futures:
future = self.pending_futures.pop(0)
while future.cancelled() and self.pending_futures:
future = self.pending_futures.pop(0)
if not future.cancelled():
future.set_result(token)
return
self.read_transport.pause_reading()
self.writer.write(token)
self.writer.flush()
async def __aenter__(self):
loop = asyncio.get_event_loop()
if self.started is None:
self.started = asyncio.Event()
self.read_transport, self.read_protocol = await loop.connect_read_pipe(
lambda: _TokenProtocol(self), self.reader
)
self.started.set()
await self.started.wait()
future = loop.create_future()
self.pending_futures.append(future)
self.read_transport.resume_reading()
self.tokens_in_use.append(await future)
async def __aexit__(self, exc_type, exc, tb):
token = self.tokens_in_use.pop()
self.new_token(token)
def _create_semaphore():
match = re.search(r"fifo:([^\s]+)", os.environ.get("MAKEFLAGS", ""))
fifo_path = None
if match:
fifo_path = match.group(1)
return _MakeJobClient(fifo_path=fifo_path)
return asyncio.BoundedSemaphore(1)
shared_semaphore = _create_semaphore()
tracks = []
max_track = 0
async def run_command(command, working_directory, description=None, check_hash=[], extradeps=[]):
"""
Runs a command asynchronously. The command should ideally be a list of strings
and pathlib.Path objects. If all of the paths haven't been modified since the last
time the command was run, then it'll be skipped. (The last time a command was run
is stored based on the hash of the command.)
The command is run from the working_directory and the paths are made relative to it.
Description is used for logging only. If None, the command itself is logged.
Paths in check_hash are hashed before and after the command. If the hash is
the same, then the old mtimes are reset. This is helpful if a command may produce
the same result and you don't want the rest of the build impacted.
"""
paths = []
if isinstance(command, list):
for i, part in enumerate(command):
if isinstance(part, pathlib.Path):
paths.append(part)
part = part.relative_to(working_directory, walk_up=True)
# if isinstance(part, list):
command[i] = str(part)
command = " ".join(command)
command_hash = hashlib.sha3_256(command.encode("utf-8"))
command_hash.update(str(working_directory).encode("utf-8"))
command_hash = command_hash.hexdigest()
# If a command is run multiple times, then wait for the first one to continue. Don't run it again.
if command_hash in ALREADY_RUN:
logger.debug(f"Already running {command_hash} {command}")
await ALREADY_RUN[command_hash].wait()
return
ALREADY_RUN[command_hash] = asyncio.Event()
run_reason = None
# If the path inputs are all older than the last time we ran them, then we don't have anything to do.
if command_hash in LAST_BUILD_TIMES and all((p.exists() for p in paths)):
last_build_time = LAST_BUILD_TIMES[command_hash]
# Check all paths in the command because one must be modified by the command.
newest_file = max((0 if p.is_dir() else p.stat().st_mtime_ns for p in paths))
nothing_newer = newest_file <= last_build_time
logger.debug(f"Last build time {last_build_time} Newest file {newest_file}")
if nothing_newer:
# Escape early if an extra dep is newer.
for p in extradeps:
if p.stat().st_mtime_ns > last_build_time:
run_reason = f"{p.relative_to(working_directory, walk_up=True)} is newer"
nothing_newer = False
break
else:
for p in paths:
if p.stat().st_mtime_ns == newest_file:
run_reason = f"{p.relative_to(working_directory, walk_up=True)} is newer"
break
if nothing_newer:
logger.debug(f"Nothing newer {command[-32:]}")
ALREADY_RUN[command_hash].set()
return
else:
run_reason = "no previous build time"
newest_file = 0
file_hashes = {}
for path in check_hash:
if not path.exists():
continue
with path.open("rb") as f:
digest = hashlib.file_digest(f, "sha256")
stat = path.stat()
mtimes = (stat.st_atime, stat.st_mtime)
mtimes_ns = (stat.st_atime_ns, stat.st_mtime_ns)
file_hashes[path] = (digest, mtimes, mtimes_ns)
cancellation = None
async with shared_semaphore:
global max_track
if not tracks:
max_track += 1
tracks.append(max_track)
track = tracks.pop()
start_time = time.perf_counter_ns() // 1000
process = await asyncio.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=working_directory,
)
try:
stdout, stderr = await process.communicate()
except asyncio.CancelledError as e:
cancellation = e
stdout, stderr = await process.communicate()
end_time = time.perf_counter_ns() // 1000
trace_entries.append(
{
"name": command if not description else description,
"ph": "X",
"pid": 0,
"tid": track,
"ts": start_time,
"dur": end_time - start_time,
}
)
tracks.append(track)
if process.returncode == 0:
old_newest_file = newest_file
newest_file = max((p.stat().st_mtime_ns for p in paths))
LAST_BUILD_TIMES[command_hash] = newest_file
ALREADY_RUN[command_hash].set()
for path in check_hash:
if path not in file_hashes:
continue
with path.open("rb") as f:
digest = hashlib.file_digest(f, "sha256")
old_digest, _, old_mtimes_ns = file_hashes[path]
if old_digest.digest() == digest.digest():
logger.debug(f"{path} is unchanged")
os.utime(path, ns=old_mtimes_ns)
# If something has failed and we've been canceled, hide our success so
# the error is clear.
if cancellation:
raise cancellation
if description:
logger.info(f"{description} ({run_reason})")
logger.debug(command)
else:
logger.info(f"{command} ({run_reason})")
if old_newest_file == newest_file:
logger.error("No files were modified by the command.")
raise RuntimeError()
else:
if command_hash in LAST_BUILD_TIMES:
del LAST_BUILD_TIMES[command_hash]
if stdout:
logger.info(stdout.decode("utf-8").strip())
if stderr:
logger.warning(stderr.decode("utf-8").strip())
if not stdout and not stderr:
logger.warning("No output")
logger.error(command)
if cancellation:
raise cancellation
raise RuntimeError()
async def run_function(
function,
positional,
named,
description=None,
):
async with shared_semaphore:
global max_track
if not tracks:
max_track += 1
tracks.append(max_track)
track = tracks.pop()
start_time = time.perf_counter_ns() // 1000
result = await asyncio.to_thread(function, *positional, **named)
end_time = time.perf_counter_ns() // 1000
trace_entries.append(
{
"name": str(function) if not description else description,
"ph": "X",
"pid": 0,
"tid": track,
"ts": start_time,
"dur": end_time - start_time,
}
)
tracks.append(track)
if description:
logger.info(description)
logger.debug(function)
else:
logger.info(function)
return result
def run_in_thread(function):
def wrapper(*positional, **named):
return run_function(function, positional, named)
return wrapper
cwd = pathlib.Path.cwd()
def parse_depfile(f):
depfile_contents = f.read_text().split()
extradeps = []
for dep in depfile_contents:
if dep == "\\" or dep[-1] == ":":
continue
if dep.startswith("/"):
extradeps.append(pathlib.Path(dep))
else:
extradeps.append(self.srcdir / dep)
class Compiler:
def __init__(self, srcdir: pathlib.Path, builddir: pathlib.Path, cmake_args):
self.c_compiler = cmake_args["CC"]
self.ar = cmake_args["AR"]
self.cflags = cmake_args.get("CFLAGS", "")
self.srcdir = srcdir
self.builddir = builddir
async def preprocess(
self, source_file: pathlib.Path, output_file: pathlib.Path, flags: list[pathlib.Path]
):
output_file.parent.mkdir(parents=True, exist_ok=True)
depfile = output_file.parent / (output_file.name + ".d")
if depfile.exists():
pass
await run_command(
[
self.c_compiler,
"-E",
"-MMD",
"-MF",
depfile,
"-c",
source_file,
self.cflags,
*flags,
"-o",
output_file,
],
description=f"Preprocess {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}",
working_directory=self.srcdir,
check_hash=[output_file],
)
async def compile(
self, source_file: pathlib.Path, output_file: pathlib.Path, flags: list[pathlib.Path] = []
):
if isinstance(source_file, str):
source_file = self.srcdir / source_file
if isinstance(output_file, str):
output_file = self.builddir / output_file
output_file.parent.mkdir(parents=True, exist_ok=True)
depfile = output_file.with_suffix(".d")
extradeps = []
if depfile.exists():
depfile_contents = depfile.read_text().split()
for dep in depfile_contents:
if dep == "\\" or dep[-1] == ":":
continue
if dep.startswith("/"):
extradeps.append(pathlib.Path(dep))
else:
extradeps.append(self.srcdir / dep)
await run_command(
[self.c_compiler, self.cflags, "-MMD", "-c", source_file, *flags, "-o", output_file],
description=f"Compile {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}",
working_directory=self.srcdir,
extradeps=extradeps,
)
async def archive(self, objects: list[pathlib.Path], output_file: pathlib.Path):
output_file.parent.mkdir(parents=True, exist_ok=True)
# Do one file at a time so that we don't have a long command line. run_command
# should skip unchanged files ok.
input_files = output_file.with_suffix(output_file.suffix + ".input_files")
input_file_content = "\n".join(str(p) for p in objects)
# Windows paths have \ as separator but ar wants them as / (like UNIX)
input_file_content = input_file_content.replace("\\", "/")
input_files.write_text(input_file_content)
await run_command(
[self.ar, "rvs", output_file, f"@{input_files}"],
description=f"Create archive {output_file.relative_to(self.srcdir)}",
working_directory=self.srcdir,
extradeps=objects,
)
async def link(
self,
objects: list[pathlib.Path],
output_file: pathlib.Path,
linker_script: pathlib.Path,
flags: list[str] = [],
print_memory_use=True,
output_map_file=True,
gc_sections=True,
):
output_file.parent.mkdir(parents=True, exist_ok=True)
link_flags = []
if print_memory_use:
link_flags.append("-Wl,--print-memory-usage")
if output_map_file:
link_flags.append(
"-Wl,-Map="
+ str(output_file.with_suffix(".elf.map").relative_to(caller_directory))
)
if gc_sections:
link_flags.append("-Wl,--gc-sections")
await run_command(
[
self.c_compiler,
*link_flags,
*flags,
*objects,
"-fuse-ld=lld",
"-T",
linker_script,
"-o",
output_file,
],
description=f"Link {output_file.relative_to(cwd)}",
working_directory=caller_directory,
)

View file

@ -0,0 +1,27 @@
import pathlib
mapping = {}
drivers = pathlib.Path("lib/zephyr/drivers")
for p in drivers.glob("**/*.c"):
for line in p.open():
if line.startswith("#define DT_DRV_COMPAT"):
compat = line.rsplit(None, 1)[-1].strip()
driver = str(p.parent.relative_to(drivers))
print(compat, "->", driver)
mapping[compat] = driver
with open("cptools/compat2driver.py", "w") as f:
f.write("# This file was generated by gen_compat2driver.py\n")
f.write("COMPAT_TO_DRIVER = {\n")
# Replaces make it pass lint.
last_driver = None
for key in sorted(mapping.keys(), key=lambda x: (mapping[x], x)):
driver = mapping[key]
if driver != last_driver:
if last_driver:
f.write(" #\n")
f.write(f" # {driver}\n")
last_driver = driver
f.write(f' "{key}": "{mapping[key]}",\n')
f.write("}\n")

View file

@ -0,0 +1,23 @@
# Called by the Makefile before calling out to `west`.
import pathlib
import subprocess
import sys
import tomllib
import board_tools
portdir = pathlib.Path(__file__).resolve().parent.parent
board = sys.argv[-1]
mpconfigboard = board_tools.find_mpconfigboard(portdir, board)
if mpconfigboard is None:
# Assume it doesn't need any prep.
sys.exit(0)
with mpconfigboard.open("rb") as f:
mpconfigboard = tomllib.load(f)
blobs = mpconfigboard.get("BLOBS", [])
for blob in blobs:
subprocess.run(["west", "blobs", "fetch", blob], check=True)

View file

@ -0,0 +1,395 @@
import logging
import pathlib
import cpbuild
from devicetree import dtlib
import yaml
from compat2driver import COMPAT_TO_DRIVER
logger = logging.getLogger(__name__)
MANUAL_COMPAT_TO_DRIVER = {
"renesas_ra_nv_flash": "flash",
}
# These are controllers, not the flash devices themselves.
BLOCKED_FLASH_COMPAT = (
"renesas,ra-qspi",
"renesas,ra-ospi-b",
"nordic,nrf-spim",
)
CONNECTORS = {
"mikro-bus": [
"AN",
"RST",
"CS",
"SCK",
"MISO",
"MOSI",
"PWM",
"INT",
"RX",
"TX",
"SCL",
"SDA",
],
"arduino-header-r3": [
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"D0",
"D1",
"D2",
"D3",
"D4",
"D5",
"D6",
"D7",
"D8",
"D9",
"D10",
"D11",
"D12",
"D13",
"D14",
"D15",
],
"adafruit-feather-header": [
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"SCK",
"MOSI",
"MISO",
"RX",
"TX",
"D4",
"SDA",
"SCL",
"D5",
"D6",
"D9",
"D10",
"D11",
"D12",
"D13",
],
"renesas,ra-gpio-mipi-header": [
"IIC_SDA",
"DISP_BLEN",
"IIC_SCL",
"DISP_INT",
"DISP_RST",
],
}
@cpbuild.run_in_thread
def zephyr_dts_to_cp_board(builddir, zephyrbuilddir):
board_dir = builddir / "board"
# Auto generate board files from device tree.
board_info = {
"wifi": False,
"usb_device": False,
}
runners = zephyrbuilddir / "runners.yaml"
runners = yaml.safe_load(runners.read_text())
zephyr_board_dir = pathlib.Path(runners["config"]["board_dir"])
board_yaml = zephyr_board_dir / "board.yml"
board_yaml = yaml.safe_load(board_yaml.read_text())
board_info["vendor_id"] = board_yaml["board"]["vendor"]
vendor_index = zephyr_board_dir.parent / "index.rst"
if vendor_index.exists():
vendor_index = vendor_index.read_text()
vendor_index = vendor_index.split("\n")
vendor_name = vendor_index[2].strip()
else:
vendor_name = board_info["vendor_id"]
board_info["vendor"] = vendor_name
soc_name = board_yaml["board"]["socs"][0]["name"]
board_info["soc"] = soc_name
board_name = board_yaml["board"]["full_name"]
board_info["name"] = board_name
# board_id_yaml = zephyr_board_dir / (zephyr_board_dir.name + ".yaml")
# board_id_yaml = yaml.safe_load(board_id_yaml.read_text())
# print(board_id_yaml)
# board_name = board_id_yaml["name"]
enabled_modules = []
dts = zephyrbuilddir / "zephyr.dts"
edt_pickle = dtlib.DT(dts)
node2alias = {}
for alias in edt_pickle.alias2node:
node = edt_pickle.alias2node[alias]
if node not in node2alias:
node2alias[node] = []
node2alias[node].append(alias)
ioports = {}
all_ioports = []
board_names = {}
flashes = []
rams = []
status_led = None
path2chosen = {}
chosen2path = {}
usb_num_endpoint_pairs = 0
for k in edt_pickle.root.nodes["chosen"].props:
value = edt_pickle.root.nodes["chosen"].props[k]
path2chosen[value.to_path()] = k
chosen2path[k] = value.to_path()
remaining_nodes = set([edt_pickle.root])
while remaining_nodes:
node = remaining_nodes.pop()
remaining_nodes.update(node.nodes.values())
gpio = node.props.get("gpio-controller", False)
gpio_map = node.props.get("gpio-map", [])
status = node.props.get("status", None)
if status is None:
status = "okay"
else:
status = status.to_string()
compatible = []
if "compatible" in node.props:
compatible = node.props["compatible"].to_strings()
logger.debug(node.name, status)
chosen = None
if node in path2chosen:
chosen = path2chosen[node]
logger.debug(" chosen:", chosen)
for c in compatible:
underscored = c.replace(",", "_").replace("-", "_")
driver = COMPAT_TO_DRIVER.get(underscored, None)
if "mmio" in c:
logger.debug(" ", c, node.labels, node.props)
address, size = node.props["reg"].to_nums()
end = address + size
if chosen == "zephyr,sram":
start = "z_mapped_end"
elif "zephyr,memory-region" in node.props:
start = "__" + node.props["zephyr,memory-region"].to_string() + "_end"
else:
# Check to see if the chosen sram is a subset of this region. If it is,
# then do as above for a smaller region and assume the rest is reserved.
chosen_sram = chosen2path["zephyr,sram"]
chosen_address, chosen_size = chosen_sram.props["reg"].to_nums()
chosen_end = chosen_address + chosen_size
if address <= chosen_address <= end and address <= chosen_end <= end:
start = "z_mapped_end"
address = chosen_address
size = chosen_size
end = chosen_end
else:
start = address
info = (node.labels[0], start, end, size, node.path)
if chosen == "zephyr,sram":
rams.insert(0, info)
else:
rams.append(info)
if not driver:
driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None)
logger.debug(" ", underscored, driver)
if not driver:
continue
if driver == "flash" and status == "okay":
if not chosen and compatible[0] not in BLOCKED_FLASH_COMPAT:
# Skip chosen nodes because they are used by Zephyr.
flashes.append(f"DEVICE_DT_GET(DT_NODELABEL({node.labels[0]}))")
else:
logger.debug(" skipping due to blocked compat")
if driver == "usb/udc" and status == "okay":
board_info["usb_device"] = True
props = node.props
if "num-bidir-endpoints" not in props:
props = node.parent.props
usb_num_endpoint_pairs = 0
if "num-bidir-endpoints" in props:
usb_num_endpoint_pairs = props["num-bidir-endpoints"].to_num()
single_direction_endpoints = []
for d in ("in", "out"):
eps = f"num-{d}-endpoints"
single_direction_endpoints.append(props[eps].to_num() if eps in props else 0)
# Count separate in/out pairs as bidirectional.
usb_num_endpoint_pairs += min(single_direction_endpoints)
if driver.startswith("wifi") and status == "okay":
board_info["wifi"] = True
if gpio:
if "ngpios" in node.props:
ngpios = node.props["ngpios"].to_num()
else:
ngpios = 32
all_ioports.append(node.labels[0])
if status == "okay":
ioports[node.labels[0]] = set(range(0, ngpios))
if gpio_map:
i = 0
for offset, t, label in gpio_map._markers:
if not label:
continue
num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big")
if (label, num) not in board_names:
board_names[(label, num)] = []
board_names[(label, num)].append(CONNECTORS[compatible[0]][i])
i += 1
if "gpio-leds" in compatible:
for led in node.nodes:
led = node.nodes[led]
props = led.props
ioport = props["gpios"]._markers[1][2]
num = int.from_bytes(props["gpios"].value[4:8], "big")
if "label" in props:
if (ioport, num) not in board_names:
board_names[(ioport, num)] = []
board_names[(ioport, num)].append(props["label"].to_string())
if led in node2alias:
if (ioport, num) not in board_names:
board_names[(ioport, num)] = []
if "led0" in node2alias[led]:
board_names[(ioport, num)].append("LED")
status_led = (ioport, num)
board_names[(ioport, num)].extend(node2alias[led])
if "gpio-keys" in compatible:
for key in node.nodes:
props = node.nodes[key].props
ioport = props["gpios"]._markers[1][2]
num = int.from_bytes(props["gpios"].value[4:8], "big")
if (ioport, num) not in board_names:
board_names[(ioport, num)] = []
board_names[(ioport, num)].append(props["label"].to_string())
if key in node2alias:
if "sw0" in node2alias[key]:
board_names[(ioport, num)].append("BUTTON")
board_names[(ioport, num)].extend(node2alias[key])
a, b = all_ioports[:2]
i = 0
while a[i] == b[i]:
i += 1
shared_prefix = a[:i]
for ioport in ioports:
if not ioport.startswith(shared_prefix):
shared_prefix = ""
break
pin_defs = []
pin_declarations = ["#pragma once"]
mcu_pin_mapping = []
board_pin_mapping = []
for ioport in sorted(ioports.keys()):
for num in ioports[ioport]:
pin_object_name = f"P{ioport[len(shared_prefix):].upper()}_{num:02d}"
if status_led and (ioport, num) == status_led:
status_led = pin_object_name
pin_defs.append(
f"const mcu_pin_obj_t pin_{pin_object_name} = {{ .base.type = &mcu_pin_type, .port = DEVICE_DT_GET(DT_NODELABEL({ioport})), .number = {num}}};"
)
pin_declarations.append(f"extern const mcu_pin_obj_t pin_{pin_object_name};")
mcu_pin_mapping.append(
f"{{ MP_ROM_QSTR(MP_QSTR_{pin_object_name}), MP_ROM_PTR(&pin_{pin_object_name}) }},"
)
board_pin_names = board_names.get((ioport, num), [])
for board_pin_name in board_pin_names:
board_pin_name = board_pin_name.upper().replace(" ", "_").replace("-", "_")
board_pin_mapping.append(
f"{{ MP_ROM_QSTR(MP_QSTR_{board_pin_name}), MP_ROM_PTR(&pin_{pin_object_name}) }},"
)
pin_defs = "\n".join(pin_defs)
pin_declarations = "\n".join(pin_declarations)
board_pin_mapping = "\n ".join(board_pin_mapping)
mcu_pin_mapping = "\n ".join(mcu_pin_mapping)
board_dir.mkdir(exist_ok=True, parents=True)
header = board_dir / "mpconfigboard.h"
if status_led:
status_led = f"#define MICROPY_HW_LED_STATUS (&pin_{status_led})\n"
else:
status_led = ""
ram_list = []
ram_externs = []
max_size = 0
for ram in rams:
device, start, end, size, path = ram
max_size = max(max_size, size)
if isinstance(start, str):
ram_externs.append(f"extern uint32_t {start};")
start = "&" + start
else:
start = f"(uint32_t*) 0x{start:08x}"
ram_list.append(f" {start}, (uint32_t*) 0x{end:08x}, // {path}")
ram_list = "\n".join(ram_list)
ram_externs = "\n".join(ram_externs)
new_header_content = f"""#pragma once
#define MICROPY_HW_BOARD_NAME "{board_name}"
#define MICROPY_HW_MCU_NAME "{soc_name}"
#define CIRCUITPY_RAM_DEVICE_COUNT {len(rams)}
{status_led}
"""
if not header.exists() or header.read_text() != new_header_content:
header.write_text(new_header_content)
pins = board_dir / "autogen-pins.h"
if not pins.exists() or pins.read_text() != pin_declarations:
pins.write_text(pin_declarations)
board_c = board_dir / "board.c"
new_board_c_content = f"""
// This file is autogenerated by build_circuitpython.py
#include "shared-bindings/board/__init__.h"
#include <stdint.h>
#include "py/obj.h"
#include "py/mphal.h"
const struct device* const flashes[] = {{ {", ".join(flashes)} }};
const int circuitpy_flash_device_count = {len(flashes)};
{ram_externs}
const uint32_t* const ram_bounds[] = {{
{ram_list}
}};
const size_t circuitpy_max_ram_size = {max_size};
{pin_defs}
static const mp_rom_map_elem_t mcu_pin_globals_table[] = {{
{mcu_pin_mapping}
}};
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_globals_table);
static const mp_rom_map_elem_t board_module_globals_table[] = {{
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
{board_pin_mapping}
// {{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }},
}};
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
"""
if not board_c.exists() or new_board_c_content != board_c.read_text():
board_c.write_text(new_board_c_content)
board_info["source_files"] = [board_c]
board_info["cflags"] = ("-I", board_dir)
board_info["flash_count"] = len(flashes)
board_info["usb_num_endpoint_pairs"] = usb_num_endpoint_pairs
return board_info

View file

@ -0,0 +1,23 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2015 Glenn Ruben Bakke
// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#pragma once
// 24kiB stack
#define CIRCUITPY_DEFAULT_STACK_SIZE (24 * 1024)
#define MICROPY_PY_SYS_PLATFORM "Zephyr"
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1)
#define DIGITALINOUT_INVALID_DRIVE_MODE (1)
#define CIRCUITPY_DEBUG_TINYUSB 0
////////////////////////////////////////////////////////////////////////////////////////////////////
// This also includes mpconfigboard.h.
#include "py/circuitpy_mpconfig.h"

View file

@ -0,0 +1,19 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2015 Glenn Ruben Bakke
//
// SPDX-License-Identifier: MIT
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "shared/runtime/interrupt_char.h"
#include "py/mpconfig.h"
#include "supervisor/shared/tick.h"
#define mp_hal_ticks_ms() ((mp_uint_t)supervisor_ticks_ms32())
#define mp_hal_delay_us(us) NRFX_DELAY_US((uint32_t)(us))
bool mp_hal_stdin_any(void);

28
ports/zephyr-cp/prj.conf Normal file
View file

@ -0,0 +1,28 @@
CONFIG_SYS_HEAP_RUNTIME_STATS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_STD_C23=y
CONFIG_DYNAMIC_INTERRUPTS=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_FLASH_MAP_LABELS=y
CONFIG_MAIN_STACK_SIZE=24288
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_THREAD_STACK_INFO=y
CONFIG_STACK_SENTINEL=y
CONFIG_DEBUG_THREAD_INFO=y
CONFIG_DEBUG_INFO=y
CONFIG_USB_DEVICE_STACK=n
CONFIG_HWINFO=y
CONFIG_REBOOT=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_ASSERT=y
CONFIG_LOG_BLOCK_IN_THREAD=y
CONFIG_EVENTS=y

View file

@ -0,0 +1,4 @@
CONFIG_NRFX_UARTE0=y
CONFIG_NRFX_UARTE1=y
CONFIG_NRFX_POWER=y

Some files were not shown because too many files have changed in this diff Show more