summaryrefslogtreecommitdiff
path: root/zephyr
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr')
-rw-r--r--zephyr/Makefile39
-rw-r--r--zephyr/Makefile.zephyr5
-rw-r--r--zephyr/README.md46
-rw-r--r--zephyr/help.c20
-rw-r--r--zephyr/machine_pin.c43
-rw-r--r--zephyr/main.c51
-rwxr-xr-xzephyr/make-minimal16
-rw-r--r--zephyr/makeprj.py29
-rw-r--r--zephyr/modmachine.c8
-rw-r--r--zephyr/modusocket.c638
-rw-r--r--zephyr/modutime.c2
-rw-r--r--zephyr/modzephyr.c51
-rw-r--r--zephyr/mpconfigport.h36
-rw-r--r--zephyr/mpconfigport_minimal.h5
-rw-r--r--zephyr/prj.mdef5
-rw-r--r--zephyr/prj_96b_carbon.conf2
-rw-r--r--zephyr/prj_base.conf33
-rw-r--r--zephyr/prj_frdm_k64f.conf2
-rw-r--r--zephyr/prj_minimal.conf (renamed from zephyr/prj.conf)1
-rw-r--r--zephyr/prj_qemu_cortex_m3.conf3
-rw-r--r--zephyr/prj_qemu_x86.conf6
-rw-r--r--zephyr/src/zephyr_getchar.c3
22 files changed, 956 insertions, 88 deletions
diff --git a/zephyr/Makefile b/zephyr/Makefile
index 9ddf12125..9e8cb31d0 100644
--- a/zephyr/Makefile
+++ b/zephyr/Makefile
@@ -5,12 +5,10 @@
# recursively Makefile.zephyr to build complete application binary
# using Zephyr build system.
#
+# To build a "minimal" configuration, use "make-minimal" wrapper.
BOARD ?= qemu_x86
CONF_FILE = prj.conf
-# Zephyr 1.5.0
-#OUTDIR_PREFIX =
-# Zephyr 1.6.0
OUTDIR_PREFIX = $(BOARD)
# Default heap size is 16KB, which is on conservative side, to let
@@ -20,7 +18,6 @@ MICROPY_HEAP_SIZE = 16384
FROZEN_DIR = scripts
# Zephyr (generated) config files - must be defined before include below
-Z_SYSGEN_H = outdir/$(OUTDIR_PREFIX)/misc/generated/sysgen/sysgen.h
Z_EXPORTS = outdir/$(OUTDIR_PREFIX)/Makefile.export
include $(Z_EXPORTS)
@@ -36,7 +33,9 @@ INC += -I$(ZEPHYR_BASE)/net/ip/contiki/os
SRC_C = main.c \
help.c \
+ modusocket.c \
modutime.c \
+ modzephyr.c \
modmachine.c \
machine_pin.c \
uart_core.c \
@@ -44,7 +43,6 @@ SRC_C = main.c \
lib/utils/printf.c \
lib/utils/pyexec.c \
lib/utils/interrupt_char.c \
- lib/utils/pyhelp.c \
lib/mp-readline/readline.c \
$(SRC_MOD)
@@ -58,10 +56,15 @@ CFLAGS = $(KBUILD_CFLAGS) $(NOSTDINC_FLAGS) $(ZEPHYRINCLUDE) \
include ../py/mkrules.mk
-$(Z_EXPORTS):
- $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) initconfig outputexports
+$(Z_EXPORTS): $(CONF_FILE)
+ # We use single target here ($(Z_EXPORTS)) for simplicity, but actually
+ # number of things get generated here: 'initconfig' generates C header for
+ # Kconfig configuration, 'outputexports' generates make environment with CC,
+ # etc., and 'lib' generates other headers which may be included by zephyr.h,
+ # e.g. DTS-related.
+ $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) initconfig outputexports lib
-GENERIC_TARGETS = all zephyr qemu qemugdb flash debug
+GENERIC_TARGETS = all zephyr run qemu qemugdb flash debug debugserver
KCONFIG_TARGETS = \
initconfig config nconfig menuconfig xconfig gconfig \
oldconfig silentoldconfig defconfig savedefconfig \
@@ -73,21 +76,23 @@ $(GENERIC_TARGETS): $(LIBMICROPYTHON)
$(CLEAN_TARGETS): clean
$(GENERIC_TARGETS) $(KCONFIG_TARGETS) $(CLEAN_TARGETS):
- $(RM) -f outdir/$(OUTDIR_PREFIX)/zephyr.lnk
$(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) $@
-$(LIBMICROPYTHON): $(Z_SYSGEN_H)
-build/genhdr/qstr.i.last: $(Z_SYSGEN_H)
+$(LIBMICROPYTHON): | $(Z_EXPORTS)
+build/genhdr/qstr.i.last: | $(Z_EXPORTS)
-$(Z_SYSGEN_H):
- rm -f $(LIBMICROPYTHON)
- -$(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE)
-
-minimal:
- $(MAKE) BOARD=$(BOARD) CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_minimal.h>"' FROZEN_DIR=
+# If we recreate libmicropython, also cause zephyr.bin relink
+LIBMICROPYTHON_EXTRA_CMD = -$(RM) -f outdir/$(OUTDIR_PREFIX)/zephyr.lnk
# Clean Zephyr things too
clean: z_clean
z_clean:
$(MAKE) -f Makefile.zephyr BOARD=$(BOARD) clean
+
+.PHONY: prj.conf
+prj.conf: prj_base.conf
+ $(PYTHON) makeprj.py prj_base.conf prj_$(BOARD).conf $@
+
+test:
+ cd ../tests && ./run-tests --target minimal --device "execpty:make -C ../zephyr run BOARD=$(BOARD) QEMU_PTY=1"
diff --git a/zephyr/Makefile.zephyr b/zephyr/Makefile.zephyr
index ad905a4db..16f0a9452 100644
--- a/zephyr/Makefile.zephyr
+++ b/zephyr/Makefile.zephyr
@@ -18,10 +18,13 @@ KERNEL_TYPE = micro
# BOARD must be passed on command line from main Makefile
#BOARD =
CONF_FILE = prj.conf
-MDEF_FILE = prj.mdef
+QEMU_NET = 1
#export SOURCE_DIR = $(ZEPHYR_BASE)/samples/static_lib/hello_world
export LDFLAGS_zephyr += -L$(CURDIR)
export ALL_LIBS += micropython
include ${ZEPHYR_BASE}/Makefile.inc
+ifeq ($(QEMU_NET), 1)
+include ${ZEPHYR_BASE}/samples/net/common/Makefile.ipstack
+endif
diff --git a/zephyr/README.md b/zephyr/README.md
index 30f668bb4..018b4ce71 100644
--- a/zephyr/README.md
+++ b/zephyr/README.md
@@ -1,20 +1,19 @@
MicroPython port to Zephyr RTOS
===============================
-This is an initial port of MicroPython to Zephyr RTOS
+This is an work-in-progress port of MicroPython to Zephyr RTOS
(http://zephyrproject.org).
-The port integrates well with Zephyr build system, using the latest
-features which will be available in 1.6.0, and thus requires Zephyr
-master to build against. All boards supported by Zephyr (with standard
-level of feature support, like UART console) should work with
-MicroPython (but not all were tested).
+This port requires Zephyr version 1.8 or higher. All boards supported
+by Zephyr (with standard level of features support, like UART console)
+should work with MicroPython (but not all were tested).
Features supported at this time:
* REPL (interactive prompt) over Zephyr UART console.
* `utime` module for time measurements and delays.
* `machine.Pin` class for GPIO control.
+* `usocket` module for networking (IPv4/IPv6).
* "Frozen modules" support to allow to bundle Python modules together
with firmware. Including complete applications, including with
run-on-boot capability.
@@ -49,8 +48,22 @@ qemu_cortex_m3):
make qemu
+With the default configuration, networking is now enabled, so you need to
+follow instructions in https://wiki.zephyrproject.org/view/Networking-with-Qemu
+to setup host side of TAP/SLIP networking. If you get error like:
+
+ could not connect serial device to character backend 'unix:/tmp/slip.sock'
+
+it's a sign that you didn't followed instructions above. If you would like
+to just run it quickly without extra setup, see "minimal" build below.
+
For deploying/flashing a firmware on a real board, follow Zephyr
-documentation for a given board.
+documentation for a given board, including known issues for that board
+(if any). (Mind again that networking is enabled for the default build,
+so you should know if there're any special requirements in that regard,
+cf. for example QEMU networking requirements above; real hardware boards
+generally should not have any special requirements, unless there're known
+issues).
Quick example
@@ -83,13 +96,18 @@ MicroPython is committed to maintain minimal binary size for Zephyr port
below 128KB, as long as Zephyr project is committed to maintain stable
minimal size of their kernel (which they appear to be). Note that at such
size, there is no support for any Zephyr features beyond REPL over UART,
-and only very minimal set of builtin Python modules. Thus, this build
-is more suitable for code size control and quick demonstrations even on
-smaller systems. It's also suitable for careful enabling of features one
-by one to achieve needed functionality and code size. This is in contrast
-to the "default" build, which may get more and more features enabled by
-default over time.
+and only very minimal set of builtin Python modules is available. Thus,
+this build is more suitable for code size control and quick demonstrations
+on smaller systems. It's also suitable for careful enabling of features
+one by one to achieve needed functionality and code size. This is in the
+contrast to the "default" build, which may get more and more features
+enabled over time.
To make a minimal build:
- make BOARD=<board> minimal
+ ./make-minimal BOARD=<board>
+
+To run a minimal build in QEMU without requiring TAP networking setup
+run the following after you built image with the previous command:
+
+ ./make-minimal BOARD=<qemu_x86|qemu_cortex_m3> qemu
diff --git a/zephyr/help.c b/zephyr/help.c
index e574adf47..0c7f27940 100644
--- a/zephyr/help.c
+++ b/zephyr/help.c
@@ -24,11 +24,9 @@
* THE SOFTWARE.
*/
-#include <stdio.h>
+#include "py/builtin.h"
-#include "lib/utils/pyhelp.h"
-
-STATIC const char *help_text =
+const char *zephyr_help_text =
"Welcome to MicroPython!\n"
"\n"
"Control commands:\n"
@@ -40,17 +38,3 @@ STATIC const char *help_text =
"\n"
"For further help on a specific object, type help(obj)\n"
;
-
-STATIC mp_obj_t builtin_help(uint n_args, const mp_obj_t *args) {
- if (n_args == 0) {
- // print a general help message
- printf("%s", help_text);
-
- } else {
- // try to print something sensible about the given object
- pyhelp_print_obj(args[0]);
- }
-
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj, 0, 1, builtin_help);
diff --git a/zephyr/machine_pin.c b/zephyr/machine_pin.c
index 049255e9f..38971399c 100644
--- a/zephyr/machine_pin.c
+++ b/zephyr/machine_pin.c
@@ -81,7 +81,7 @@ STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, mp_uint_t n
}
// constructor(drv_name, pin, ...)
-STATIC mp_obj_t machine_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
// get the wanted port
@@ -117,7 +117,7 @@ STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
mp_arg_check_num(n_args, n_kw, 0, 1, false);
machine_pin_obj_t *self = self_in;
if (n_args == 0) {
- uint32_t pin_val;
+ u32_t pin_val;
(void)gpio_pin_read(self->port, self->pin, &pin_val);
return MP_OBJ_NEW_SMALL_INT(pin_val);
} else {
@@ -138,28 +138,44 @@ STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
-// pin.low()
-STATIC mp_obj_t machine_pin_low(mp_obj_t self_in) {
+STATIC mp_obj_t machine_pin_off(mp_obj_t self_in) {
machine_pin_obj_t *self = self_in;
(void)gpio_pin_write(self->port, self->pin, 0);
return mp_const_none;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_off_obj, machine_pin_off);
-// pin.high()
-STATIC mp_obj_t machine_pin_high(mp_obj_t self_in) {
+STATIC mp_obj_t machine_pin_on(mp_obj_t self_in) {
machine_pin_obj_t *self = self_in;
(void)gpio_pin_write(self->port, self->pin, 1);
return mp_const_none;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_on_obj, machine_pin_on);
+
+STATIC mp_uint_t machine_pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+ (void)errcode;
+ machine_pin_obj_t *self = self_in;
+
+ switch (request) {
+ case MP_PIN_READ: {
+ u32_t pin_val;
+ gpio_pin_read(self->port, self->pin, &pin_val);
+ return pin_val;
+ }
+ case MP_PIN_WRITE: {
+ gpio_pin_write(self->port, self->pin, arg);
+ return 0;
+ }
+ }
+ return -1;
+}
STATIC const mp_map_elem_t machine_pin_locals_dict_table[] = {
// instance methods
{ MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&machine_pin_init_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_value), (mp_obj_t)&machine_pin_value_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_low), (mp_obj_t)&machine_pin_low_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_high), (mp_obj_t)&machine_pin_high_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_off), (mp_obj_t)&machine_pin_off_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_on), (mp_obj_t)&machine_pin_on_obj },
// class constants
{ MP_OBJ_NEW_QSTR(MP_QSTR_IN), MP_OBJ_NEW_SMALL_INT(GPIO_DIR_IN) },
@@ -170,11 +186,16 @@ STATIC const mp_map_elem_t machine_pin_locals_dict_table[] = {
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
+STATIC const mp_pin_p_t machine_pin_pin_p = {
+ .ioctl = machine_pin_ioctl,
+};
+
const mp_obj_type_t machine_pin_type = {
{ &mp_type_type },
.name = MP_QSTR_Pin,
.print = machine_pin_print,
- .make_new = machine_pin_make_new,
+ .make_new = mp_pin_make_new,
.call = machine_pin_call,
+ .protocol = &machine_pin_pin_p,
.locals_dict = (mp_obj_t)&machine_pin_locals_dict,
};
diff --git a/zephyr/main.c b/zephyr/main.c
index 2c7f3fc47..a4301629f 100644
--- a/zephyr/main.c
+++ b/zephyr/main.c
@@ -28,6 +28,11 @@
#include <stdio.h>
#include <string.h>
+#include <zephyr.h>
+#ifdef CONFIG_NETWORKING
+#include <net/net_context.h>
+#endif
+
#include "py/nlr.h"
#include "py/compile.h"
#include "py/runtime.h"
@@ -38,14 +43,9 @@
#include "lib/mp-readline/readline.h"
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
- mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
- if (lex == NULL) {
- printf("MemoryError: lexer could not allocate memory\n");
- return;
- }
-
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
+ mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
qstr source_name = lex->source_name;
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true);
@@ -60,12 +60,38 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
static char *stack_top;
static char heap[MICROPY_HEAP_SIZE];
+void init_zephyr(void) {
+ // TODO: Make addresses configurable
+ #ifdef CONFIG_NETWORKING
+ if (net_if_get_default() == NULL) {
+ // If there's no default networking interface,
+ // there's nothing to configure.
+ return;
+ }
+ #endif
+ #ifdef CONFIG_NET_IPV4
+ static struct in_addr in4addr_my = {{{192, 0, 2, 1}}};
+ net_if_ipv4_addr_add(net_if_get_default(), &in4addr_my, NET_ADDR_MANUAL, 0);
+ static struct in_addr in4netmask_my = {{{255, 255, 255, 0}}};
+ net_if_ipv4_set_netmask(net_if_get_default(), &in4netmask_my);
+ static struct in_addr in4gw_my = {{{192, 0, 2, 2}}};
+ net_if_ipv4_set_gw(net_if_get_default(), &in4gw_my);
+ #endif
+ #ifdef CONFIG_NET_IPV6
+ // 2001:db8::1
+ static struct in6_addr in6addr_my = {{{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}};
+ net_if_ipv6_addr_add(net_if_get_default(), &in6addr_my, NET_ADDR_MANUAL, 0);
+ #endif
+}
+
int real_main(void) {
int stack_dummy;
stack_top = (char*)&stack_dummy;
mp_stack_set_top(stack_top);
- // Should be set to stack size in prj.mdef minus fuzz factor
- mp_stack_set_limit(3584);
+ // Make MicroPython's stack limit somewhat smaller than full stack available
+ mp_stack_set_limit(CONFIG_MAIN_STACK_SIZE - 512);
+
+ init_zephyr();
soft_reset:
#if MICROPY_ENABLE_GC
@@ -105,11 +131,11 @@ void gc_collect(void) {
gc_collect_start();
gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
gc_collect_end();
- gc_dump_info();
+ //gc_dump_info();
}
mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
- return NULL;
+ mp_raise_OSError(ENOENT);
}
mp_import_stat_t mp_import_stat(const char *path) {
@@ -121,10 +147,7 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs)
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
-void nlr_jump_fail(void *val) {
-}
-
-void NORETURN __fatal_error(const char *msg) {
+NORETURN void nlr_jump_fail(void *val) {
while (1);
}
diff --git a/zephyr/make-minimal b/zephyr/make-minimal
new file mode 100755
index 000000000..1fc143e4d
--- /dev/null
+++ b/zephyr/make-minimal
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# This is a wrapper for make to build a "minimal" Zephyr port.
+# It should be run just like make (i.e. extra vars can be passed on the
+# command line, etc.), e.g.:
+#
+# ./make-minimal BOARD=qemu_cortex_m3
+# ./make-minimal BOARD=qemu_cortex_m3 run
+#
+
+make \
+ CONF_FILE=prj_minimal.conf \
+ CFLAGS_EXTRA='-DMP_CONFIGFILE="<mpconfigport_minimal.h>"' \
+ FROZEN_DIR= \
+ QEMU_NET=0 \
+ "$@"
diff --git a/zephyr/makeprj.py b/zephyr/makeprj.py
new file mode 100644
index 000000000..239c877cd
--- /dev/null
+++ b/zephyr/makeprj.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+import sys
+import os
+import hashlib
+
+
+def hash_file(fname):
+ if not os.path.exists(fname):
+ return b""
+ hasher = hashlib.md5()
+ with open(fname, "rb") as f:
+ hasher.update(f.read())
+ return hasher.digest()
+
+
+old_digest = hash_file(sys.argv[3])
+
+with open(sys.argv[3] + ".tmp", "wb") as f:
+ f.write(open(sys.argv[1], "rb").read())
+ if os.path.exists(sys.argv[2]):
+ f.write(open(sys.argv[2], "rb").read())
+
+new_digest = hash_file(sys.argv[3] + ".tmp")
+
+if new_digest != old_digest:
+ print("Replacing")
+ os.rename(sys.argv[3] + ".tmp", sys.argv[3])
+else:
+ os.remove(sys.argv[3] + ".tmp")
diff --git a/zephyr/modmachine.c b/zephyr/modmachine.c
index 6194a95da..5909c37d6 100644
--- a/zephyr/modmachine.c
+++ b/zephyr/modmachine.c
@@ -28,10 +28,12 @@
#include <stdint.h>
#include <stdio.h>
+#include <misc/reboot.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "extmod/machine_mem.h"
+#include "extmod/machine_signal.h"
#include "extmod/machine_pulse.h"
#include "extmod/machine_i2c.h"
#include "modmachine.h"
@@ -39,7 +41,8 @@
#if MICROPY_PY_MACHINE
STATIC mp_obj_t machine_reset(void) {
- printf("Warning: %s is not implemented\n", __func__);
+ sys_reboot(SYS_REBOOT_COLD);
+ // Won't get here, Zephyr has infiniloop on its side
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
@@ -52,10 +55,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause);
STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
+ #ifdef CONFIG_REBOOT
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
+ #endif
{ MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) },
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
+ { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) },
// reset causes
/*{ MP_ROM_QSTR(MP_QSTR_PWRON_RESET), MP_ROM_INT(REASON_DEFAULT_RST) },*/
diff --git a/zephyr/modusocket.c b/zephyr/modusocket.c
new file mode 100644
index 000000000..cec0eec7c
--- /dev/null
+++ b/zephyr/modusocket.c
@@ -0,0 +1,638 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Linaro Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#ifdef MICROPY_PY_USOCKET
+
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#include <stdio.h>
+#include <zephyr.h>
+// Zephyr's generated version header
+#include <version.h>
+#include <net/net_context.h>
+#include <net/net_pkt.h>
+#include <net/dns_resolve.h>
+
+#define DEBUG_PRINT 0
+#if DEBUG_PRINT // print debugging info
+#define DEBUG_printf printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+typedef struct _socket_obj_t {
+ mp_obj_base_t base;
+ struct net_context *ctx;
+ union {
+ struct k_fifo recv_q;
+ struct k_fifo accept_q;
+ };
+
+ #define STATE_NEW 0
+ #define STATE_CONNECTING 1
+ #define STATE_CONNECTED 2
+ #define STATE_PEER_CLOSED 3
+ int8_t state;
+} socket_obj_t;
+
+STATIC const mp_obj_type_t socket_type;
+
+// k_fifo extended API
+
+static inline void *_k_fifo_peek_head(struct k_fifo *fifo)
+{
+#if KERNEL_VERSION_NUMBER < 0x010763 /* 1.7.99 */
+ return sys_slist_peek_head(&fifo->data_q);
+#else
+ return sys_slist_peek_head(&fifo->_queue.data_q);
+#endif
+}
+
+static inline void *_k_fifo_peek_tail(struct k_fifo *fifo)
+{
+#if KERNEL_VERSION_NUMBER < 0x010763 /* 1.7.99 */
+ return sys_slist_peek_tail(&fifo->data_q);
+#else
+ return sys_slist_peek_tail(&fifo->_queue.data_q);
+#endif
+}
+
+static inline void _k_fifo_wait_non_empty(struct k_fifo *fifo, int32_t timeout)
+{
+ struct k_poll_event events[] = {
+ K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY, fifo),
+ };
+
+ k_poll(events, MP_ARRAY_SIZE(events), timeout);
+ DEBUG_printf("poll res: %d\n", events[0].state);
+}
+
+// Helper functions
+
+#define RAISE_ERRNO(x) { int _err = x; if (_err < 0) mp_raise_OSError(-_err); }
+
+STATIC void socket_check_closed(socket_obj_t *socket) {
+ if (socket->ctx == NULL) {
+ // already closed
+ mp_raise_OSError(EBADF);
+ }
+}
+
+STATIC void parse_inet_addr(socket_obj_t *socket, mp_obj_t addr_in, struct sockaddr *sockaddr) {
+ // We employ the fact that port and address offsets are the same for IPv4 & IPv6
+ struct sockaddr_in *sockaddr_in = (struct sockaddr_in*)sockaddr;
+
+ mp_obj_t *addr_items;
+ mp_obj_get_array_fixed_n(addr_in, 2, &addr_items);
+ sockaddr_in->sin_family = net_context_get_family(socket->ctx);
+ RAISE_ERRNO(net_addr_pton(sockaddr_in->sin_family, mp_obj_str_get_str(addr_items[0]), &sockaddr_in->sin_addr));
+ sockaddr_in->sin_port = htons(mp_obj_get_int(addr_items[1]));
+}
+
+STATIC mp_obj_t format_inet_addr(struct sockaddr *addr, mp_obj_t port) {
+ // We employ the fact that port and address offsets are the same for IPv4 & IPv6
+ struct sockaddr_in6 *sockaddr_in6 = (struct sockaddr_in6*)addr;
+ char buf[40];
+ net_addr_ntop(addr->family, &sockaddr_in6->sin6_addr, buf, sizeof(buf));
+ mp_obj_tuple_t *tuple = mp_obj_new_tuple(addr->family == AF_INET ? 2 : 4, NULL);
+
+ tuple->items[0] = mp_obj_new_str(buf, strlen(buf), false);
+ // We employ the fact that port offset is the same for IPv4 & IPv6
+ // not filled in
+ //tuple->items[1] = mp_obj_new_int(ntohs(((struct sockaddr_in*)addr)->sin_port));
+ tuple->items[1] = port;
+
+ if (addr->family == AF_INET6) {
+ tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); // flow_info
+ tuple->items[3] = MP_OBJ_NEW_SMALL_INT(sockaddr_in6->sin6_scope_id);
+ }
+
+ return MP_OBJ_FROM_PTR(tuple);
+}
+
+// Copy data from Zephyr net_buf chain into linear buffer.
+// We don't use net_pkt_read(), because it's weird (e.g., we'd like to
+// free processed data fragment ASAP, while net_pkt_read() holds onto
+// the whole fragment chain to do its deeds, and that's minor comparing
+// to the fact that it copies data byte by byte).
+static char *net_pkt_gather(struct net_pkt *pkt, char *to, unsigned max_len) {
+ struct net_buf *tmp = pkt->frags;
+
+ while (tmp && max_len) {
+ unsigned len = tmp->len;
+ if (len > max_len) {
+ len = max_len;
+ }
+ memcpy(to, tmp->data, len);
+ to += len;
+ max_len -= len;
+ tmp = net_pkt_frag_del(pkt, NULL, tmp);
+ }
+
+ return to;
+}
+
+// Callback for incoming packets.
+static void sock_received_cb(struct net_context *context, struct net_pkt *pkt, int status, void *user_data) {
+ socket_obj_t *socket = (socket_obj_t*)user_data;
+ DEBUG_printf("recv cb: context: %p, status: %d, pkt: %p", context, status, pkt);
+ if (pkt) {
+ DEBUG_printf(" (appdatalen=%d), token: %p", pkt->appdatalen, net_pkt_token(pkt));
+ }
+ DEBUG_printf("\n");
+ #if DEBUG_PRINT > 1
+ net_pkt_print_frags(pkt);
+ #endif
+
+ // if net_buf == NULL, EOF
+ if (pkt == NULL) {
+ struct net_pkt *last_pkt = _k_fifo_peek_tail(&socket->recv_q);
+ if (last_pkt == NULL) {
+ socket->state = STATE_PEER_CLOSED;
+ k_fifo_cancel_wait(&socket->recv_q);
+ DEBUG_printf("Marked socket %p as peer-closed\n", socket);
+ } else {
+ // We abuse "buf_sent" flag to store EOF flag
+ net_pkt_set_sent(last_pkt, true);
+ DEBUG_printf("Set EOF flag on %p\n", last_pkt);
+ }
+ return;
+ }
+
+ // Make sure that "EOF flag" is not set
+ net_pkt_set_sent(pkt, false);
+
+ // We don't care about packet header, so get rid of it asap
+ unsigned header_len = net_pkt_appdata(pkt) - pkt->frags->data;
+ net_buf_pull(pkt->frags, header_len);
+
+ k_fifo_put(&socket->recv_q, pkt);
+}
+
+// Callback for incoming connections.
+static void sock_accepted_cb(struct net_context *new_ctx, struct sockaddr *addr, socklen_t addrlen, int status, void *user_data) {
+ socket_obj_t *socket = (socket_obj_t*)user_data;
+ DEBUG_printf("accept cb: context: %p, status: %d, new ctx: %p\n", socket->ctx, status, new_ctx);
+ DEBUG_printf("new_ctx ref_cnt: %d\n", new_ctx->refcount);
+
+ k_fifo_put(&socket->accept_q, new_ctx);
+}
+
+socket_obj_t *socket_new(void) {
+ socket_obj_t *socket = m_new_obj_with_finaliser(socket_obj_t);
+ socket->base.type = (mp_obj_t)&socket_type;
+ k_fifo_init(&socket->recv_q);
+ socket->state = STATE_NEW;
+ return socket;
+}
+
+// Methods
+
+STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+ socket_obj_t *self = self_in;
+ if (self->ctx == NULL) {
+ mp_printf(print, "<socket NULL>");
+ } else {
+ struct net_context *ctx = self->ctx;
+ mp_printf(print, "<socket %p type=%d>", ctx, net_context_get_type(ctx));
+ }
+}
+
+STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 0, 4, false);
+
+ socket_obj_t *socket = socket_new();
+
+ int family = AF_INET;
+ int socktype = SOCK_STREAM;
+ int proto = -1;
+
+ if (n_args >= 1) {
+ family = mp_obj_get_int(args[0]);
+ if (n_args >= 2) {
+ socktype = mp_obj_get_int(args[1]);
+ if (n_args >= 3) {
+ proto = mp_obj_get_int(args[2]);
+ }
+ }
+ }
+
+ if (proto == -1) {
+ proto = IPPROTO_TCP;
+ if (socktype != SOCK_STREAM) {
+ proto = IPPROTO_UDP;
+ }
+ }
+
+ RAISE_ERRNO(net_context_get(family, socktype, proto, &socket->ctx));
+
+ return MP_OBJ_FROM_PTR(socket);
+}
+
+STATIC mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
+ socket_obj_t *socket = self_in;
+ socket_check_closed(socket);
+
+ struct sockaddr sockaddr;
+ parse_inet_addr(socket, addr_in, &sockaddr);
+
+ RAISE_ERRNO(net_context_bind(socket->ctx, &sockaddr, sizeof(sockaddr)));
+ // For DGRAM socket, we expect to receive packets after call to bind(),
+ // but for STREAM socket, next expected operation is listen(), which
+ // doesn't work if recv callback is set.
+ if (net_context_get_type(socket->ctx) == SOCK_DGRAM) {
+ DEBUG_printf("Setting recv cb after bind\n");
+ RAISE_ERRNO(net_context_recv(socket->ctx, sock_received_cb, K_NO_WAIT, socket));
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind);
+
+STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
+ socket_obj_t *socket = self_in;
+ socket_check_closed(socket);
+
+ struct sockaddr sockaddr;
+ parse_inet_addr(socket, addr_in, &sockaddr);
+
+ RAISE_ERRNO(net_context_connect(socket->ctx, &sockaddr, sizeof(sockaddr), NULL, K_FOREVER, NULL));
+ DEBUG_printf("Setting recv cb after connect()\n");
+ RAISE_ERRNO(net_context_recv(socket->ctx, sock_received_cb, K_NO_WAIT, socket));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect);
+
+STATIC mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog_in) {
+ socket_obj_t *socket = self_in;
+ socket_check_closed(socket);
+
+ mp_int_t backlog = mp_obj_get_int(backlog_in);
+ RAISE_ERRNO(net_context_listen(socket->ctx, backlog));
+ RAISE_ERRNO(net_context_accept(socket->ctx, sock_accepted_cb, K_NO_WAIT, socket));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen);
+
+STATIC mp_obj_t socket_accept(mp_obj_t self_in) {
+ socket_obj_t *socket = self_in;
+ socket_check_closed(socket);
+
+ struct net_context *ctx = k_fifo_get(&socket->accept_q, K_FOREVER);
+ // Was overwritten by fifo
+ ctx->refcount = 1;
+
+ socket_obj_t *socket2 = socket_new();
+ socket2->ctx = ctx;
+ DEBUG_printf("Setting recv cb after accept()\n");
+ RAISE_ERRNO(net_context_recv(ctx, sock_received_cb, K_NO_WAIT, socket2));
+
+ mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL);
+ client->items[0] = MP_OBJ_FROM_PTR(socket2);
+ // TODO
+ client->items[1] = mp_const_none;
+
+ return MP_OBJ_FROM_PTR(client);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept);
+
+STATIC mp_uint_t sock_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+ socket_obj_t *socket = self_in;
+ if (socket->ctx == NULL) {
+ // already closed
+ *errcode = EBADF;
+ return MP_STREAM_ERROR;
+ }
+
+ struct net_pkt *send_pkt = net_pkt_get_tx(socket->ctx, K_FOREVER);
+
+ unsigned len = net_if_get_mtu(net_context_get_iface(socket->ctx));
+ // Arbitrary value to account for protocol headers
+ len -= 64;
+ if (len > size) {
+ len = size;
+ }
+
+ // TODO: Return value of 0 is a hard case (as we wait forever, should
+ // not happen).
+ len = net_pkt_append(send_pkt, len, buf, K_FOREVER);
+
+ int err = net_context_send(send_pkt, /*cb*/NULL, K_FOREVER, NULL, NULL);
+ if (err < 0) {
+ *errcode = -err;
+ return MP_STREAM_ERROR;
+ }
+
+ return len;
+}
+
+STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+ int err = 0;
+ mp_uint_t len = sock_write(self_in, bufinfo.buf, bufinfo.len, &err);
+ if (len == MP_STREAM_ERROR) {
+ mp_raise_OSError(err);
+ }
+ return mp_obj_new_int_from_uint(len);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send);
+
+STATIC mp_uint_t sock_read(mp_obj_t self_in, void *buf, mp_uint_t max_len, int *errcode) {
+ socket_obj_t *socket = self_in;
+ if (socket->ctx == NULL) {
+ // already closed
+ *errcode = EBADF;
+ return MP_STREAM_ERROR;
+ }
+
+ enum net_sock_type sock_type = net_context_get_type(socket->ctx);
+ unsigned recv_len;
+
+ if (sock_type == SOCK_DGRAM) {
+
+ struct net_pkt *pkt = k_fifo_get(&socket->recv_q, K_FOREVER);
+
+ recv_len = net_pkt_appdatalen(pkt);
+ DEBUG_printf("recv: pkt=%p, appdatalen: %d\n", pkt, recv_len);
+
+ if (recv_len > max_len) {
+ recv_len = max_len;
+ }
+
+ net_pkt_gather(pkt, buf, recv_len);
+ net_pkt_unref(pkt);
+
+ } else if (sock_type == SOCK_STREAM) {
+
+ do {
+
+ if (socket->state == STATE_PEER_CLOSED) {
+ return 0;
+ }
+
+ _k_fifo_wait_non_empty(&socket->recv_q, K_FOREVER);
+ struct net_pkt *pkt = _k_fifo_peek_head(&socket->recv_q);
+ if (pkt == NULL) {
+ DEBUG_printf("TCP recv: NULL return from fifo\n");
+ continue;
+ }
+
+ DEBUG_printf("TCP recv: cur_pkt: %p\n", pkt);
+
+ struct net_buf *frag = pkt->frags;
+ if (frag == NULL) {
+ printf("net_pkt has empty fragments on start!\n");
+ assert(0);
+ }
+
+ unsigned frag_len = frag->len;
+ recv_len = frag_len;
+ if (recv_len > max_len) {
+ recv_len = max_len;
+ }
+ DEBUG_printf("%d data bytes in head frag, going to read %d\n", frag_len, recv_len);
+
+ memcpy(buf, frag->data, recv_len);
+
+ if (recv_len != frag_len) {
+ net_buf_pull(frag, recv_len);
+ } else {
+ frag = net_pkt_frag_del(pkt, NULL, frag);
+ if (frag == NULL) {
+ DEBUG_printf("Finished processing pkt %p\n", pkt);
+ // Drop head packet from queue
+ k_fifo_get(&socket->recv_q, K_NO_WAIT);
+
+ // If "sent" flag was set, it's last packet and we reached EOF
+ if (net_pkt_sent(pkt)) {
+ socket->state = STATE_PEER_CLOSED;
+ }
+ net_pkt_unref(pkt);
+ }
+ }
+ // Keep repeating while we're getting empty fragments
+ // Zephyr IP stack appears to have fed empty net_buf's with empty
+ // frags for various TCP control packets - in previous versions.
+ } while (recv_len == 0);
+ }
+
+ return recv_len;
+}
+
+STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
+ mp_int_t max_len = mp_obj_get_int(len_in);
+ vstr_t vstr;
+ // +1 to accommodate for trailing \0
+ vstr_init_len(&vstr, max_len + 1);
+
+ int err;
+ mp_uint_t len = sock_read(self_in, vstr.buf, max_len, &err);
+
+ if (len == MP_STREAM_ERROR) {
+ vstr_clear(&vstr);
+ mp_raise_OSError(err);
+ }
+
+ if (len == 0) {
+ vstr_clear(&vstr);
+ return mp_const_empty_bytes;
+ }
+
+ vstr.len = len;
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
+
+STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
+ (void)n_args; // always 4
+ mp_warning("setsockopt() not implemented");
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt);
+
+STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
+ (void)n_args;
+ return args[0];
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile);
+
+STATIC mp_obj_t socket_close(mp_obj_t self_in) {
+ socket_obj_t *socket = self_in;
+ if (socket->ctx != NULL) {
+ RAISE_ERRNO(net_context_put(socket->ctx));
+ socket->ctx = NULL;
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close);
+
+STATIC const mp_map_elem_t socket_locals_dict_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&socket_close_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&socket_close_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&socket_bind_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&socket_connect_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&socket_listen_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj},
+ { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&socket_makefile_obj },
+};
+STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table);
+
+STATIC const mp_stream_p_t socket_stream_p = {
+ .read = sock_read,
+ .write = sock_write,
+ //.ioctl = sock_ioctl,
+};
+
+STATIC const mp_obj_type_t socket_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_socket,
+ .print = socket_print,
+ .make_new = socket_make_new,
+ .protocol = &socket_stream_p,
+ .locals_dict = (mp_obj_t)&socket_locals_dict,
+};
+
+//
+// getaddrinfo() implementation
+//
+
+typedef struct _getaddrinfo_state_t {
+ mp_obj_t result;
+ struct k_sem sem;
+ mp_obj_t port;
+ int status;
+} getaddrinfo_state_t;
+
+void dns_resolve_cb(enum dns_resolve_status status, struct dns_addrinfo *info, void *user_data) {
+ getaddrinfo_state_t *state = user_data;
+ DEBUG_printf("dns status: %d\n", status);
+
+ if (info == NULL) {
+ if (status == DNS_EAI_ALLDONE) {
+ status = 0;
+ }
+ state->status = status;
+ k_sem_give(&state->sem);
+ return;
+ }
+
+ mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL);
+ tuple->items[0] = MP_OBJ_NEW_SMALL_INT(info->ai_family);
+ // info->ai_socktype not filled
+ tuple->items[1] = MP_OBJ_NEW_SMALL_INT(SOCK_STREAM);
+ // info->ai_protocol not filled
+ tuple->items[2] = MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP);
+ tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
+ tuple->items[4] = format_inet_addr(&info->ai_addr, state->port);
+ mp_obj_list_append(state->result, MP_OBJ_FROM_PTR(tuple));
+}
+
+STATIC mp_obj_t mod_getaddrinfo(size_t n_args, const mp_obj_t *args) {
+ mp_obj_t host_in = args[0], port_in = args[1];
+ const char *host = mp_obj_str_get_str(host_in);
+ mp_int_t family = 0;
+ if (n_args > 2) {
+ family = mp_obj_get_int(args[2]);
+ }
+
+ getaddrinfo_state_t state;
+ // Just validate that it's int
+ (void)mp_obj_get_int(port_in);
+ state.port = port_in;
+ state.result = mp_obj_new_list(0, NULL);
+ k_sem_init(&state.sem, 0, UINT_MAX);
+
+ for (int i = 2; i--;) {
+ int type = (family != AF_INET6 ? DNS_QUERY_TYPE_A : DNS_QUERY_TYPE_AAAA);
+ RAISE_ERRNO(dns_get_addr_info(host, type, NULL, dns_resolve_cb, &state, 3000));
+ k_sem_take(&state.sem, K_FOREVER);
+ if (family != 0) {
+ break;
+ }
+ family = AF_INET6;
+ }
+
+ // Raise error only if there's nothing to return, otherwise
+ // it may be IPv4 vs IPv6 differences.
+ mp_int_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(state.result));
+ if (state.status != 0 && len == 0) {
+ mp_raise_OSError(state.status);
+ }
+
+ return state.result;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_getaddrinfo_obj, 2, 3, mod_getaddrinfo);
+
+
+STATIC mp_obj_t pkt_get_info(void) {
+ struct k_mem_slab *rx, *tx;
+ struct net_buf_pool *rx_data, *tx_data;
+ net_pkt_get_info(&rx, &tx, &rx_data, &tx_data);
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(k_mem_slab_num_free_get(rx));
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(k_mem_slab_num_free_get(tx));
+ t->items[2] = MP_OBJ_NEW_SMALL_INT(rx_data->avail_count);
+ t->items[3] = MP_OBJ_NEW_SMALL_INT(tx_data->avail_count);
+ return MP_OBJ_FROM_PTR(t);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pkt_get_info_obj, pkt_get_info);
+
+STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) },
+ // objects
+ { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&socket_type },
+ // class constants
+ { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET), MP_OBJ_NEW_SMALL_INT(AF_INET) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET6), MP_OBJ_NEW_SMALL_INT(AF_INET6) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(1) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(2) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&mod_getaddrinfo_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_pkt_get_info), (mp_obj_t)&pkt_get_info_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_usocket_globals, mp_module_usocket_globals_table);
+
+const mp_obj_module_t mp_module_usocket = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_usocket_globals,
+};
+
+#endif // MICROPY_PY_USOCKET
diff --git a/zephyr/modutime.c b/zephyr/modutime.c
index 378068bb3..0c268046a 100644
--- a/zephyr/modutime.c
+++ b/zephyr/modutime.c
@@ -36,7 +36,7 @@
#include "extmod/utime_mphal.h"
STATIC mp_obj_t mod_time_time(void) {
- /* The absense of FP support is deliberate. The Zephyr port uses
+ /* The absence of FP support is deliberate. The Zephyr port uses
* single precision floats so the fraction component will start to
* lose precision on devices with a long uptime.
*/
diff --git a/zephyr/modzephyr.c b/zephyr/modzephyr.c
new file mode 100644
index 000000000..4bac5c970
--- /dev/null
+++ b/zephyr/modzephyr.c
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Linaro Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_ZEPHYR
+
+#include <zephyr.h>
+
+#include "py/runtime.h"
+
+STATIC mp_obj_t mod_is_preempt_thread(void) {
+ return mp_obj_new_bool(k_is_preempt_thread());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_is_preempt_thread_obj, mod_is_preempt_thread);
+
+STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr) },
+ { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table);
+
+const mp_obj_module_t mp_module_zephyr = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_time_globals,
+};
+
+#endif // MICROPY_PY_ZEPHYR
diff --git a/zephyr/mpconfigport.h b/zephyr/mpconfigport.h
index 26fb1410f..2f2526779 100644
--- a/zephyr/mpconfigport.h
+++ b/zephyr/mpconfigport.h
@@ -27,12 +27,15 @@
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
#include "autoconf.h"
+// Included here to get basic Zephyr environment (macros, etc.)
+#include <zephyr.h>
// Usually passed from Makefile
#ifndef MICROPY_HEAP_SIZE
#define MICROPY_HEAP_SIZE (16 * 1024)
#endif
+#define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_STACK_CHECK (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_HELPER_REPL (1)
@@ -48,16 +51,25 @@
#define MICROPY_PY_BUILTINS_RANGE_ATTRS (0)
#define MICROPY_PY_BUILTINS_REVERSED (0)
#define MICROPY_PY_BUILTINS_SET (0)
+#define MICROPY_PY_BUILTINS_HELP (1)
+#define MICROPY_PY_BUILTINS_HELP_TEXT zephyr_help_text
#define MICROPY_PY_ARRAY (0)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_CMATH (0)
#define MICROPY_PY_IO (0)
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
#define MICROPY_PY_MACHINE (1)
+#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
#define MICROPY_MODULE_WEAK_LINKS (1)
#define MICROPY_PY_STRUCT (0)
+#ifdef CONFIG_NETWORKING
+// If we have networking, we likely want errno comfort
+#define MICROPY_PY_UERRNO (1)
+#define MICROPY_PY_USOCKET (1)
+#endif
#define MICROPY_PY_UTIME (1)
#define MICROPY_PY_UTIME_MP_HAL (1)
+#define MICROPY_PY_ZEPHYR (1)
#define MICROPY_PY_SYS_MODULES (0)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
@@ -68,6 +80,8 @@
#define MICROPY_COMP_CONST (0)
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0)
+#define MICROPY_PY_SYS_PLATFORM "zephyr"
+
#ifdef CONFIG_BOARD
#define MICROPY_HW_BOARD_NAME "zephyr-" CONFIG_BOARD
#else
@@ -89,8 +103,6 @@ typedef void *machine_ptr_t; // must be of pointer size
typedef const void *machine_const_ptr_t; // must be of pointer size
typedef long mp_off_t;
-#define BYTES_PER_WORD (sizeof(mp_int_t))
-
#define MP_STATE_PORT MP_STATE_VM
#define MICROPY_PORT_ROOT_POINTERS \
@@ -98,6 +110,16 @@ typedef long mp_off_t;
extern const struct _mp_obj_module_t mp_module_machine;
extern const struct _mp_obj_module_t mp_module_time;
+extern const struct _mp_obj_module_t mp_module_usocket;
+extern const struct _mp_obj_module_t mp_module_zephyr;
+
+#if MICROPY_PY_USOCKET
+#define MICROPY_PY_USOCKET_DEF { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_usocket) },
+#define MICROPY_PY_USOCKET_WEAK_DEF { MP_OBJ_NEW_QSTR(MP_QSTR_socket), MP_ROM_PTR(&mp_module_usocket) },
+#else
+#define MICROPY_PY_USOCKET_DEF
+#define MICROPY_PY_USOCKET_WEAK_DEF
+#endif
#if MICROPY_PY_UTIME
#define MICROPY_PY_UTIME_DEF { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) },
@@ -105,14 +127,22 @@ extern const struct _mp_obj_module_t mp_module_time;
#define MICROPY_PY_UTIME_DEF
#endif
+#if MICROPY_PY_ZEPHYR
+#define MICROPY_PY_ZEPHYR_DEF { MP_ROM_QSTR(MP_QSTR_zephyr), MP_ROM_PTR(&mp_module_zephyr) },
+#else
+#define MICROPY_PY_ZEPHYR_DEF
+#endif
+
#define MICROPY_PORT_BUILTIN_MODULES \
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \
+ MICROPY_PY_USOCKET_DEF \
MICROPY_PY_UTIME_DEF \
+ MICROPY_PY_ZEPHYR_DEF \
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time) }, \
+ MICROPY_PY_USOCKET_WEAK_DEF \
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
- { MP_OBJ_NEW_QSTR(MP_QSTR_help), (mp_obj_t)&mp_builtin_help_obj }, \
diff --git a/zephyr/mpconfigport_minimal.h b/zephyr/mpconfigport_minimal.h
index 4ecc03afb..772335c0a 100644
--- a/zephyr/mpconfigport_minimal.h
+++ b/zephyr/mpconfigport_minimal.h
@@ -27,6 +27,8 @@
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
#include "autoconf.h"
+// Included here to get basic Zephyr environment (macros, etc.)
+#include <zephyr.h>
// Usually passed from Makefile
#ifndef MICROPY_HEAP_SIZE
@@ -37,6 +39,7 @@
#define MICROPY_ENABLE_GC (1)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_REPL_AUTO_INDENT (1)
+#define MICROPY_KBD_EXCEPTION (1)
#define MICROPY_CPYTHON_COMPAT (0)
#define MICROPY_PY_ASYNC_AWAIT (0)
#define MICROPY_PY_ATTRTUPLE (0)
@@ -82,8 +85,6 @@ typedef void *machine_ptr_t; // must be of pointer size
typedef const void *machine_const_ptr_t; // must be of pointer size
typedef long mp_off_t;
-#define BYTES_PER_WORD (sizeof(mp_int_t))
-
#define MP_STATE_PORT MP_STATE_VM
#define MICROPY_PORT_ROOT_POINTERS \
diff --git a/zephyr/prj.mdef b/zephyr/prj.mdef
deleted file mode 100644
index 5a7312f89..000000000
--- a/zephyr/prj.mdef
+++ /dev/null
@@ -1,5 +0,0 @@
-% Application : MicroPython
-
-% TASK NAME PRIO ENTRY STACK GROUPS
-% ==================================
- TASK MAIN 7 main 4096 [EXE]
diff --git a/zephyr/prj_96b_carbon.conf b/zephyr/prj_96b_carbon.conf
new file mode 100644
index 000000000..3e41e2532
--- /dev/null
+++ b/zephyr/prj_96b_carbon.conf
@@ -0,0 +1,2 @@
+# TODO: Enable networking
+CONFIG_NETWORKING=n
diff --git a/zephyr/prj_base.conf b/zephyr/prj_base.conf
new file mode 100644
index 000000000..4346f20bf
--- /dev/null
+++ b/zephyr/prj_base.conf
@@ -0,0 +1,33 @@
+CONFIG_LEGACY_KERNEL=n
+CONFIG_REBOOT=y
+
+CONFIG_STDOUT_CONSOLE=y
+CONFIG_CONSOLE_HANDLER=y
+CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y
+CONFIG_NEWLIB_LIBC=y
+CONFIG_FLOAT=y
+CONFIG_MAIN_STACK_SIZE=4096
+
+# Networking config
+CONFIG_NETWORKING=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_IPV6=y
+CONFIG_NET_UDP=y
+CONFIG_NET_TCP=y
+CONFIG_TEST_RANDOM_GENERATOR=y
+CONFIG_NET_NBUF_RX_COUNT=5
+
+# DNS
+CONFIG_DNS_RESOLVER=y
+CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES=2
+CONFIG_DNS_SERVER_IP_ADDRESSES=y
+CONFIG_DNS_SERVER1="192.0.2.2"
+
+# Required for usocket.pkt_get_info()
+CONFIG_NET_BUF_POOL_USAGE=y
+
+# Uncomment to enable "INFO" level net_buf logging
+#CONFIG_NET_LOG=y
+#CONFIG_NET_DEBUG_NET_BUF=y
+# Change to 4 for "DEBUG" level
+#CONFIG_SYS_LOG_NET_LEVEL=3
diff --git a/zephyr/prj_frdm_k64f.conf b/zephyr/prj_frdm_k64f.conf
new file mode 100644
index 000000000..611d6bc0a
--- /dev/null
+++ b/zephyr/prj_frdm_k64f.conf
@@ -0,0 +1,2 @@
+# Networking drivers
+CONFIG_NET_L2_ETHERNET=y
diff --git a/zephyr/prj.conf b/zephyr/prj_minimal.conf
index 0b0827d25..5d6b353ba 100644
--- a/zephyr/prj.conf
+++ b/zephyr/prj_minimal.conf
@@ -3,3 +3,4 @@ CONFIG_CONSOLE_HANDLER=y
CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y
CONFIG_NEWLIB_LIBC=y
CONFIG_FLOAT=y
+CONFIG_MAIN_STACK_SIZE=4096
diff --git a/zephyr/prj_qemu_cortex_m3.conf b/zephyr/prj_qemu_cortex_m3.conf
new file mode 100644
index 000000000..09614c362
--- /dev/null
+++ b/zephyr/prj_qemu_cortex_m3.conf
@@ -0,0 +1,3 @@
+# Networking drivers
+# SLIP driver for QEMU
+CONFIG_NET_SLIP_TAP=y
diff --git a/zephyr/prj_qemu_x86.conf b/zephyr/prj_qemu_x86.conf
new file mode 100644
index 000000000..ef60cfec9
--- /dev/null
+++ b/zephyr/prj_qemu_x86.conf
@@ -0,0 +1,6 @@
+# Networking drivers
+# SLIP driver for QEMU
+CONFIG_NET_SLIP_TAP=y
+
+# Default RAM easily overflows with uPy and networking
+CONFIG_RAM_SIZE=320
diff --git a/zephyr/src/zephyr_getchar.c b/zephyr/src/zephyr_getchar.c
index 95912f3df..52b3394d0 100644
--- a/zephyr/src/zephyr_getchar.c
+++ b/zephyr/src/zephyr_getchar.c
@@ -23,7 +23,7 @@
extern int mp_interrupt_char;
void mp_keyboard_interrupt(void);
-static struct nano_sem uart_sem;
+static struct k_sem uart_sem;
#define UART_BUFSIZE 256
static uint8_t uart_ringbuf[UART_BUFSIZE];
static uint8_t i_get, i_put;
@@ -44,6 +44,7 @@ static int console_irq_input_hook(uint8_t ch)
}
//printk("%x\n", ch);
k_sem_give(&uart_sem);
+ k_yield();
return 1;
}