From 4b069e173e195cc347f532895f98c36a65ea3668 Mon Sep 17 00:00:00 2001 From: "kunming.wang" Date: Wed, 9 Jul 2025 16:48:31 +0800 Subject: [PATCH] add new port quectel --- ports/quectel/Makefile | 152 ++ ports/quectel/README.md | 136 + ports/quectel/boards/EC600UCN_LB/board.json | 19 + .../boards/EC600UCN_LB/mpconfigboard.h | 1 + .../boards/EC600UCN_LB/mpconfigvariant.mk | 2 + ports/quectel/boards/EG915UEC_AC/board.json | 19 + .../boards/EG915UEC_AC/mpconfigboard.h | 1 + .../boards/EG915UEC_AC/mpconfigvariant.mk | 2 + ports/quectel/boards/deploy.md | 3 + ports/quectel/boards/manifest.py | 2 + ports/quectel/callbackdeal.c | 885 ++++++ ports/quectel/callbackdeal.h | 389 +++ ports/quectel/gccollect.c | 120 + ports/quectel/gccollect.h | 41 + ports/quectel/help.c | 44 + ports/quectel/machine_extint.c | 734 +++++ ports/quectel/machine_hw_spi.c | 265 ++ ports/quectel/machine_i2c.h | 90 + ports/quectel/machine_iic.c | 188 ++ ports/quectel/machine_pin.c | 333 +++ ports/quectel/machine_rtc.c | 228 ++ ports/quectel/machine_timer.c | 311 +++ ports/quectel/machine_uart.c | 492 ++++ ports/quectel/machine_wdt.c | 101 + ports/quectel/main.c | 312 +++ ports/quectel/misc_adc.c | 119 + ports/quectel/misc_power.c | 107 + ports/quectel/misc_pwm.c | 390 +++ ports/quectel/misc_usbnet.c | 213 ++ ports/quectel/moddatacall.c | 833 ++++++ ports/quectel/moddatacall.h | 42 + ports/quectel/moddev.c | 288 ++ ports/quectel/modexample.c | 127 + ports/quectel/modflashdev.c | 181 ++ ports/quectel/modfota.c | 446 +++ ports/quectel/modhelios.c | 57 + ports/quectel/modmachine.c | 113 + ports/quectel/modmachine.h | 53 + ports/quectel/modmisc.c | 253 ++ ports/quectel/modmisc.h | 48 + ports/quectel/modnet.c | 2423 +++++++++++++++++ ports/quectel/modostimer.c | 167 ++ ports/quectel/modsim.c | 1128 ++++++++ ports/quectel/modsim.h | 222 ++ ports/quectel/modsocket.c | 1280 +++++++++ ports/quectel/modules/_boot.py | 144 + ports/quectel/modules/app_fota.py | 33 + ports/quectel/modules/app_fota_download.py | 212 ++ ports/quectel/modules/app_fota_mount.py | 49 + ports/quectel/modules/app_fota_updater.py | 91 + ports/quectel/modules/checkNet.py | 231 ++ ports/quectel/modules/checksum.py | 157 ++ ports/quectel/modules/dataCall.py | 284 ++ ports/quectel/modules/file_crc32.py | 45 + ports/quectel/modules/log.py | 119 + ports/quectel/modules/ql_fs.py | 137 + ports/quectel/modules/queue.py | 51 + ports/quectel/modules/request.py | 470 ++++ ports/quectel/modules/umqtt.py | 776 ++++++ ports/quectel/moduos.c | 240 ++ ports/quectel/modussl_mbedtls.c | 600 ++++ ports/quectel/modutime.c | 220 ++ ports/quectel/mpconfigport.h | 241 ++ ports/quectel/mpconfigport.mk | 51 + ports/quectel/mphalport.c | 384 +++ ports/quectel/mphalport.h | 88 + ports/quectel/mpthreadport.c | 334 +++ ports/quectel/mpthreadport.h | 71 + ports/quectel/plat.mk | 9 + ports/quectel/qstrdefsport.h | 2 + ports/quectel/quectel.mk | 422 +++ ports/quectel/utime_mphal.c | 110 + ports/quectel/utime_mphal.h | 43 + 73 files changed, 18974 insertions(+) create mode 100644 ports/quectel/Makefile create mode 100644 ports/quectel/README.md create mode 100644 ports/quectel/boards/EC600UCN_LB/board.json create mode 100644 ports/quectel/boards/EC600UCN_LB/mpconfigboard.h create mode 100644 ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk create mode 100644 ports/quectel/boards/EG915UEC_AC/board.json create mode 100644 ports/quectel/boards/EG915UEC_AC/mpconfigboard.h create mode 100644 ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk create mode 100644 ports/quectel/boards/deploy.md create mode 100644 ports/quectel/boards/manifest.py create mode 100644 ports/quectel/callbackdeal.c create mode 100644 ports/quectel/callbackdeal.h create mode 100644 ports/quectel/gccollect.c create mode 100644 ports/quectel/gccollect.h create mode 100644 ports/quectel/help.c create mode 100644 ports/quectel/machine_extint.c create mode 100644 ports/quectel/machine_hw_spi.c create mode 100644 ports/quectel/machine_i2c.h create mode 100644 ports/quectel/machine_iic.c create mode 100644 ports/quectel/machine_pin.c create mode 100644 ports/quectel/machine_rtc.c create mode 100644 ports/quectel/machine_timer.c create mode 100644 ports/quectel/machine_uart.c create mode 100644 ports/quectel/machine_wdt.c create mode 100644 ports/quectel/main.c create mode 100644 ports/quectel/misc_adc.c create mode 100644 ports/quectel/misc_power.c create mode 100644 ports/quectel/misc_pwm.c create mode 100644 ports/quectel/misc_usbnet.c create mode 100644 ports/quectel/moddatacall.c create mode 100644 ports/quectel/moddatacall.h create mode 100644 ports/quectel/moddev.c create mode 100644 ports/quectel/modexample.c create mode 100644 ports/quectel/modflashdev.c create mode 100644 ports/quectel/modfota.c create mode 100644 ports/quectel/modhelios.c create mode 100644 ports/quectel/modmachine.c create mode 100644 ports/quectel/modmachine.h create mode 100644 ports/quectel/modmisc.c create mode 100644 ports/quectel/modmisc.h create mode 100644 ports/quectel/modnet.c create mode 100644 ports/quectel/modostimer.c create mode 100644 ports/quectel/modsim.c create mode 100644 ports/quectel/modsim.h create mode 100644 ports/quectel/modsocket.c create mode 100644 ports/quectel/modules/_boot.py create mode 100644 ports/quectel/modules/app_fota.py create mode 100644 ports/quectel/modules/app_fota_download.py create mode 100644 ports/quectel/modules/app_fota_mount.py create mode 100644 ports/quectel/modules/app_fota_updater.py create mode 100644 ports/quectel/modules/checkNet.py create mode 100644 ports/quectel/modules/checksum.py create mode 100644 ports/quectel/modules/dataCall.py create mode 100644 ports/quectel/modules/file_crc32.py create mode 100644 ports/quectel/modules/log.py create mode 100644 ports/quectel/modules/ql_fs.py create mode 100644 ports/quectel/modules/queue.py create mode 100644 ports/quectel/modules/request.py create mode 100644 ports/quectel/modules/umqtt.py create mode 100644 ports/quectel/moduos.c create mode 100644 ports/quectel/modussl_mbedtls.c create mode 100644 ports/quectel/modutime.c create mode 100644 ports/quectel/mpconfigport.h create mode 100644 ports/quectel/mpconfigport.mk create mode 100644 ports/quectel/mphalport.c create mode 100644 ports/quectel/mphalport.h create mode 100644 ports/quectel/mpthreadport.c create mode 100644 ports/quectel/mpthreadport.h create mode 100644 ports/quectel/plat.mk create mode 100644 ports/quectel/qstrdefsport.h create mode 100644 ports/quectel/quectel.mk create mode 100644 ports/quectel/utime_mphal.c create mode 100644 ports/quectel/utime_mphal.h diff --git a/ports/quectel/Makefile b/ports/quectel/Makefile new file mode 100644 index 0000000000000..39f6f6dbcfe1e --- /dev/null +++ b/ports/quectel/Makefile @@ -0,0 +1,152 @@ +TOP := ../.. +ROOT := $(TOP)/../.. + +include ../../py/mkenv.mk +include mpconfigport.mk +include quectel.mk + +include ../../py/verbose.mk + +# Select the board to build for: +ifdef BOARD_DIR +# Custom board path - remove trailing slash and get the final component of +# the path as the board name. +BOARD ?= $(notdir $(BOARD_DIR:/=)) +else +# If not given on the command line, then default to RPI_PICO. +BOARD ?= EC600UCN_LB +BOARD_DIR ?= boards/$(BOARD) +endif + +ifeq ($(wildcard $(BOARD_DIR)/.),) +ifeq ($(findstring boards/PICO,$(BOARD_DIR)),boards/PICO) +$(warning The PICO* boards have been renamed to RPI_PICO*) +endif +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + +# If the build directory is not given, make it reflect the board name (and +# optionally the board variant). +ifneq ($(BOARD_VARIANT),) +BUILD ?= build-$(BOARD)-$(BOARD_VARIANT) +else +BUILD ?= build-$(BOARD) +endif + +ifeq ($(BUILD_VERBOSE),1) +MAKE_ARGS += VERBOSE=1 # Picked up in Makefile generated by CMake +endif + +MAKE_ARGS += -DMICROPY_BOARD=$(BOARD) -DMICROPY_BOARD_DIR="$(abspath $(BOARD_DIR))" + +ifdef USER_C_MODULES +MAKE_ARGS += -DUSER_C_MODULES=${USER_C_MODULES} +endif + +ifneq ($(FROZEN_MANIFEST),) +MAKE_ARGS += -DMICROPY_FROZEN_MANIFEST=${FROZEN_MANIFEST} +endif + +ifeq ($(DEBUG),1) +MAKE_ARGS += -DCMAKE_BUILD_TYPE=Debug +endif + +ifdef BOARD_VARIANT +MAKE_ARGS += -DMICROPY_BOARD_VARIANT=$(BOARD_VARIANT) +endif + +ifdef MICROPY_PREVIEW_VERSION_2 +MAKE_ARGS += -DMICROPY_PREVIEW_VERSION_2=1 +endif + +FROZEN_MANIFEST ?= boards/manifest.py + +CFLAGS = $(INC) $(QUEC_MOD_CFLAGS) $(PLAT_CFLAGS) $(PLAT_DFLAGS) $(COPT) $(MAKE_ARGS) + +CSUPEROPT = -Os # save some code space + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# MicroPython feature configurations +MICROPY_ROM_TEXT_COMPRESSION ?= 1 + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +CROSS_COMPILE ?= arm-none-eabi- + + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += $(MICROPYTHON_CFLAGS_INC) +INC += $(QUEC_INC) + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -O0 -ggdb +else +CFLAGS += -Os -DNDEBUG +CFLAGS += -fdata-sections -ffunction-sections +endif + +# Flags for optional C++ source code +CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) +CXXFLAGS += $(CXXFLAGS_MOD) + +# Flags for user C modules +CFLAGS += $(CFLAGS_MOD) +LDFLAGS += $(LDFLAGS_MOD) + +LIBS = + +SHARED_SRC_C = shared/libc/printf.c \ + shared/readline/readline.c \ + shared/runtime/pyexec.c \ + shared/runtime/sys_stdio_mphal.c \ + shared/runtime/stdout_helpers.c \ + shared/netutils/netutils.c \ + shared/runtime/interrupt_char.c \ + shared/timeutils/timeutils.c + +EXTMOD_SRC_C = extmod/vfs.c \ + extmod/vfs_lfs.c \ + extmod/vfs_blockdev.c \ + extmod/modrandom.c \ + extmod/modjson.c \ + extmod/modbinascii.c \ + extmod/vfs_reader.c \ + extmod/modre.c \ + extmod/modhashlib.c \ + extmod/moductypes.c + +EXTMOD_SRC_C += $(SRC_THIRDPARTY_C) + +SRC_MOD += $(QUEC_SRC_MOD) + +SRC_C += $(SRC_MOD) \ + $(SHARED_SRC_C) \ + $(EXTMOD_SRC_C) \ + $(QUEC_SRC) \ + $(BUILD)/frozen_content.c + +SRC_CXX += $(SRC_MOD_CXX) + +SRC_QSTR += $(SRC_MOD) $(SRC_MOD_CXX) $(SHARED_SRC_C) $(EXTMOD_SRC_C) + +OBJ += $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) + +all: $(BUILD)/firmware.a + +$(BUILD)/_frozen_mpy.c: frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h + $(ECHO) "MISC freezing bytecode" + $(Q)$(PYTHON) $(TOP)/tools/mpy-tool.py -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h -mlongint-impl=none $< > $@ + +$(BUILD)/firmware.a: $(OBJ) + $(ECHO) "AR $@" + $(Q)$(AR) -cr $@ $^ + +include $(TOP)/py/mkrules.mk diff --git a/ports/quectel/README.md b/ports/quectel/README.md new file mode 100644 index 0000000000000..a0fcb3dd74944 --- /dev/null +++ b/ports/quectel/README.md @@ -0,0 +1,136 @@ +MicroPython port to the Quectel +============================= + +This is a port of MicroPython to the Quectel series of +microcontrollers. It uses the HeliosSDK and MicroPython runs as +a task under FreeRTOS. + +Support Eigencomm, Unisoc, Qualcomm and ASR cellular modules. + +Supported features include: +- REPL (Python prompt) over usb. +- Python 3.4 syntax and built-in rich functional modules. +- The machine module with GPIO, EXTINT, UART, SPI, ADC, WDT RTC, and Timer. +- The network module with cellular modem support. +- etc. + +Setting up HeliosSDK and the build environment +-------------------------------------------- + +MicroPython on quectel port requires the HeliosSDK. The HeliosSDK includes the libraries and RTOS needed to +manage the quectel microcontroller, as well as a way to manage the required +build environment and toolchains needed to build the firmware. + +To install the HeliosSDK the full instructions can be found at the +[HeliosSDK Development Guide](https://python.quectel.com/doc/Application_guide/zh/helios-sdk/quick-start.html). + +**Windows 10 Environment Setup** + +*Step 1: Download Toolchain* + +Download the toolchain installation package [helios-toolchain.exe](https://github.com/QuecPython/toolchain/releases/tag/V1.4.2) for Windows 10 environment from the QuecPython official website. + +*Step 2: Install Toolchain* + +Run helios-toolchain.exe as an administrator, as shown in the following figure, and click "**Install**" to install the toolchain. + +> The target folder **must not** contain spaces. + +**Ubuntu Environment Setup** + +*Step 1: Download Toolchain* + +Download the toolchain installation package [helios-toolchain](https://github.com/QuecPython/toolchain/releases/tag/V1.1.0) for Ubuntu 16.04 environment from the QuecPython official website. + +*Step 2: Install Toolchain* + +Place the installation package in the same location as the target folder. Execute the following command to install the toolchain. +`sudo ./helios-toolchain` + +*Step 3: Install Other Tools* + +Enter the following command in the terminal to install `p7zip-full`, `git`, `make`, and `python3`. +``` +sudo apt install p7zip-full git make python3 +``` + +*Source Code* + +For HeliosSDK, please contact the [QuecPython technical team](https://python.quectel.com/en/contact) for the source code of HeliosSDK(We are preparing to open source), You can also get supports through email QuecPython@quectel.com. + +For MicroPython, You can directly pull the official code, but you need to pull MicroPython into the heliossdk directory, for example, create a services directory under heliossdk and place MicroPython in the services directory. + +Building the firmware +--------------------- + +Before you start building the firmware, you must build the MicroPython cross compiler firstly, it will be used to pre-compile some of the built-in scripts to bytecode. + +> If you are in a Windows environment, you need a Windows compilation toolchain, such as MinGW. + +```bash +# path: heliossdk/services/micropython +$ make -C mpy-cross +``` + +Then to build MicroPython for the quectel run: + +```bash +# path: heliossdk/services/micropython +$ cd ports/quectel +$ make submodules +$ make +``` + +This will produce a combined `firmware.a` lib in the `build/` +subdirectory directly.The compiled MicroPython library will be used by HeliosSDK to build a complete firmware package. Of course, you can also build the entire firmware directly using the following method. + +*Check the usage of the helios compilation commands* + +In the command line started in the HeliosSDK directory, type `helios` and press "**Enter**" to view the usage of the helios command. +The output is as follows: + +``` +Usage: helios [] [] [] + +These are common commands used in various situations: + menuconfig - Do the project configuration + make [[] []] - Do the compilation work + private_clean - Clean the app private target + clean - Clean the output directory + git [] - Git commands execution + help - Show this help page +``` + +> For detailed usage of the compilation command, please refer to `README.MD` in the SDK root directory. + +*Compile the firmware* + +Taking the EG915UEC_AC module as an example, type the following command in the command line and press "**Enter**": +``` +helios make services/micropython @EG915UEC_AC EG915UECACR03A01M08 +``` + +- `helios`: Trigger the compilation process. +- `make`: Compile the firmware. +- `services/micropython`: Application entry address (relative to the SDK root directory, according to the requirements of the host system, the Win32 platform is \ and the Linux platform is /). It can be adjusted according to the specific location of MicroPython. +- `@EG915UEC_AC`: Specify the target module model. You need to modify it according to your actual model. +- `EG915UECACR03A01M08`: Firmware version name, which can be omitted. You need to modify it according to your actual model. + +*Check the compilation target* + +The generated firmware package is stored in the *`output/release`* folder in the HeliosSDK root directory. + +To clean the compilation target, type the following command in the command line and press "**Enter**": +``` +helios clean +``` + +*Flash the firmware* + +You need to use QPYcom or VSCode tool to burn the firmware. Please refer to [Quectel_QFlash_User_Guide](https://python.quectel.com/doc/Getting_started/en/flash_firmware.html) for firmware flashing. + + +Accessing the board +------------------------------------- + +You can access via the `USB REPL` port, which stands for `Read-Eval-Print-Loop` (interactive interpreter). Please refer to [Quectel_Getting_Started](https://python.quectel.com/doc/Getting_started/en/REPL_dev.html) for firmware debugging. diff --git a/ports/quectel/boards/EC600UCN_LB/board.json b/ports/quectel/boards/EC600UCN_LB/board.json new file mode 100644 index 0000000000000..3edacaf108c01 --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Celluar network", + "bsp", + "USB" + ], + "images": [ + "ec600ucn_lb.jpg" + ], + "mcu": "ec600ucnlb", + "product": "EC600UCN_LB", + "thumbnail": "", + "url": "https://developer.quectel.com/modules-cat/ec600u-series", + "vendor": "Unisoc" +} diff --git a/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h b/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h new file mode 100644 index 0000000000000..6b68f6af78f16 --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h @@ -0,0 +1 @@ +// Board and hardware specific configuration diff --git a/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk b/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk new file mode 100644 index 0000000000000..139597f9cb07c --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk @@ -0,0 +1,2 @@ + + diff --git a/ports/quectel/boards/EG915UEC_AC/board.json b/ports/quectel/boards/EG915UEC_AC/board.json new file mode 100644 index 0000000000000..24894487c2793 --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Celluar network", + "bsp", + "USB" + ], + "images": [ + "eg915uec_ac.jpg" + ], + "mcu": "eg915uecac", + "product": "EG915UEC_AC", + "thumbnail": "", + "url": "https://developer.quectel.com/en/modules-cat/eg915u-series", + "vendor": "Unisoc" +} \ No newline at end of file diff --git a/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h b/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h new file mode 100644 index 0000000000000..6b68f6af78f16 --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h @@ -0,0 +1 @@ +// Board and hardware specific configuration diff --git a/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk b/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk new file mode 100644 index 0000000000000..139597f9cb07c --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk @@ -0,0 +1,2 @@ + + diff --git a/ports/quectel/boards/deploy.md b/ports/quectel/boards/deploy.md new file mode 100644 index 0000000000000..d23eaced8cbbb --- /dev/null +++ b/ports/quectel/boards/deploy.md @@ -0,0 +1,3 @@ +*Flash the firmware* + +You need to use QPYcom or VSCode tool to burn the firmware. Please refer to [Quectel_QFlash_User_Guide](https://python.quectel.com/doc/Getting_started/en/flash_firmware.html) for firmware flashing. diff --git a/ports/quectel/boards/manifest.py b/ports/quectel/boards/manifest.py new file mode 100644 index 0000000000000..b0b0d31a051b1 --- /dev/null +++ b/ports/quectel/boards/manifest.py @@ -0,0 +1,2 @@ +freeze("$(PORT_DIR)/modules") +include("$(MPY_DIR)/extmod/asyncio") diff --git a/ports/quectel/callbackdeal.c b/ports/quectel/callbackdeal.c new file mode 100644 index 0000000000000..dce7dcd77dc4a --- /dev/null +++ b/ports/quectel/callbackdeal.c @@ -0,0 +1,885 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include "helios_os.h" +#include "stackctrl.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "callbackdeal.h" +#include "py/obj.h" +#if MICROPY_ENABLE_CALLBACK_DEAL + + +#ifdef QPY_CALLBACKDEAL_LOG_ENABLE +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) +extern void ql_log_mask_set(unsigned long long module_mask, unsigned int port_mask); +extern void _ql_app_log(const char* fmt, ...); +static char usb_trace_init_flag = 0; +#define qpy_callbackdeal_trace(fmt, ...) do{ \ + if(!usb_trace_init_flag) \ + { \ + ql_log_mask_set(0x20, 0x02); \ + usb_trace_init_flag = 1; \ + } \ + _ql_app_log("[%s][%s, L%d]"fmt"", "cbdl", __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + }while(0) + +#elif defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8850_R02) || defined(PLAT_Unisoc_8910_R06) +#include "helios_debug.h" +#define qpy_callbackdeal_trace(fmt, ...) Helios_Debug_Output("[%s][%s, L%d]"fmt"", "cbdl", __FUNCTION__, __LINE__, ##__VA_ARGS__) +#else +#error "The platform not support usb trace!!!" +#endif +#else +#define qpy_callbackdeal_trace(fmt, ...) +#endif + + + +#define QPY_CALLBACK_DEAL_MSG_MAX_NUM (2 * MICROPY_SCHEDULER_DEPTH) +#if defined(PLAT_ECR6600) || defined (PLAT_aic8800m40) +#define QPY_CALLBACK_DEAL_THREAD_STACK_SIZE (4 *1024) +#else +#define QPY_CALLBACK_DEAL_THREAD_STACK_SIZE (16 *1024) +#endif + +static Helios_MsgQ_t qpy_callback_deal_queue = 0; +Helios_Thread_t qpy_callback_deal_task_ref = 0; + +typedef struct _callback_deal_thread_entry_args_t { + mp_obj_dict_t *dict_locals; + mp_obj_dict_t *dict_globals; +} callback_deal_thread_entry_args_t; + +#if MICROPY_QPY_MODULE_BT +extern void qpy_bt_msg_proc(void *msg); +#endif +#if MICROPY_QPY_MODULE_BLE +extern void qpy_ble_msg_proc(void *msg); +#endif + +#if MICROPY_QPY_MODULE_POC +extern void qpy_poc_msg_proc ( void *msg ); +#endif //end MICROPY_QPY_MODULE_POC + +#if MICROPY_QPY_MODULE_LWM2M +extern void qpy_lwm2m_msg_proc(void *param); +#endif //end MICROPY_QPY_MODULE_LWM2M + +#if MICROPY_QPY_MODULE_ESIM && MICROPY_QPY_MODULE_ESIM_IPA +extern void qpy_esim_msg_proc(void *param); +#endif +/* + * Create a task&queue that handles callback tasks exclusively + */ +void qpy_callback_deal_thread(void * args_in); + +/*Jayceon 2022/08/31 add for QuecPython --start*/ +extern bool mp_obj_is_boundmeth(mp_obj_t o); +extern mp_obj_t mp_obj_bound_get_self(void *bound); +extern mp_obj_t mp_obj_bound_get_meth(void *bound); +extern bool mp_obj_is_closure(void *obj); +extern mp_obj_t mp_obj_closure_get_fun(void *closure); + +typedef struct temp_mp_obj_bound_meth_t { + mp_obj_base_t base; + mp_obj_t meth; + mp_obj_t self; +} temp_mp_obj_bound_meth_t; + +typedef struct temp_mp_obj_closure_t { + mp_obj_base_t base; + mp_obj_t fun; + size_t n_closed; + mp_obj_t closed[]; +} temp_mp_obj_closure_t; + +mp_obj_t mp_obj_bound_get_self(void *bound) +{ + if(NULL == bound) + return NULL; + + return ((temp_mp_obj_bound_meth_t *)bound)->self; +} + +mp_obj_t mp_obj_bound_get_meth(void *bound) +{ + if(NULL == bound) + return NULL; + + return ((temp_mp_obj_bound_meth_t *)bound)->meth; +} + +bool mp_obj_is_boundmeth(mp_obj_t o) { + + return mp_obj_is_type(o, &mp_type_bound_meth); +} + +extern const mp_obj_type_t mp_type_closure; +bool mp_obj_is_closure(void *obj) +{ + return mp_obj_is_type(obj, &mp_type_closure); +} + +mp_obj_t mp_obj_closure_get_fun(void *closure){ + if(NULL == closure) + return NULL; + + return ((temp_mp_obj_closure_t *)closure)->fun; +} + +extern void mp_main_thread_wakeup(); +static bool _mp_sched_schedule_ex(c_callback_t *callback) { + + // When executing code within a handler we must lock the scheduler to + // prevent any scheduled callbacks from running, and lock the GC to + // prevent any memory allocations. + mp_sched_lock(); + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_sched_schedule(callback->cb, callback->arg); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so that it doesn't run again + mp_printf(&mp_plat_print, "Uncaught exception in IRQ callback handler\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + gc_unlock(); + mp_sched_unlock(); + return false; + } + gc_unlock(); + mp_sched_unlock(); + + mp_main_thread_wakeup(); + return true; +} + +//Add a callback to the unscheduled table. If it is a Method, +//load it first so that the recorded callback is not collected by the GC +bool mp_sched_schedule_ex(c_callback_t *callback, mp_obj_t arg) +{ + (void)arg; + if(NULL == callback) + { + return false; + } + + if((false == callback->is_method) && mp_obj_is_callable(callback->cb))//function + { + return _mp_sched_schedule_ex(callback); + } + else if(true == callback->is_method)//bound_method + { + if(mp_obj_is_boundmeth(callback->cb)) { + return _mp_sched_schedule_ex(callback); + } else { + mp_obj_t cb = mp_load_attr(callback->method_self, callback->method_name); + callback->cb = cb;//record a new boundmeth + return _mp_sched_schedule_ex(callback); + } + } + else + { + return false; + } +} + +//Register callback +bool mp_sched_schedule_callback_register(c_callback_t *callback, mp_obj_t func_obj) +{ + if(NULL == callback || NULL == func_obj) { + return false; + } + //memset(callback, 0 , sizeof(c_callback_t)); + if(mp_const_none != func_obj) + { + if(mp_obj_is_boundmeth(func_obj))//is bound_meth. Records the object for this method, along with the bytecode for the name of this method + { + callback->is_method = true; + callback->method_self = mp_obj_bound_get_self(func_obj); + mp_obj_t fun = mp_obj_bound_get_meth((int*)func_obj); + if(mp_obj_is_closure(fun)) { + callback->method_name = mp_obj_fun_get_name(mp_obj_closure_get_fun(fun)); + } else { + callback->method_name = mp_obj_fun_get_name(fun); + } + callback->cb = func_obj; + return true; + } + } + + callback->is_method = false; + callback->cb = func_obj; + + return true; +} + +void qpy_callback_deal_init(void) +{ +#if !defined(PLAT_RDA) + qpy_callback_deal_queue = Helios_MsgQ_Create(QPY_CALLBACK_DEAL_MSG_MAX_NUM, sizeof(st_CallBack_Deal)); +#endif + // create thread + callback_deal_thread_entry_args_t *th_args; + th_args = m_new_obj(callback_deal_thread_entry_args_t); + // pass our locals and globals into the new thread + th_args->dict_locals = mp_locals_get(); + th_args->dict_globals = mp_globals_get(); + + Helios_ThreadAttr ThreadAttr = { + .name = "cb_deal", + .stack_size = QPY_CALLBACK_DEAL_THREAD_STACK_SIZE, + .priority = (MP_THREAD_PRIORITY - 2),//high than mpthread + .entry = (void*)qpy_callback_deal_thread, + .argv = (void*)th_args + }; + qpy_callback_deal_task_ref = Helios_Thread_Create(&ThreadAttr); + //add thread to python_thread link list, the thread is automatically deleted after the VM restarts. + mp_new_thread_add((uint32_t)qpy_callback_deal_task_ref, QPY_CALLBACK_DEAL_THREAD_STACK_SIZE); +} + +/* + * Returns whether the current thread is callbackdeal thread + */ +bool qpy_is_callback_deal_thread(void) +{ + return (Helios_Thread_GetID() == qpy_callback_deal_task_ref); +} + +/* + * delete callback_deal task&queue + */ +void qpy_callback_deal_deinit(void) +{ + Helios_MsgQ_Delete(qpy_callback_deal_queue); qpy_callback_deal_queue = (Helios_MsgQ_t)0; + //Just set it to null and the thread will be deleted automatically when the vm restarts + qpy_callback_deal_task_ref = (Helios_Thread_t)0; +} + + +//Added by Freddy @20211124 发送消息至deal task的queue +int qpy_send_msg_to_callback_deal_thread(uint8_t id, void *msg) +{ + int ret = -1; + if((Helios_MsgQ_t)0 != qpy_callback_deal_queue) + { + st_CallBack_Deal cb_msg = {0}; + cb_msg.callback_type_id = id; + cb_msg.msg = msg; + qpy_callbackdeal_trace("callback_type_id:%d", id); + ret = Helios_MsgQ_Put(qpy_callback_deal_queue, (void*)(&cb_msg), sizeof(st_CallBack_Deal), HELIOS_NO_WAIT); + } + return ret; +} + + +st_CallBack_LoadBoundMeth * qpy_callback_para_node_add(c_callback_t *callback, mp_obj_t arg) +{ + st_CallBack_LoadBoundMeth *loadboundmet_msg = calloc(1, sizeof(st_CallBack_LoadBoundMeth)); + if(NULL == loadboundmet_msg) + { + return NULL; + } +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + int rc = Helios_Critical_Enter(); +#endif + loadboundmet_msg->next = (st_CallBack_LoadBoundMeth *)MP_STATE_PORT(loadmeth_para); + loadboundmet_msg->callback = callback; + loadboundmet_msg->arg = arg; + MP_STATE_PORT(loadmeth_para) = (void *)loadboundmet_msg; + qpy_callbackdeal_trace("loadmeth_para:0x%x", MP_STATE_PORT(loadmeth_para)); +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + Helios_Critical_Exit(rc); +#endif + + return loadboundmet_msg; +} + + +void qpy_callback_para_node_delete(st_CallBack_LoadBoundMeth *cb_msg) +{ +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + int rc = Helios_Critical_Enter(); +#endif + qpy_callbackdeal_trace("loadmeth_para:0x%x", MP_STATE_PORT(loadmeth_para)); + st_CallBack_LoadBoundMeth *loadmeth_para_head = (st_CallBack_LoadBoundMeth *)MP_STATE_PORT(loadmeth_para); + st_CallBack_LoadBoundMeth *loadmeth_para_tmp = loadmeth_para_head; + for(;loadmeth_para_head != NULL; loadmeth_para_head = loadmeth_para_head->next) + { + if(loadmeth_para_head == cb_msg) + { + qpy_callbackdeal_trace("cb_msg:0x%x", cb_msg); + if(loadmeth_para_head == (st_CallBack_LoadBoundMeth *)MP_STATE_PORT(loadmeth_para)) + { + MP_STATE_PORT(loadmeth_para) = loadmeth_para_head->next; + qpy_callbackdeal_trace("loadmeth_para:0x%x", MP_STATE_PORT(loadmeth_para)); + } + else + { + loadmeth_para_tmp->next = loadmeth_para_head->next; + } + break; + } + loadmeth_para_tmp = loadmeth_para_head; + } +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + Helios_Critical_Exit(rc); +#endif + +} + +void qpy_callback_para_link_mark_in_used(void) +{ + +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + int rc = Helios_Critical_Enter(); +#endif + st_CallBack_LoadBoundMeth *loadmeth_para_head = (st_CallBack_LoadBoundMeth *)MP_STATE_PORT(loadmeth_para); + for(;loadmeth_para_head != NULL; loadmeth_para_head = loadmeth_para_head->next) + { + if(NULL != loadmeth_para_head->arg) + { + gc_collect_root((void **)&loadmeth_para_head->arg, 1); + } + } +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + Helios_Critical_Exit(rc); +#endif + +} + + +void qpy_callback_para_link_free_all(void) +{ +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + int rc = Helios_Critical_Enter(); +#endif + st_CallBack_LoadBoundMeth *loadmeth_para_head = (st_CallBack_LoadBoundMeth *)MP_STATE_PORT(loadmeth_para); + st_CallBack_LoadBoundMeth *loadmeth_para_tmp = NULL; + while(loadmeth_para_head != NULL) + { + loadmeth_para_tmp = loadmeth_para_head; + loadmeth_para_head = loadmeth_para_head->next; + free(loadmeth_para_tmp); + } + MP_STATE_PORT(loadmeth_para) = NULL; +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_ASR_1602) + Helios_Critical_Exit(rc); +#endif + +} + + +bool mp_sched_empty(void); +/* + * Resolve the crash issue when non-Python threads apply for GC memory and throw exceptions when memory application fails + */ +void qpy_callback_deal_thread(void * args_in) +{ +#if !defined(PLAT_RDA) + qpy_callback_deal_queue = Helios_MsgQ_Create(QPY_CALLBACK_DEAL_MSG_MAX_NUM, sizeof(st_CallBack_Deal)); +#endif + callback_deal_thread_entry_args_t *args = (callback_deal_thread_entry_args_t *)args_in; + mp_state_thread_t ts; + mp_thread_set_state(&ts); + st_CallBack_Deal msg = {0}; + + mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan +#if defined(PLAT_ECR6600) || defined (PLAT_aic8800m40) + mp_stack_set_limit(QPY_CALLBACK_DEAL_THREAD_STACK_SIZE * sizeof(uint32_t) - 1024); +#else + mp_stack_set_limit(QPY_CALLBACK_DEAL_THREAD_STACK_SIZE - 1024); +#endif + + #if MICROPY_ENABLE_PYSTACK + // TODO threading and pystack is not fully supported, for now just make a small stack + mp_obj_t mini_pystack[128]; + mp_pystack_init(mini_pystack, &mini_pystack[128]); + #endif + + // set locals and globals from the calling context + mp_locals_set(args->dict_locals); + mp_globals_set(args->dict_globals); + m_del_obj(callback_deal_thread_entry_args_t, args); + + MP_THREAD_GIL_ENTER(); + + //mp_thread_start must placed after GIL_ENTER + // signal that we are set up and running + mp_thread_start(); + + while(1) + { + mp_obj_t arg = NULL; + MP_THREAD_GIL_EXIT(); + qpy_callbackdeal_trace("wait msg"); + int ret = Helios_MsgQ_Get(qpy_callback_deal_queue, (void*)&msg, sizeof(msg), HELIOS_WAIT_FOREVER); + qpy_callbackdeal_trace("get msg"); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + //Each is processed according to the callback ID + qpy_callbackdeal_trace("callback_type_id:%d", msg.callback_type_id); + switch(msg.callback_type_id) + { + case CALLBACK_TYPE_ID_EXTINT: + { + st_CallBack_Extint *cb_msg = (st_CallBack_Extint *)msg.msg; + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(cb_msg->callback.arg); + pair->items[0] = mp_obj_new_int(cb_msg->pin_no); + pair->items[1] = mp_obj_new_int(cb_msg->edge); + // mp_obj_t extint_list[2] = { + // mp_obj_new_int(cb_msg->pin_no), + // mp_obj_new_int(cb_msg->edge), + // }; + // arg = mp_obj_new_list(2, extint_list); + mp_sched_schedule_ex(&cb_msg->callback, cb_msg->callback.arg); + } + break; + case CALLBACK_TYPE_ID_AUDIO_RECORD: + { + st_CallBack_AudioRecord *cb_msg = (st_CallBack_AudioRecord *)msg.msg; + mp_obj_t audio_cb[3] = { + mp_obj_new_str(cb_msg->p_data,strlen(cb_msg->p_data)), + mp_obj_new_int(cb_msg->len), + mp_obj_new_int(cb_msg->res), + }; + if(RECORE_TYPE_FILE == cb_msg->record_type && NULL != cb_msg->p_data) { + free(cb_msg->p_data); + } + arg = mp_obj_new_list(3, audio_cb); + mp_sched_schedule_ex(&cb_msg->callback, arg); + #if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + if (RECORE_TYPE_STREAM == cb_msg->record_type) + { + MP_THREAD_GIL_EXIT(); + Helios_msleep(10); + MP_THREAD_GIL_ENTER(); + } + #endif + } + break; + case CALLBACK_TYPE_ID_VOICECALL_RECORD: + { + st_CallBack_AudioRecord *cb_msg = (st_CallBack_AudioRecord *)msg.msg; + mp_obj_t audio_cb[3] = { + mp_obj_new_str(cb_msg->p_data,strlen(cb_msg->p_data)), + mp_obj_new_int(cb_msg->len), + mp_obj_new_int(cb_msg->res), + }; + + arg = mp_obj_new_list(3, audio_cb); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #if MICROPY_QPY_MODULE_QUECIOT + case CALLBACK_TYPE_ID_QUETCH_CALLCB: + { + st_CallBack_Queth cb_msg; + memcpy(&cb_msg,msg.msg,sizeof(st_CallBack_Queth)); + mp_obj_t tuple[] = + { + mp_obj_new_int_from_uint(cb_msg.event), + mp_obj_new_int_from_uint(cb_msg.errcode), + mp_obj_new_bytes(cb_msg.valueT, cb_msg.valLen) + }; + if ( cb_msg.valueT != NULL ) + free(cb_msg.valueT); + free(msg.msg); + mp_sched_schedule_ex(&(cb_msg.call_cb),mp_obj_new_tuple(3, tuple)); + } + break; + #endif //end MICROPY_QPY_MODULE_QUECIOT + #if MICROPY_QPY_MODULE_POC + case CALLBACK_TYPE_ID_POC: + { + qpy_poc_msg_proc(msg.msg); + } + break; + #endif //end MICROPY_QPY_MODULE_POC + #if MICROPY_QPY_MACHINE_KEYPAD + case CALLBACK_TYPE_ID_KEYPAD: + { + st_CallBack_Keypad *cb_msg = (st_CallBack_Keypad *)msg.msg; + mp_obj_t keypad_list[3] = + { + mp_obj_new_int(cb_msg->event_id), + mp_obj_new_int(cb_msg->row), + mp_obj_new_int(cb_msg->col), + }; + arg = mp_obj_new_list(3, keypad_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + + } + break; + #endif + #if MICROPY_QPY_SENSOR_ICC_KEY + case CALLBACK_TYPE_ID_ICCKEY: + { + st_CallBack_IccKey *cb_msg = (st_CallBack_IccKey *)msg.msg; + mp_sched_schedule_ex(&cb_msg->callback, mp_obj_new_int(cb_msg->key_event)); + } + break; + #endif + + #if MICROPY_QPY_MACHINE_KEY + case CALLBACK_TYPE_ID_KEY: + { + st_CallBack_Key *cb_msg = (st_CallBack_Key *)msg.msg; + mp_obj_t key_list[2] = { + mp_obj_new_int(cb_msg->gpio_number), + mp_obj_new_int(cb_msg->key_event), + }; + arg = mp_obj_new_list(2, key_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #endif + #if MICROPY_QPY_SENSOR_HANDMIC_KEY + case CALLBACK_TYPE_ID_HANDMIC_KEY: + { + st_CallBack_Handmic *cb_msg = (st_CallBack_Handmic *)msg.msg; + mp_obj_t key_list[2] = { + mp_obj_new_int(cb_msg->key_state), + mp_obj_new_int(cb_msg->key_value), + }; + arg = mp_obj_new_list(2, key_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #endif + #if MICROPY_QPY_MODULE_EXTUART_WK2114 + case CALLBACK_TYPE_ID_WK2114: + { + st_CallBack_Wk2114 *cb_msg = (st_CallBack_Wk2114 *)msg.msg; + mp_obj_t key_list[3] = { + mp_obj_new_int(cb_msg->int_type), + mp_obj_new_int(cb_msg->port), + mp_obj_new_int(cb_msg->read_lenth), + }; + arg = mp_obj_new_list(3, key_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #endif + #if MICROPY_QPY_MODULE_JRTC + case CALLBACK_TYPE_ID_JRTC: + { + st_CallBack_jrtc *cb_msg = (st_CallBack_jrtc *)msg.msg; + arg = mp_obj_new_int(cb_msg->result); + mp_sched_schedule_ex(&cb_msg->callback, arg); + + } + break; + #endif + #if MICROPY_QPY_MODULE_GPIO_ENCODER + case CALLBACK_TYPE_ID_ENCODER: + { + st_CallBack_Encoder *cb_msg = (st_CallBack_Encoder *)msg.msg; + arg = mp_obj_new_int(cb_msg->spin_direct); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #endif + #if MICROPY_QPY_MODULE_EXTGPIO_AW9523 + case CALLBACK_TYPE_ID_AW9523: + { + st_CallBack_AW9523 *cb_msg = (st_CallBack_AW9523 *)msg.msg; + mp_obj_t aw9523_list[2] = { + mp_obj_new_int(cb_msg->pin_no), + mp_obj_new_int(cb_msg->pin_level), + }; + arg = mp_obj_new_list(2, aw9523_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; + #endif + case CALLBACK_TYPE_ID_TP: + { + st_CallBack_TP *cb_msg = (st_CallBack_TP *)msg.msg; + mp_sched_schedule_ex(&cb_msg->callback, mp_obj_new_int(cb_msg->state)); + } + break; + #if MICROPY_QPY_MODULE_BT + case CALLBACK_TYPE_ID_BT: + { + qpy_bt_msg_proc(msg.msg); + break; + } + #endif + #if MICROPY_QPY_MODULE_BLE + case CALLBACK_TYPE_ID_BLE: + { + qpy_ble_msg_proc(msg.msg); + break; + } + #endif + case CALLBACK_TYPE_ID_LOAD_BOUNDMETH: + { + st_CallBack_LoadBoundMeth *cb_msg = (st_CallBack_LoadBoundMeth *)msg.msg; + qpy_callbackdeal_trace("cb_msg:0x%x", cb_msg); + qpy_callback_para_node_delete(cb_msg); + + mp_obj_t cb = mp_load_attr(cb_msg->callback->method_self, cb_msg->callback->method_name); + qpy_callbackdeal_trace("cb:0x%x", cb); + mp_call_function_1_protected(cb, cb_msg->arg); + break; + } + #if MICROPY_QPY_MODULE_ALIPAY + case CALLBACK_TYPE_ID_ALIPAY: + { + st_CallBack_ALIPAY *alipay_msg = (st_CallBack_ALIPAY *)msg.msg; + mp_obj_t alipay_cb[5] = + { + mp_obj_new_int(alipay_msg->cmd), + mp_obj_new_int(alipay_msg->rsp_code), + mp_obj_new_str((char *)(alipay_msg->result), (char *)(strlen(alipay_msg->result))), + mp_obj_new_int(alipay_msg->result_len), + mp_obj_new_str((char *)(alipay_msg->userdata), (char *)(strlen(alipay_msg->userdata))), + }; + mp_sched_schedule_ex(&alipay_msg->callback, mp_obj_new_tuple(5, alipay_cb)); + break; + } + #endif + #if MICROPY_QPY_LPM_WAKEUP + case CALLBACK_TYPE_ID_WAKEUP: + { + st_CallBack_Wakeup *cb_msg = (st_CallBack_Wakeup *)msg.msg; + mp_sched_schedule_ex(&cb_msg->callback, mp_obj_new_int(cb_msg->level)); + break; + } + #endif + #if MICROPY_QPY_MACHINE_UART + case CALLBACK_TYPE_ID_UART: + { + st_CallBack_Uart *uart_msg = (st_CallBack_Uart *)msg.msg; + // mp_obj_t uart_cb[3] = + // { + // mp_obj_new_int(uart_msg->ind_type), + // mp_obj_new_int(uart_msg->port), + // mp_obj_new_int(uart_msg->size), + // }; + // arg = mp_obj_new_list(3,uart_cb); + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(uart_msg->callback.arg); + pair->items[0] = mp_obj_new_int(uart_msg->ind_type); + pair->items[1] = mp_obj_new_int(uart_msg->port); + pair->items[2] = mp_obj_new_int(uart_msg->size); + mp_sched_schedule_ex(&uart_msg->callback, uart_msg->callback.arg); + break; + } + #endif + #if MICROPY_QPY_MODULE_NET + case CALLBACK_TYPE_ID_NET: + { + qpy_callbackdeal_trace("callback_type_id:CALLBACK_TYPE_ID_NET = %d", msg.callback_type_id); + st_CallBack_Net *net_msg = (st_CallBack_Net *)msg.msg; + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(net_msg->callback.arg); + pair->items[0] = mp_obj_new_int(net_msg->event_id); + pair->items[1] = mp_obj_new_int(net_msg->status); + pair->items[2] = mp_obj_new_int(net_msg->lac); + pair->items[3] = mp_obj_new_int(net_msg->cid); + pair->items[4] = mp_obj_new_int(net_msg->act); + + mp_sched_schedule_ex(&net_msg->callback, net_msg->callback.arg); + break; + } + #endif + #if MICROPY_QPY_MODULE_DATACALL + case CALLBACK_TYPE_ID_DATACALL: + { + st_CallBack_Datacall *datacall_msg = (st_CallBack_Datacall *)msg.msg; + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(datacall_msg->callback.arg); + pair->items[0] = mp_obj_new_int(datacall_msg->profile_idx); + pair->items[1] = mp_obj_new_int(datacall_msg->nw_status); + pair->items[2] = mp_obj_new_int(datacall_msg->sim_id); + + mp_sched_schedule_ex(&datacall_msg->callback, datacall_msg->callback.arg); + break; + } + #endif + #if MICROPY_QPY_MODULE_ALIPAY_COLLECTION + case CALLBACK_TYPE_ID_ALIPAY_COLLECTION: + { + st_CallBack_ALIPAY_COLLECTION *alipay_collection_msg = (st_CallBack_ALIPAY_COLLECTION *)msg.msg; + mp_obj_t alipay_collection_cb[2] = + { + mp_obj_new_int(alipay_collection_msg->msgID), + mp_obj_new_int(alipay_collection_msg->data), + }; + mp_sched_schedule_ex(&alipay_collection_msg->callback, mp_obj_new_tuple(2, alipay_collection_cb)); + break; + } + #endif + #if MICROPY_QPY_MODULE_SMS + case CALLBACK_TYPE_ID_SMS: + { + st_CallBack_SMS *sms_msg = (st_CallBack_SMS *)msg.msg; + mp_obj_t sms_cb[3] = + { + mp_obj_new_int(sms_msg->sim_id), + mp_obj_new_int(sms_msg->index), + mp_obj_new_str(sms_msg->storage, strlen(sms_msg->storage)) + }; + mp_sched_schedule_ex(&sms_msg->callback, mp_obj_new_tuple(3, sms_cb)); + break; + } + #endif + #if MICROPY_QPY_MODULE_SIF + case CALLBACK_TYPE_ID_SIF: + { + st_CallBack_SIF *sif_msg = (st_CallBack_SIF *)msg.msg; + mp_sched_schedule_ex(&sif_msg->callback, mp_obj_new_bytes(sif_msg->data, sif_msg->data_len)); + break; + } + #endif + #if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350)//forrest.liu@20231228 add for FAE-102027 dump issue + #if MICROPY_QPY_MACHINE_TIMER + case CALLBACK_TYPE_ID_TIMER: + { + st_CallBack_Timer *timer_msg = (st_CallBack_Timer *)msg.msg; + + mp_sched_schedule_ex(&timer_msg->callback, mp_const_none); + break; + } + #endif + #endif + #if defined(BOARD_EC600MCN_LA_POC_XBND) || defined(BOARD_EC600MCN_LE_POC_XBND) || defined(BOARD_EC600MEU_LA_POC_XBND) \ + || defined(BOARD_EC600MLA_LA_POC_XBND) + case CALLBACK_TYPE_ID_POCSPEAK: + { + st_CallBack_PocSpeak *pocspeak_msg = (st_CallBack_PocSpeak *)msg.msg; + mp_obj_t pocspeak_list[2] = + { + mp_obj_new_int(pocspeak_msg->speak_op), + mp_obj_new_int(pocspeak_msg->speak_result), + }; + arg = mp_obj_new_list(2, pocspeak_list); + mp_sched_schedule_ex(&pocspeak_msg->callback, arg); + break; + } + #endif +#if MICROPY_QPY_MODULE_NFC + case CALLBACK_TYPE_ID_NFC: + { + st_CallBack_nfc *nfc_msg = (st_CallBack_nfc *)msg.msg; + + mp_obj_t nfc_list[2] = {0}; + nfc_list[0] = mp_obj_new_int(nfc_msg->oid); + + if(nfc_msg->data && nfc_msg->data_len != 0) + { + nfc_list[1] = mp_obj_new_bytes(nfc_msg->data, nfc_msg->data_len); + arg = mp_obj_new_list(2, nfc_list); + } else { + arg = mp_obj_new_list(1, nfc_list); + } + mp_sched_schedule_ex(&nfc_msg->callback, arg); + + if(nfc_msg->data) { + free(nfc_msg->data); + nfc_msg->data = NULL; + } + + break; + } +#endif +#if MICROPY_QPY_SENSOR_VC9202 + + case CALLBACK_TYPE_ID_VC9202: + { + st_CallBack_Sensor_VC9202 *cb_msg = (st_CallBack_Sensor_VC9202 *)msg.msg; + mp_obj_t vc9202_list[3] = { + mp_obj_new_int(cb_msg->work_mode), + mp_obj_new_int(cb_msg->value), + mp_obj_new_int(cb_msg->measure_mode), + }; + arg = mp_obj_new_list(3, vc9202_list); + mp_sched_schedule_ex(&cb_msg->callback, arg); + } + break; +#endif +#if MICROPY_QPY_MODULE_LWM2M + case CALLBACK_TYPE_ID_LWM2M:{ + qpy_lwm2m_msg_proc(msg.msg); + break; + } +#endif +#if MICROPY_QPY_MODULE_ESIM && MICROPY_QPY_MODULE_ESIM_IPA + case CALLBACK_TYPE_ID_ESIM_IPA:{ + qpy_esim_msg_proc(msg.msg); + break; + } +#endif + case CALLBACK_TYPE_ID_NONE: + default: + break; + } + + #if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350)//forrest.liu@20231228 modify for FAE-102027 dump issue + if(msg.callback_type_id != CALLBACK_TYPE_ID_TIMER) + { + if(msg.msg) { + free(msg.msg); + } + } + memset(&msg, 0, sizeof(st_CallBack_Deal)); + #else + if(msg.msg) { + free(msg.msg); + msg.msg = NULL; + } + memset(&msg, 0, sizeof(st_CallBack_Deal)); + #endif + + } + else + { +#if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350)//forrest.liu@20231228 modify for FAE-102027 dump issue + if(msg.callback_type_id != CALLBACK_TYPE_ID_TIMER) + { + if(msg.msg) { + free(msg.msg); + } + } + memset(&msg, 0, sizeof(st_CallBack_Deal)); +#else + if(msg.msg) { + free(msg.msg); + } + memset(&msg, 0, sizeof(st_CallBack_Deal)); +#endif + } + } +} + +#endif + +MP_REGISTER_ROOT_POINTER(void * loadmeth_para); diff --git a/ports/quectel/callbackdeal.h b/ports/quectel/callbackdeal.h new file mode 100644 index 0000000000000..465ca378053c2 --- /dev/null +++ b/ports/quectel/callbackdeal.h @@ -0,0 +1,389 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +// empty file +#ifndef __CALLBACKDEAL_H__ +#define __CALLBACKDEAL_H__ + +#include "py/runtime.h" + +#if MICROPY_QPY_MODULE_BT || MICROPY_QPY_MODULE_BLE +#include "helios_btble.h" +#endif + +#if MICROPY_QPY_MODULE_ALIPAY +#include "helios_aliiot.h" +#endif + +#if MICROPY_QPY_MACHINE_UART +#include "helios_uart.h" +#endif + +#if MICROPY_ENABLE_CALLBACK_DEAL + +typedef struct _c_callback_t { + bool is_method; + mp_obj_t method_self; + qstr method_name; + mp_obj_t cb; + mp_obj_t arg; +}c_callback_t; + +typedef struct{ + uint8_t callback_type_id; + void *msg; +}st_CallBack_Deal; + +enum { + CALLBACK_TYPE_ID_NONE, + CALLBACK_TYPE_ID_LOAD_BOUNDMETH, + CALLBACK_TYPE_ID_EXTINT, + CALLBACK_TYPE_ID_OSTIMER, + CALLBACK_TYPE_ID_HWTIMER, + CALLBACK_TYPE_ID_AUDIO_AUDIO, + CALLBACK_TYPE_ID_AUDIO_RECORD, + CALLBACK_TYPE_ID_AUDIO_TTS, + CALLBACK_TYPE_ID_CAM_CAP, + CALLBACK_TYPE_ID_VOICECALL_RECORD, +#if MICROPY_QPY_MODULE_QUECIOT + CALLBACK_TYPE_ID_QUETCH_CALLCB, +#endif +#if MICROPY_QPY_MODULE_POC + CALLBACK_TYPE_ID_POC, +#endif +#if MICROPY_QPY_MACHINE_KEYPAD + CALLBACK_TYPE_ID_KEYPAD, +#endif +#if MICROPY_QPY_MACHINE_KEY + CALLBACK_TYPE_ID_KEY, +#endif +#if MICROPY_QPY_SENSOR_ICC_KEY + CALLBACK_TYPE_ID_ICCKEY, +#endif +#if MICROPY_QPY_SENSOR_HANDMIC_KEY + CALLBACK_TYPE_ID_HANDMIC_KEY, +#endif +#if MICROPY_QPY_MODULE_EXTUART_WK2114 + CALLBACK_TYPE_ID_WK2114, +#endif +#if MICROPY_QPY_MODULE_GPIO_ENCODER + CALLBACK_TYPE_ID_ENCODER, +#endif +#if MICROPY_QPY_MODULE_EXTGPIO_AW9523 + CALLBACK_TYPE_ID_AW9523, +#endif + CALLBACK_TYPE_ID_TP, +#if MICROPY_QPY_MODULE_BT + CALLBACK_TYPE_ID_BT, +#endif +#if MICROPY_QPY_MODULE_BLE + CALLBACK_TYPE_ID_BLE, +#endif +#if MICROPY_QPY_MODULE_ALIPAY + CALLBACK_TYPE_ID_ALIPAY, +#endif +#if MICROPY_QPY_LPM_WAKEUP + CALLBACK_TYPE_ID_WAKEUP, +#endif +#if MICROPY_QPY_MACHINE_UART + CALLBACK_TYPE_ID_UART, +#endif +#if MICROPY_QPY_MODULE_NET + CALLBACK_TYPE_ID_NET, +#endif +#if MICROPY_QPY_MODULE_DATACALL + CALLBACK_TYPE_ID_DATACALL, +#endif +#if MICROPY_QPY_MODULE_ALIPAY_COLLECTION + CALLBACK_TYPE_ID_ALIPAY_COLLECTION, +#endif +#if MICROPY_QPY_MODULE_SMS + CALLBACK_TYPE_ID_SMS, +#endif +#if MICROPY_QPY_MODULE_JRTC + CALLBACK_TYPE_ID_JRTC, +#endif +#if MICROPY_QPY_MODULE_SIF + CALLBACK_TYPE_ID_SIF, +#endif +#if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350) +#if MICROPY_QPY_MACHINE_TIMER + CALLBACK_TYPE_ID_TIMER, +#endif +#endif +#if defined(BOARD_EC600MCN_LA_POC_XBND) || defined(BOARD_EC600MCN_LE_POC_XBND) || defined(BOARD_EC600MEU_LA_POC_XBND)\ + || defined(BOARD_EC600MLA_LA_POC_XBND) + CALLBACK_TYPE_ID_POCSPEAK, +#endif +#if MICROPY_QPY_MODULE_NFC + CALLBACK_TYPE_ID_NFC, +#endif +#if MICROPY_QPY_SENSOR_VC9202 + CALLBACK_TYPE_ID_VC9202, +#endif +#if MICROPY_QPY_MODULE_LWM2M + CALLBACK_TYPE_ID_LWM2M, +#endif +#if MICROPY_QPY_MODULE_ESIM && MICROPY_QPY_MODULE_ESIM_IPA + CALLBACK_TYPE_ID_ESIM_IPA, +#endif +}; + +typedef struct CallBack_LoadBoundMeth{ + c_callback_t *callback; + void *arg; + struct CallBack_LoadBoundMeth *next; +}st_CallBack_LoadBoundMeth; + +typedef struct{ + uint8_t key_event; + c_callback_t callback; +}st_CallBack_IccKey; + +typedef struct{ + uint8_t gpio_number; + uint8_t key_event; + c_callback_t callback; +}st_CallBack_Key; + +typedef struct{ + uint8_t state; + c_callback_t callback; +}st_CallBack_TP; + +typedef struct{ + uint8_t pin_no; + uint8_t edge; + c_callback_t callback; +}st_CallBack_Extint; + +typedef struct{ + uint8_t key_value; + uint8_t key_state; + c_callback_t callback; +}st_CallBack_Handmic; + +typedef struct{ + uint32_t int_type; + uint32_t port; + uint32_t read_lenth; + c_callback_t callback; +}st_CallBack_Wk2114; + +typedef struct{ + uint8_t pin_no; + uint8_t pin_level; + c_callback_t callback; +}st_CallBack_AW9523; + +typedef struct{ + uint32_t spin_direct; + c_callback_t callback; +}st_CallBack_Encoder; + +enum { + POC_CAL_ELEM_INT, + POC_CAL_ELEM_STR, + POC_CAL_ELEM_UINT64, + POC_CAL_ELEM_ARRAY_INT, + POC_CAL_ELEM_FLOAT, +}; + +typedef struct +{ + int type; + void* data; +}st_CallBack_POC_ELEM; + +typedef struct +{ + st_CallBack_POC_ELEM *para; + int len; + c_callback_t callback; +}st_CallBack_POC; + + +enum {RECORE_TYPE_FILE,RECORE_TYPE_STREAM}; +typedef struct{ + int record_type; + char * p_data; + int len; + int res; + c_callback_t callback; +}st_CallBack_AudioRecord; +typedef struct{ + uint32_t event; + uint32_t errcode; + void *valueT; + uint32_t valLen; + c_callback_t call_cb; +}st_CallBack_Queth; + +typedef struct{ + mp_obj_t fun_in; + size_t n_args; + size_t n_kw; + mp_obj_t *args; +}st_CallBack_lvgl; + +typedef struct{ + uint32_t result; + c_callback_t callback; +}st_CallBack_jrtc; + +typedef struct{ + uint8_t event_id; + uint8_t row; + uint8_t col; + c_callback_t callback; +}st_CallBack_Keypad; + +#if MICROPY_QPY_SENSOR_VC9202 +typedef struct{ + uint32_t work_mode; + uint16_t value; + uint32_t measure_mode; + c_callback_t callback; +}st_CallBack_Sensor_VC9202; +#endif +#if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350) +#if MICROPY_QPY_MACHINE_TIMER +typedef struct{ + c_callback_t callback; +}st_CallBack_Timer; +#endif +#endif + +#if defined(BOARD_EC600MCN_LA_POC_XBND) || defined(BOARD_EC600MCN_LE_POC_XBND) || defined(BOARD_EC600MEU_LA_POC_XBND)\ + || defined(BOARD_EC600MLA_LA_POC_XBND) +typedef struct{ + uint8_t speak_op; + int speak_result; + c_callback_t callback; +}st_CallBack_PocSpeak; +#endif +#if MICROPY_QPY_MACHINE_UART +typedef struct{ + uint64_t ind_type; + Helios_UARTNum port; + uint64_t size; + c_callback_t callback; +}st_CallBack_Uart; +#endif + +#if MICROPY_QPY_MODULE_NFC +typedef struct{ + uint16_t oid; + void* data; + uint8_t data_len; + c_callback_t callback; +}st_CallBack_nfc; +#endif + +#if MICROPY_QPY_MODULE_NET +typedef struct{ + int32_t event_id; + int32_t status; + int32_t act; + int32_t lac; + int32_t cid; + c_callback_t callback; +}st_CallBack_Net; +#endif + +#if MICROPY_QPY_MODULE_DATACALL +typedef struct{ + int32_t profile_idx; + uint8_t sim_id; + int32_t nw_status; + c_callback_t callback; +}st_CallBack_Datacall; +#endif + +#if MICROPY_QPY_MODULE_SMS +typedef struct{ + uint8_t sim_id; + uint8_t index; + char storage[5]; + c_callback_t callback; +}st_CallBack_SMS; +#endif + +#if MICROPY_QPY_MODULE_BT || MICROPY_QPY_MODULE_BLE +typedef struct{ + //void *msg; + Helios_Event event; + c_callback_t callback; +}st_CallBack_BT; +#endif + +#if MICROPY_QPY_MODULE_ALIPAY +typedef struct{ + helios_pay_msg_cmd_t cmd; + helios_pay_rsp_code_t rsp_code; + char *result; + int result_len; + void *userdata; + c_callback_t callback; +}st_CallBack_ALIPAY; +#endif + +#if MICROPY_QPY_MODULE_ALIPAY_COLLECTION +typedef struct{ + int msgID; + int data; + c_callback_t callback; +}st_CallBack_ALIPAY_COLLECTION; +#endif + +#if MICROPY_QPY_LPM_WAKEUP +typedef struct{ + uint8_t level; + c_callback_t callback; +} st_CallBack_Wakeup; +#endif + +#if MICROPY_QPY_MODULE_SIF +typedef struct{ + int data_len; + uint8_t *data; + c_callback_t callback; +} st_CallBack_SIF; +#endif + +bool mp_sched_schedule_callback_register(c_callback_t *callback, mp_obj_t func_obj); +bool mp_sched_schedule_ex(c_callback_t *callback, mp_obj_t arg); + +void qpy_callback_deal_init(void); +void qpy_callback_deal_deinit(void); +int qpy_send_msg_to_callback_deal_thread(uint8_t id, void *msg); +bool qpy_is_callback_deal_thread(void); +st_CallBack_LoadBoundMeth * qpy_callback_para_node_add(c_callback_t *callback, mp_obj_t arg); +void qpy_callback_para_node_delete(st_CallBack_LoadBoundMeth *cb_msg); +void qpy_callback_para_link_free_all(void); +void qpy_callback_para_link_mark_in_used(void); +#endif +#endif //__CALLBACKDEAL_H__ \ No newline at end of file diff --git a/ports/quectel/gccollect.c b/ports/quectel/gccollect.c new file mode 100644 index 0000000000000..e72f2678737d8 --- /dev/null +++ b/ports/quectel/gccollect.c @@ -0,0 +1,120 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include + +#include "mpstate.h" +#include "gc.h" +#include "mpthread.h" +#include "gccollect.h" + +#if defined(PLAT_RDA) +unsigned int ReadSP(void) +{ + uint32_t res; + __asm volatile ( + "move %0, $29\n" + :"=r"(res) + : + : + ); + + return res; +} +#elif defined(PLAT_ECR6600) + +#else +unsigned int ReadSP(void) +{ + uint32_t res; + __asm volatile ( + "mov %0, r13\n" + :"=r"(res) + : + : + ); + + return res; +} +#endif + +void gc_stacktop_set(void * ptr) +{ + MP_STATE_PORT(global_stacktop_ptr) = ptr; +} + +void gc_collect(void) { + // get current time, in case we want to time the GC + #if 0 + uint32_t start = mp_hal_ticks_us(); + #endif +#if defined(PLAT_ECR6600) + int val = 0; +#endif + // start the GC + gc_collect_start(); +#if defined(PLAT_ECR6600) + uintptr_t sp = (uintptr_t)&val; +#else + // get the registers and the sp + uintptr_t sp = (uintptr_t)ReadSP(); +#endif + if(mp_is_python_thread()) + { + // trace the stack, including the registers (since they live on the stack in this function) + gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + } + else if(NULL != MP_STATE_PORT(global_stacktop_ptr)) + { + gc_collect_root((void **)sp, ((uint32_t)MP_STATE_PORT(global_stacktop_ptr) - sp) / sizeof(uint32_t)); + } + else + { + //do nothing + } + + // trace root pointers from any threads + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif + + // end the GC + gc_collect_end(); + + #if 0 + // print GC info + uint32_t ticks = mp_hal_ticks_us() - start; + gc_info_t info; + gc_info(&info); + printf("GC@%lu %lums\n", start, ticks); + printf(" " UINT_FMT " total\n", info.total); + printf(" " UINT_FMT " : " UINT_FMT "\n", info.used, info.free); + printf(" 1=" UINT_FMT " 2=" UINT_FMT " m=" UINT_FMT "\n", info.num_1block, info.num_2block, info.max_block); + #endif +} + +MP_REGISTER_ROOT_POINTER(volatile void * global_stacktop_ptr); diff --git a/ports/quectel/gccollect.h b/ports/quectel/gccollect.h new file mode 100644 index 0000000000000..9ce6896e47df7 --- /dev/null +++ b/ports/quectel/gccollect.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MICROPY_INCLUDED_QUECTEL_GCCOLLECT_H__ +#define __MICROPY_INCLUDED_QUECTEL_GCCOLLECT_H__ + +void gc_stacktop_set(void * ptr); +void gc_collect(void); + +//Set a stack address as the end address of the GC collection +//Do not add {} to the macro definition to prevent the scoping of stack_dummy from changing +#define GC_STACKTOP_SET() int stack_dummy;gc_stacktop_set(&stack_dummy); + +//Clears the gc collection end stack pointer for global non-Python threads +#define GC_STACKTOP_CLEAR() gc_stacktop_set(NULL); + +#endif + diff --git a/ports/quectel/help.c b/ports/quectel/help.c new file mode 100644 index 0000000000000..fadb2f60e5195 --- /dev/null +++ b/ports/quectel/help.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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/builtin.h" + +const char quecpython_help_text[] = + "Welcome to QuecPython! The Quecpython is a \n" + "small microcontroller that runs MicroPython.\n" + "\n" + "For online help please visit https://python.quectel.com/wiki \n" + "\n" + "Control commands:\n" + " CTRL-A -- on a blank line, enter raw REPL mode\n" + " CTRL-B -- on a blank line, enter normal REPL mode\n" + " CTRL-C -- interrupt a running program\n" + " CTRL-D -- on a blank line, do a soft reset of the board\n" + " CTRL-E -- on a blank line, enter paste mode\n" + "\n" + "For further help on a specific object, type help(obj)\n" + "For a list of available modules, type help('modules')\n" +; diff --git a/ports/quectel/machine_extint.c b/ports/quectel/machine_extint.c new file mode 100644 index 0000000000000..6eabf508f6889 --- /dev/null +++ b/ports/quectel/machine_extint.c @@ -0,0 +1,734 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include + +#include "runtime.h" +#include "gc.h" +#include "mphal.h" +#include "gccollect.h" + +#if MICROPY_QPY_MACHINE_EXTINT +#include "helios_pin.h" +#include "helios_extint.h" +#include "helios_debug.h" +#include "callbackdeal.h" +#include "helios_os.h" + +//#if defined(PLAT_EIGEN) +static Helios_Thread_t thread_id = 0; +static Helios_MsgQ_t extint_queue = 0; +typedef struct +{ + uint8_t msg_type; + uint8_t pin; + uint8_t edge; + void* cb; +}extint_Msg; + +//#endif +#if 1 +#define EXTINT_LOG(msg, ...) custom_log("extint", msg, ##__VA_ARGS__) +#else +#define EXTINT_LOG(msg, ...) +#endif +/* sample time (ms) */ +#define EXTINT_SAMPLE_TIME (10) + +extern int Helios_GPIO_GetLevel(Helios_GPIONum gpio_num); +const mp_obj_type_t machine_extint_type; + +#if defined(BOARD_EG810MCN_GA_ALIPAY) || defined(BOARD_EG810MCN_GA_VOLTE) +static int __fastIntGpio = 0xffff; +#endif + +typedef struct { + mp_obj_base_t base; + mp_int_t line; + mp_int_t mode; + mp_int_t pull; + mp_int_t fliter_time; + c_callback_t callback; +} extint_obj_t; + + +typedef struct { + mp_int_t rising_count; + mp_int_t falling_count; +} extint_count_t; + +#if defined(PLAT_aic8800m40) +typedef void(*eint_handler_t)(int EDGE); +#else +typedef void(*eint_handler_t)(void); + +#endif + +static extint_obj_t *extint_obj[HELIOS_GPIOMAX]; +static extint_count_t extint_count[HELIOS_GPIOMAX] = {0}; +static Helios_OSTimer_t extint_fliter_timer[HELIOS_GPIOMAX] = {0}; +static uint8_t extint_fliter_timer_is_start[HELIOS_GPIOMAX] = {0}; + +enum { + HELIOS_EXTINT_RISING, + HELIOS_EXTINT_FALLING, +}; + +#define EINT_HANDLER_DEF(n) handler##n +#define PLAT_EINT_HANDLER_DEF(n) BOOST_PP_REPEAT_1(n,EINT_HANDLER_DEF) + +/* +** Jayceon-20200908: +** Replace function mp_call_function_1_protected() with mp_sched_schedule_ex to slove the dump problem. +*/ +#if MICROPY_ENABLE_CALLBACK_DEAL +#define EXTINT_CALLBACK_OP(X, edge) do{ \ + st_CallBack_Extint *extint = malloc(sizeof(st_CallBack_Extint)); \ + if(NULL != extint) { \ + extint->pin_no = X; \ + extint->edge = edge; \ + extint->callback = extint_obj[X]->callback; \ + if(qpy_send_msg_to_callback_deal_thread(CALLBACK_TYPE_ID_EXTINT, extint)) \ + { \ + free(extint); \ + } \ + } \ + }while(0) +#else +#define EXTINT_CALLBACK_OP(X, edge) do{ \ + GC_STACKTOP_SET(); \ + mp_obj_t extint_list[2] = { \ + mp_obj_new_int(X), \ + mp_obj_new_int(edge), \ + }; \ + mp_sched_schedule(&extint_obj[X]->callback, mp_obj_new_list(2, extint_list)); \ + GC_STACKTOP_CLEAR(); \ + }while(0) + +#endif + + +#if defined(PLAT_aic8800m40) +#define HANDLER_FUN(X) \ +static void fliter_timer_handler##X(void *args) { \ + int edge = (int)args; \ + static uint32_t extint_fliter_count = 0; \ + uint32_t extint_fliter_count_max = extint_obj[X]->fliter_time / EXTINT_SAMPLE_TIME; \ + int level = Helios_GPIO_GetLevel(X); \ + switch (edge) { \ + case HELIOS_EXTINT_RISING: \ + if (level != 1) { \ + extint_fliter_count = 0; \ + } \ + break; \ + case HELIOS_EXTINT_FALLING: \ + if (level != 0) { \ + extint_fliter_count = 0; \ + } \ + break; \ + } \ + if (++extint_fliter_count > extint_fliter_count_max) { \ + Helios_OSTimer_Stop(extint_fliter_timer[X]); \ + extint_fliter_count = 0; \ + extint_fliter_timer_is_start[X] = false; \ + EXTINT_CALLBACK_OP(X, edge); \ + } \ +} \ +static void handler##X(int EDGE) \ +{ \ + int edge = EDGE; \ + if (extint_obj[X]->callback.cb != mp_const_none && \ + ((mp_sched_num_pending() < MICROPY_SCHEDULER_DEPTH))) \ + { \ + if (extint_obj[X]->fliter_time > 0 && !extint_fliter_timer_is_start[X]) { \ + if (!extint_fliter_timer[X]) { \ + extint_fliter_timer[X] = Helios_OSTimer_Create(); \ + if (!extint_fliter_timer[X]) { \ + EXTINT_LOG("error: extint fliter timer create failed!"); \ + EXTINT_CALLBACK_OP(X, edge); \ + } \ + } else { \ + Helios_OSTimerAttr attr = { \ + .ms = EXTINT_SAMPLE_TIME, \ + .cycle_enable = 1, \ + .cb = fliter_timer_handler##X, \ + .argv = (void*)edge \ + }; \ + Helios_OSTimer_Start(extint_fliter_timer[X], &attr); \ + extint_fliter_timer_is_start[X] = true; \ + } \ + } else if(extint_obj[X]->fliter_time == 0){ \ + EXTINT_CALLBACK_OP(X, edge); \ + } \ + } \ + Helios_ExtInt_Enable(extint_obj[X]->line); \ +} +#else +#define HANDLER_FUN(X) \ +static void fliter_timer_handler##X(void *args) { \ + int edge = (int)args; \ + static uint32_t extint_fliter_count = 0; \ + uint32_t extint_fliter_count_max = extint_obj[X]->fliter_time / EXTINT_SAMPLE_TIME; \ + int level = Helios_GPIO_GetLevel(X); \ + switch (edge) { \ + case HELIOS_EXTINT_RISING: \ + if (level != 1) { \ + Helios_OSTimer_Stop(extint_fliter_timer[X]); \ + extint_fliter_count = 0; \ + extint_fliter_timer_is_start[X] = false;\ + } \ + break; \ + case HELIOS_EXTINT_FALLING: \ + if (level != 0) { \ + Helios_OSTimer_Stop(extint_fliter_timer[X]); \ + extint_fliter_count = 0; \ + extint_fliter_timer_is_start[X] = false;\ + } \ + break; \ + } \ + if (++extint_fliter_count > extint_fliter_count_max) { \ + Helios_OSTimer_Stop(extint_fliter_timer[X]); \ + extint_fliter_count = 0; \ + extint_fliter_timer_is_start[X] = false; \ + EXTINT_CALLBACK_OP(X, edge); \ + if(edge == HELIOS_EXTINT_RISING) \ + { \ + extint_count[X].rising_count++; \ + } \ + else \ + { \ + extint_count[X].falling_count++; \ + } \ + } \ + Helios_ExtInt_Enable(extint_obj[X]->line); \ +} \ +static void handler##X(void) \ +{ \ + int edge = HELIOS_EXTINT_RISING;\ + if(extint_obj[X]->mode == HELIOS_EDGE_RISING) \ + { \ + if(extint_obj[X]->fliter_time == 0) \ + { \ + extint_count[X].rising_count++; \ + } \ + } \ + else if(extint_obj[X]->mode == HELIOS_EDGE_FALLING) \ + { \ + edge = HELIOS_EXTINT_FALLING; \ + if(extint_obj[X]->fliter_time == 0) \ + { \ + extint_count[X].falling_count++; \ + } \ + } \ + else \ + { \ + if(Helios_GPIO_GetLevel((Helios_GPIONum)X) == 0) \ + { \ + edge = HELIOS_EXTINT_FALLING; \ + if(extint_obj[X]->fliter_time == 0) \ + { \ + extint_count[X].falling_count++; \ + } \ + } \ + else \ + { \ + if(extint_obj[X]->fliter_time == 0) \ + { \ + extint_count[X].rising_count++; \ + } \ + } \ + } \ + if (extint_obj[X]->callback.cb != mp_const_none && \ + ((mp_sched_num_pending() < MICROPY_SCHEDULER_DEPTH))) \ + { \ + if (extint_obj[X]->fliter_time > 0 && !extint_fliter_timer_is_start[X]) { \ + extint_fliter_timer_is_start[X] = true;\ + if(extint_queue && thread_id) \ + { \ + extint_Msg msg; \ + msg.msg_type = 1;\ + msg.pin = X; \ + msg.edge = edge; \ + msg.cb = fliter_timer_handler##X;\ + Helios_MsgQ_Put(extint_queue, (void *)&msg, sizeof(extint_Msg), HELIOS_NO_WAIT);\ + } \ + else\ + {\ + Helios_OSTimerAttr attr = \ + { \ + .ms = EXTINT_SAMPLE_TIME,\ + .cycle_enable = 1, \ + .cb = fliter_timer_handler##X, \ + .argv = (void*)edge \ + }; \ + if(extint_fliter_timer[X])\ + {\ + Helios_OSTimer_Start(extint_fliter_timer[X], &attr); \ + }\ + }\ + } else if(extint_obj[X]->fliter_time == 0){\ + if(extint_queue && thread_id) \ + { \ + extint_Msg msg; \ + msg.msg_type = 0;\ + msg.pin = X; \ + msg.edge = edge; \ + Helios_MsgQ_Put(extint_queue, (void *)&msg, sizeof(extint_Msg), HELIOS_NO_WAIT);\ + } \ + else \ + { \ + EXTINT_CALLBACK_OP(X, edge); \ + } \ + } \ + } \ + if((extint_obj[X]->fliter_time == 0) && !(extint_queue && thread_id)) \ + { \ + Helios_ExtInt_Enable(extint_obj[X]->line); \ + } \ +} +#endif + + +HANDLER_FUN_0_N(PLAT_GPIO_NUM) + + + + +eint_handler_t eint_handler[HELIOS_GPIOMAX] = { + handler0, PLAT_EINT_HANDLER_DEF(PLAT_GPIO_NUM) +}; + + + +static void extint_count_reset(int offset) { + extint_count[offset].falling_count = 0; + extint_count[offset].rising_count = 0; +} + +/// \method line() +/// Return the line number that the pin is mapped to. +static mp_obj_t extint_obj_line(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->line); +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_line_obj, extint_obj_line); + +/// \method enable() +/// Enable a disabled interrupt. +static mp_obj_t extint_obj_enable(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ret = Helios_ExtInt_Enable((Helios_GPIONum) self->line); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable); + +/// \method disable() +/// Disable the interrupt associated with the ExtInt object. +/// This could be useful for debouncing. +static mp_obj_t extint_obj_disable(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ret = Helios_ExtInt_Disable((Helios_GPIONum) self->line); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable); + +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) +static void extint_entry(void * argv) +{ + extint_Msg msg; + int edge; + while(1) + { + Helios_MsgQ_Get(extint_queue, (void *)&msg, sizeof(extint_Msg), HELIOS_WAIT_FOREVER); + edge = msg.edge; + if(msg.msg_type == 1)//start timer + { + if(extint_fliter_timer[msg.pin]) + { + Helios_OSTimerAttr attr = + { + .ms = EXTINT_SAMPLE_TIME, + .cycle_enable = 1, + .cb = (void(*)(void *))msg.cb, + .argv = (void*)edge + }; + if(Helios_OSTimer_Start(extint_fliter_timer[msg.pin], &attr))//eigen 618 plat,we cant call Helios_OSTimer_Start in IRQ + { + } + else + { + } + } + } + else + { + EXTINT_CALLBACK_OP(msg.pin,edge); + Helios_ExtInt_Enable(msg.pin); + } + } +} +#endif +/// \classmethod \constructor(pin, mode, pull, callback) +/// Create an ExtInt object: +/// +/// - `pin` is the pin on which to enable the interrupt (can be a pin object or any valid pin name). +/// - `mode` can be one of: +/// - `ExtInt.IRQ_RISING` - trigger on a rising edge; +/// - `ExtInt.IRQ_FALLING` - trigger on a falling edge; +/// - `ExtInt.IRQ_RISING_FALLING` - trigger on a rising or falling edge. +/// - `pull` can be one of: +/// - `pyb.Pin.PULL_NONE` - no pull up or down resistors; +/// - `pyb.Pin.PULL_UP` - enable the pull-up resistor; +/// - `pyb.Pin.PULL_DOWN` - enable the pull-down resistor. +/// - `callback` is the function to call when the interrupt triggers. The +/// callback function must accept exactly 1 argument, which is the line that +/// triggered the interrupt. +static const mp_arg_t pyb_extint_make_new_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = 0} }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_pull, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_callback, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, +#if defined(BOARD_EG810MCN_GA_ALIPAY) || defined(BOARD_EG810MCN_GA_VOLTE) + { MP_QSTR_fast, MP_ARG_INT, {.u_int = 0} }, +#endif + { MP_QSTR_filter_time, MP_ARG_INT, {.u_int = 0} }, +}; +#define PYB_EXTINT_MAKE_NEW_NUM_ARGS MP_ARRAY_SIZE(pyb_extint_make_new_args) + +static mp_obj_t extint_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // type_in == extint_obj_type + // parse args + mp_arg_val_t vals[PYB_EXTINT_MAKE_NEW_NUM_ARGS]; + mp_arg_parse_all_kw_array(n_args, n_kw, args, PYB_EXTINT_MAKE_NEW_NUM_ARGS, pyb_extint_make_new_args, vals); + +#if !defined(PLAT_ECR6600) + if (vals[0].u_int < HELIOS_GPIO0 || vals[0].u_int > HELIOS_GPIOMAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin value")); + } +#endif + + if (extint_obj[vals[0].u_int] == NULL) + { + extint_obj[vals[0].u_int] = mp_obj_malloc_with_finaliser(extint_obj_t, &machine_extint_type); + } + extint_obj_t *self = extint_obj[vals[0].u_int]; + self->base.type = type; + self->line = vals[0].u_int; + + self->mode = vals[1].u_int; + if (self->mode < HELIOS_EDGE_RISING || self->mode > HELIOS_EDGE_BOTH) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid mode value")); + } + + self->pull = vals[2].u_int; + if (self->pull < HELIOS_PULL_NONE || self->pull > HELIOS_PULL_DOWN) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid pull value")); + } + + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + cb.arg = mp_obj_new_tuple(2, NULL); + self->callback = cb; + mp_sched_schedule_callback_register(&self->callback, vals[3].u_obj); + // EXTINT_LOG("ds cb 457!\n"); + + Helios_ExtIntStruct extint_struct = {0}; + extint_struct.gpio_trigger = HELIOS_EDGE_TRIGGER; + extint_struct.gpio_edge = self->mode; + extint_struct.gpio_debounce = HELIOS_DEBOUNCE_EN; + extint_struct.gpio_pull = self->pull; + extint_struct.eint_cb = NULL; + extint_struct.wakeup_eint_cb = eint_handler[self->line]; + + // EXTINT_LOG("%x\n", extint_struct.wakeup_eint_cb); + + Helios_ExtInt_Deinit((Helios_GPIONum) self->line); + + if(0 != Helios_ExtInt_Init((Helios_GPIONum) self->line, &extint_struct)) { + mp_raise_ValueError(MP_ERROR_TEXT("Interrupt initialization failed")); + } + +#if defined(BOARD_EG810MCN_GA_ALIPAY) || defined(BOARD_EG810MCN_GA_VOLTE) + if(vals[4].u_int == 1) { + __fastIntGpio = self->line; + Helios_ExtInt_FastSetGpio(__fastIntGpio); + } else { + if(__fastIntGpio == self->line) { + __fastIntGpio = 0xffff; + Helios_ExtInt_FastSetGpio(__fastIntGpio); + } + } + self->fliter_time = vals[5].u_int; +#else + self->fliter_time = vals[4].u_int; +#endif + + if (self->fliter_time < 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid fliter time value")); + } + + if(self->fliter_time > 0) + { + if(!extint_fliter_timer[self->line]) + { + extint_fliter_timer[self->line] = Helios_OSTimer_Create(); + if (!extint_fliter_timer[self->line]) + { + mp_raise_ValueError(MP_ERROR_TEXT("error: extint fliter timer create failed!")); + } + } + } +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + + if(extint_queue == 0) + { + extint_queue = Helios_MsgQ_Create(256, sizeof(extint_Msg)); + if(extint_queue == 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("initialization failed")); + } + } + + if(thread_id == 0) + { + Helios_ThreadAttr ThreadAttr = + { + .name = "machine_extint", + .stack_size = 2048, + .priority = 92, + .entry = extint_entry, + .argv = NULL + }; + thread_id = Helios_Thread_Create(&ThreadAttr); + if(thread_id == 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("initialization failed")); + } + } + +#endif + extint_count_reset(self->line); + return MP_OBJ_FROM_PTR(self); +} + +static void extint_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->line); +} + +static mp_obj_t extint_obj_read_count(mp_obj_t self_in, mp_obj_t is_reset) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + int reset_flag = mp_obj_get_int(is_reset); + + if(reset_flag != 0 && reset_flag != 1) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid is_reset value, must in [0,1]")); + } + +#if defined(BOARD_EG810MCN_GA_ALIPAY) || defined(BOARD_EG810MCN_GA_VOLTE) + int count = 0; + if(__fastIntGpio == self->line) { + count += Helios_ExtInt_FastGetCnt(); + if(extint_obj[self->line]->mode == HELIOS_EDGE_RISING){ + extint_count[self->line].rising_count += count; + } else if(extint_obj[self->line]->mode == HELIOS_EDGE_FALLING) { + extint_count[self->line].falling_count += count; + } else { + if(count % 2 == 0) { + extint_count[self->line].rising_count += count / 2; + extint_count[self->line].falling_count += count / 2; + } else { + if(Helios_GPIO_GetLevel(self->line) == 0) { + extint_count[self->line].rising_count++; + } else { + extint_count[self->line].falling_count++; + } + } + } + } +#endif + + mp_obj_t extint_list[2] = { + mp_obj_new_int(extint_count[self->line].rising_count), + mp_obj_new_int(extint_count[self->line].falling_count), + }; + + if(1 == reset_flag) { + extint_count_reset(self->line); + } + return mp_obj_new_list(2, extint_list); +} +static MP_DEFINE_CONST_FUN_OBJ_2(extint_obj_read_count_obj, extint_obj_read_count); + +static mp_obj_t extint_obj_count_reset(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + extint_count_reset(self->line); + + return mp_obj_new_int(0); + +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_count_reset_obj, extint_obj_count_reset); + +static mp_obj_t extint_obj_Deinit(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ret = -1; + //EXTINT_LOG("extint deinit"); + + extint_obj[self->line] = NULL; + extint_count_reset(self->line); + + if((0 == Helios_ExtInt_Disable((Helios_GPIONum) self->line)) && (0 == Helios_ExtInt_Deinit((Helios_GPIONum) self->line))) { + ret = 0; + } + + return mp_obj_new_int(ret); + +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint__del__obj, extint_obj_Deinit); + +static mp_obj_t extint_obj_read_level(mp_obj_t self_in) { + extint_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ret = -1; + + ret = Helios_GPIO_GetLevel((Helios_GPIONum) self->line); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_read_level_obj, extint_obj_read_level); + + +static const mp_rom_map_elem_t extint_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&extint__del__obj) }, + { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&extint_obj_line_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&extint_obj_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&extint_obj_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_count), MP_ROM_PTR(&extint_obj_read_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_count_reset), MP_ROM_PTR(&extint_obj_count_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_level), MP_ROM_PTR(&extint_obj_read_level_obj)}, + + // class constants + /// \constant IRQ_RISING - interrupt on a rising edge + /// \constant IRQ_FALLING - interrupt on a falling edge + /// \constant IRQ_RISING_FALLING - interrupt on a rising or falling edge + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(HELIOS_EDGE_RISING) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(HELIOS_EDGE_FALLING) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_ROM_INT(HELIOS_EDGE_BOTH) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DISABLE), MP_ROM_INT(HELIOS_PULL_NONE) }, + { MP_ROM_QSTR(MP_QSTR_PULL_PU), MP_ROM_INT(HELIOS_PULL_UP) }, + { MP_ROM_QSTR(MP_QSTR_PULL_PD), MP_ROM_INT(HELIOS_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_GPIO0), MP_ROM_INT(HELIOS_GPIO0) }, +#if defined(PLAT_Qualcomm) +#if defined(BOARD_BG95M1) || defined(BOARD_BG95M2) || defined(BOARD_BG95M3) \ + || defined(BOARD_BG95M8) || defined(BOARD_BG95M9) || defined(BOARD_BG95M3_LX) \ + || defined(BOARD_BG95M6) || defined(BOARD_BG95M8_SANX) + { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_INT(HELIOS_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GPIO3), MP_ROM_INT(HELIOS_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GPIO6), MP_ROM_INT(HELIOS_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GPIO7), MP_ROM_INT(HELIOS_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_INT(HELIOS_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GPIO9), MP_ROM_INT(HELIOS_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GPIO11), MP_ROM_INT(HELIOS_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GPIO12), MP_ROM_INT(HELIOS_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GPIO14), MP_ROM_INT(HELIOS_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GPIO16), MP_ROM_INT(HELIOS_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GPIO17), MP_ROM_INT(HELIOS_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GPIO18), MP_ROM_INT(HELIOS_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GPIO19), MP_ROM_INT(HELIOS_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GPIO20), MP_ROM_INT(HELIOS_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GPIO21), MP_ROM_INT(HELIOS_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_GPIO22), MP_ROM_INT(HELIOS_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GPIO23), MP_ROM_INT(HELIOS_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_GPIO24), MP_ROM_INT(HELIOS_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_GPIO25), MP_ROM_INT(HELIOS_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_GPIO26), MP_ROM_INT(HELIOS_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GPIO29), MP_ROM_INT(HELIOS_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_GPIO30), MP_ROM_INT(HELIOS_GPIO30) }, +#elif defined(BOARD_BG77) + { MP_ROM_QSTR(MP_QSTR_GPIO1), MP_ROM_INT(HELIOS_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_INT(HELIOS_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GPIO4), MP_ROM_INT(HELIOS_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GPIO5), MP_ROM_INT(HELIOS_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_INT(HELIOS_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GPIO10), MP_ROM_INT(HELIOS_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GPIO13), MP_ROM_INT(HELIOS_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GPIO14), MP_ROM_INT(HELIOS_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GPIO17), MP_ROM_INT(HELIOS_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GPIO20), MP_ROM_INT(HELIOS_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GPIO21), MP_ROM_INT(HELIOS_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GPIO22), MP_ROM_INT(HELIOS_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GPIO25), MP_ROM_INT(HELIOS_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_GPIO28), MP_ROM_INT(HELIOS_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GPIO31), MP_ROM_INT(HELIOS_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_GPIO33), MP_ROM_INT(HELIOS_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GPIO34), MP_ROM_INT(HELIOS_GPIO34) }, + + { MP_ROM_QSTR(MP_QSTR_GPIO35), MP_ROM_INT(HELIOS_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_GPIO36), MP_ROM_INT(HELIOS_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_GPIO39), MP_ROM_INT(HELIOS_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_GPIO40), MP_ROM_INT(HELIOS_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_GPIO42), MP_ROM_INT(HELIOS_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_GPIO43), MP_ROM_INT(HELIOS_GPIO43) }, +#elif defined(BOARD_BG600LM3) + { MP_ROM_QSTR(MP_QSTR_GPIO1), MP_ROM_INT(HELIOS_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_INT(HELIOS_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GPIO3), MP_ROM_INT(HELIOS_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GPIO6), MP_ROM_INT(HELIOS_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GPIO7), MP_ROM_INT(HELIOS_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_INT(HELIOS_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GPIO10), MP_ROM_INT(HELIOS_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GPIO11), MP_ROM_INT(HELIOS_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GPIO15), MP_ROM_INT(HELIOS_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GPIO16), MP_ROM_INT(HELIOS_GPIO16) }, + + { MP_ROM_QSTR(MP_QSTR_GPIO17), MP_ROM_INT(HELIOS_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GPIO18), MP_ROM_INT(HELIOS_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GPIO19), MP_ROM_INT(HELIOS_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GPIO20), MP_ROM_INT(HELIOS_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GPIO23), MP_ROM_INT(HELIOS_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_GPIO25), MP_ROM_INT(HELIOS_GPIO25) }, +#endif +#else +#if !defined(PLAT_ECR6600) + PLAT_GPIO_DEF(PLAT_GPIO_NUM), +#endif +#endif +}; + +static MP_DEFINE_CONST_DICT(extint_locals_dict, extint_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_extint_type, + MP_QSTR_ExtInt, + MP_TYPE_FLAG_NONE, + make_new, extint_make_new, + print, extint_obj_print, + locals_dict, &extint_locals_dict + ); +#endif diff --git a/ports/quectel/machine_hw_spi.c b/ports/quectel/machine_hw_spi.c new file mode 100644 index 0000000000000..6a6c79460ae5b --- /dev/null +++ b/ports/quectel/machine_hw_spi.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "mphalport.h" + +#if MICROPY_QPY_MACHINE_SPI + +#include "helios_spi.h" +#include "helios_debug.h" + + +#define HELIOS_SPI_LOG(msg, ...) custom_log("machine spi", msg, ##__VA_ARGS__) + + +const mp_obj_type_t machine_hard_spi_type; + +typedef struct _machine_hard_spi_obj_t { + mp_obj_base_t base; + uint32_t port; + uint32_t mode; + uint32_t clk; +} machine_hard_spi_obj_t; + +static void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_hard_spi_obj_t *self = self_in; + mp_printf(print, "spi%d", self->port); +} + +mp_obj_t machine_hard_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_port, ARG_mode, ARG_clk }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_port, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_clk, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint32_t port = args[ARG_port].u_int; + uint32_t mode = args[ARG_mode].u_int; + uint32_t clk = args[ARG_clk].u_int; + int ret=0; + + if (port > HELIOS_SPI3) { + mp_raise_ValueError(MP_ERROR_TEXT("port must be (0~3)")); + } + + if ( mode > 3) { + mp_raise_ValueError(MP_ERROR_TEXT("mode must be (0~3)")); + } + +#if defined(PLAT_Unisoc) + if (clk > 9) { + mp_raise_ValueError(MP_ERROR_TEXT("clk must be (0~9)")); + } +#elif defined(PLAT_RDA) + if (clk > 39) { + mp_raise_ValueError(MP_ERROR_TEXT("clk must be (0~39)")); + } +#elif defined(PLAT_Unisoc_8850) +if (clk > 14) +{ + mp_raise_ValueError(MP_ERROR_TEXT("clk must be (0~14)")); +} +#elif defined(PLAT_SONY_ALT1350) +if (clk > 30) +{ + mp_raise_ValueError(MP_ERROR_TEXT("clk must be (0~30)")); +} +#else + if (clk > 6) { + mp_raise_ValueError(MP_ERROR_TEXT("clk must be (0~6)")); + } +#endif + + + machine_hard_spi_obj_t *self = m_new_obj(machine_hard_spi_obj_t); + + self->base.type = &machine_hard_spi_type; + self->port = port; + self->mode = mode; + self->clk = clk; + + ret = Helios_SPI_Init((Helios_SPINum) self->port, (Helios_SPIMode) self->mode, (uint32_t) self->clk); + if(ret != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("spi init fail")); + } + + HELIOS_SPI_LOG("Helios_SPI_Init %d\r\n",ret); + return MP_OBJ_FROM_PTR(self); +} + +static const mp_arg_t machine_spi_mem_allowed_args[] = { + { MP_QSTR_databuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, +}; +static mp_obj_t machine_spi_write_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_databuf, ARG_datasize }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_spi_mem_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_spi_mem_allowed_args), machine_spi_mem_allowed_args, args); + + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + // get the buffer to write the data from + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_databuf].u_obj, &bufinfo, MP_BUFFER_READ); + + int length = (size_t)args[ARG_datasize].u_int > bufinfo.len ? bufinfo.len : (size_t)args[ARG_datasize].u_int; + // do the transfer + + int ret = Helios_SPI_Write((Helios_SPINum) self->port, (void*) bufinfo.buf, (size_t) length); + if (ret < 0) { + HELIOS_SPI_LOG("Helios_SPI_Write ret=%d\r\n",ret); + mp_raise_OSError(-ret); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_write_obj, 1, machine_spi_write_mem); + +static mp_obj_t machine_spi_read_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_databuf, ARG_datasize }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_spi_mem_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_spi_mem_allowed_args), machine_spi_mem_allowed_args, args); + + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + // get the buffer to read the data into + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_databuf].u_obj, &bufinfo, MP_BUFFER_WRITE); + + int length = (size_t)args[ARG_datasize].u_int > bufinfo.len ? bufinfo.len : (size_t)args[ARG_datasize].u_int; + + // do the transfer + int ret = Helios_SPI_Read((Helios_SPINum) self->port, (void*) bufinfo.buf, (size_t) length); + if (ret < 0) { + HELIOS_SPI_LOG("spi read ret=%d\r\n",ret); + mp_raise_OSError(-ret); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_read_obj, 1, machine_spi_read_mem); +#ifndef PLAT_SONY_ALT1350 +static const mp_arg_t machine_spi_write_read_mem_allowed_args[] = { + { MP_QSTR_readbuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_writebuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, +}; +static mp_obj_t machine_spi_write_read_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_readbuf, ARG_writebuf, ARG_datasize }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_spi_write_read_mem_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_spi_write_read_mem_allowed_args), machine_spi_write_read_mem_allowed_args, args); + + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + // get the buffer to read the data into + mp_buffer_info_t readbuf; + mp_get_buffer_raise(args[ARG_readbuf].u_obj, &readbuf, MP_BUFFER_WRITE); + mp_buffer_info_t writebuf; + mp_get_buffer_raise(args[ARG_writebuf].u_obj, &writebuf, MP_BUFFER_READ); + + int length = ((size_t)args[ARG_datasize].u_int > readbuf.len ? readbuf.len : (size_t)args[ARG_datasize].u_int); + + + // do the transfer + int ret = Helios_SPI_WriteRead((Helios_SPINum) self->port,(void*) readbuf.buf, (size_t) length,(void*) writebuf.buf,(size_t)args[ARG_datasize].u_int); + + if (ret < 0) { + HELIOS_SPI_LOG("Helios_SPI_WriteRead ret=%d\r\n",ret); + mp_raise_OSError(-ret); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_write_read_obj, 1, machine_spi_write_read_mem); + +#else +static const mp_arg_t machine_spi_half_duplex_write_read_mem_allowed_args[] = { + { MP_QSTR_readbuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_writebuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_write_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_read_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, +}; +static mp_obj_t machine_spi_half_duplex_write_read_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_readbuf, ARG_writebuf, ARG_write_datasize, ARG_read_datasize }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_spi_half_duplex_write_read_mem_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_spi_half_duplex_write_read_mem_allowed_args), machine_spi_half_duplex_write_read_mem_allowed_args, args); + + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + // get the buffer to read the data into + mp_buffer_info_t readbuf; + mp_get_buffer_raise(args[ARG_readbuf].u_obj, &readbuf, MP_BUFFER_WRITE); + mp_buffer_info_t writebuf; + mp_get_buffer_raise(args[ARG_writebuf].u_obj, &writebuf, MP_BUFFER_READ); + + int write_length = ((size_t)args[ARG_write_datasize].u_int > writebuf.len ? writebuf.len : (size_t)args[ARG_write_datasize].u_int); + int read_length = ((size_t)args[ARG_read_datasize].u_int > readbuf.len ? readbuf.len : (size_t)args[ARG_read_datasize].u_int); + + + // do the transfer + int ret = Helios_SPI_WriteRead((Helios_SPINum) self->port,(void*) readbuf.buf, (size_t) read_length,(void*) writebuf.buf,(size_t)write_length); + + if (ret < 0) { + HELIOS_SPI_LOG("Helios_SPI_WriteRead ret=%d\r\n",ret); + mp_raise_OSError(-ret); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_half_duplex_write_read_obj, 1, machine_spi_half_duplex_write_read_mem); +#endif +static const mp_rom_map_elem_t machine_spi_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_spi_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_spi_write_obj) }, +#ifndef PLAT_SONY_ALT1350 + { MP_ROM_QSTR(MP_QSTR_write_read), MP_ROM_PTR(&machine_spi_write_read_obj) }, +#else + { MP_ROM_QSTR(MP_QSTR_half_duplex_write_read), MP_ROM_PTR(&machine_spi_half_duplex_write_read_obj) }, +#endif +#if !defined(BOARD_EC800GCN_GA) + { MP_ROM_QSTR(MP_QSTR_SPI0), MP_ROM_INT(HELIOS_SPI0) }, +#endif +#if !defined(PLAT_RDA) && !defined(BOARD_EG915UEU_AB) + { MP_ROM_QSTR(MP_QSTR_SPI1), MP_ROM_INT(HELIOS_SPI1) }, +#endif +}; + +MP_DEFINE_CONST_DICT(mp_machine_hard_spi_locals_dict, machine_spi_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_hard_spi_type, + MP_QSTR_SPI, + MP_TYPE_FLAG_NONE, + make_new, machine_hard_spi_make_new, + print, machine_hard_spi_print, + locals_dict, &mp_machine_hard_spi_locals_dict + ); + +#endif diff --git a/ports/quectel/machine_i2c.h b/ports/quectel/machine_i2c.h new file mode 100644 index 0000000000000..a3363d4c341c5 --- /dev/null +++ b/ports/quectel/machine_i2c.h @@ -0,0 +1,90 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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. + */ +#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H +#define MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H + +#include "py/obj.h" +#include "py/mphal.h" + +// Temporary support for legacy construction of SoftI2C via I2C type. +#define MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args) \ + do { \ + if (n_args == 0 || all_args[0] == MP_OBJ_NEW_SMALL_INT(-1)) { \ + mp_print_str(MICROPY_ERROR_PRINTER, "Warning: I2C(-1, ...) is deprecated, use SoftI2C(...) instead\n"); \ + if (n_args != 0) { \ + --n_args; \ + ++all_args; \ + } \ + return mp_machine_soft_i2c_type.make_new(&mp_machine_soft_i2c_type, n_args, n_kw, all_args); \ + } \ + } while (0) + +#define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write +#define MP_MACHINE_I2C_FLAG_STOP (0x02) + +#if MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 +// If set, the first mp_machine_i2c_buf_t in a transfer is a write. +#define MP_MACHINE_I2C_FLAG_WRITE1 (0x04) +#endif + +typedef struct _mp_machine_i2c_buf_t { + size_t len; + uint8_t *buf; +} mp_machine_i2c_buf_t; + +// I2C protocol +// - init must be non-NULL +// - start/stop/read/write can be NULL, meaning operation is not supported +// - transfer must be non-NULL +// - transfer_single only needs to be set if transfer=mp_machine_i2c_transfer_adaptor +typedef struct _mp_machine_i2c_p_t { + #if MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 + bool transfer_supports_write1; + #endif + void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + int (*start)(mp_obj_base_t *obj); + int (*stop)(mp_obj_base_t *obj); + int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack); + int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len); + int (*transfer)(mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); + int (*transfer_single)(mp_obj_base_t *obj, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags); +} mp_machine_i2c_p_t; + +typedef struct _mp_machine_soft_i2c_obj_t { + mp_obj_base_t base; + uint32_t us_delay; + uint32_t us_timeout; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; +} mp_machine_soft_i2c_obj_t; + +extern const mp_obj_type_t mp_machine_soft_i2c_type; +extern const mp_obj_dict_t mp_machine_i2c_locals_dict; + +int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); +int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); + +#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H diff --git a/ports/quectel/machine_iic.c b/ports/quectel/machine_iic.c new file mode 100644 index 0000000000000..8cd9408fae2e6 --- /dev/null +++ b/ports/quectel/machine_iic.c @@ -0,0 +1,188 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "py/mperrno.h" + +#if MICROPY_QPY_MACHINE_I2C + +#include "machine_i2c.h" +#include "helios_iic.h" +#include "mphalport.h" + + +const mp_obj_type_t machine_hard_i2c_type; + +typedef struct _machine_hard_i2c_obj_t { + mp_obj_base_t base; + uint32_t bus_id; + uint32_t fastmode; +} machine_hard_i2c_obj_t; + +static void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_hard_i2c_obj_t *self = self_in; + mp_printf(print, "I2C%d", self->bus_id); +} + +mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_fastmode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_fastmode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 1} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint32_t bus_id = args[ARG_id].u_int; + uint32_t fastmode = args[ARG_fastmode].u_int; + + + if (bus_id > HELIOS_I2C3) { + mp_raise_ValueError(MP_ERROR_TEXT("bus id must be (0~3)")); + } + + volatile machine_hard_i2c_obj_t *self = m_new_obj(machine_hard_i2c_obj_t); + + self->base.type = &machine_hard_i2c_type; + self->bus_id = bus_id; + self->fastmode = fastmode; + if(0 != Helios_I2C_Init((Helios_I2CEnum) self->bus_id, (Helios_I2CMode) self->fastmode)) { + return mp_const_false; + } + return MP_OBJ_FROM_PTR(self); +} +static const mp_arg_t machine_i2c_mem_allowed_args[] = { + { MP_QSTR_slaveaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_regaddr, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_regaddr_len, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_databuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 8} }, +}; +static mp_obj_t machine_i2c_write_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_slaveaddr, ARG_regaddr, ARG_regaddr_len, ARG_databuf, ARG_datasize }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); + + machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + mp_buffer_info_t regaddr_bufinfo; + mp_get_buffer_raise(args[ARG_regaddr].u_obj, ®addr_bufinfo, MP_BUFFER_READ); + int regaddr_length = args[ARG_regaddr_len].u_int; + + // get the buffer to write the data from + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_databuf].u_obj, &bufinfo, MP_BUFFER_READ); + int length = (size_t)args[ARG_datasize].u_int > bufinfo.len ? bufinfo.len : (size_t)args[ARG_datasize].u_int; + //uart_printf("i2c wirte: busid:%x slaveddr:%x, regaddr:%x, data:%s datalen:%d\n", self->bus_id, args[ARG_slaveaddr].u_int, args[ARG_regaddr].u_int, + // bufinfo.buf, args[ARG_datasize].u_int); + // do the transfer + int ret = HELIOS_I2C_Write((Helios_I2CEnum) self->bus_id, (uint8_t) args[ARG_slaveaddr].u_int, (uint8_t*) regaddr_bufinfo.buf,(size_t) regaddr_length,(void*) bufinfo.buf, (size_t) length); + if (ret < 0) { + ret = -1; + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_write_obj, 1, machine_i2c_write_mem); + +static const mp_arg_t machine_i2c_mem_allowed_read_args[] = { + { MP_QSTR_slaveaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_regaddr, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_regaddr_len, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_databuf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_datasize, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_dalay, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 8} }, +}; +static mp_obj_t machine_i2c_read_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_slaveaddr, ARG_regaddr, ARG_regaddr_len, ARG_databuf, ARG_datasize, ARG_dalay }; + mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_read_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(machine_i2c_mem_allowed_read_args), machine_i2c_mem_allowed_read_args, args); + + machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t *)MP_OBJ_TO_PTR(pos_args[0]); + + mp_buffer_info_t regaddr_bufinfo; + mp_get_buffer_raise(args[ARG_regaddr].u_obj, ®addr_bufinfo, MP_BUFFER_READ); + int regaddr_length = (size_t)args[ARG_regaddr_len].u_int > regaddr_bufinfo.len ? regaddr_bufinfo.len : (size_t)args[ARG_regaddr_len].u_int; + + // get the buffer to read the data into + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_databuf].u_obj, &bufinfo, MP_BUFFER_WRITE); + + int length = (size_t)args[ARG_datasize].u_int > bufinfo.len ? bufinfo.len : (size_t)args[ARG_datasize].u_int; + + //uart_printf("i2c read: busid:%x slaveddr:%x, regaddr:%x, datalen:%d\n", self->bus_id, args[ARG_slaveaddr].u_int, args[ARG_regaddr].u_int, + // args[ARG_datasize].u_int); + // do the transfer + int ret = HELIOS_I2C_Read((Helios_I2CEnum) self->bus_id, (uint8_t) args[ARG_slaveaddr].u_int,(uint8_t*) regaddr_bufinfo.buf, (size_t) regaddr_length, (void*) bufinfo.buf, (size_t) length,(uint32_t) args[ARG_dalay].u_int); + if (ret < 0) { + ret = -1; + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_read_obj, 1, machine_i2c_read_mem); + +static const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_i2c_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) }, + // class constants + { MP_ROM_QSTR(MP_QSTR_I2C0), MP_ROM_INT(HELIOS_I2C0) }, +#if !defined(PLAT_ASR_1803s) && !defined(BOARD_EC800MCN_LA) && !defined(BOARD_EC800MCN_GA) && !defined(BOARD_EC800NCN_LA) && !defined(BOARD_EC800NCN_LA_VOLTE) + { MP_ROM_QSTR(MP_QSTR_I2C1), MP_ROM_INT(HELIOS_I2C1) }, +#endif +#if !defined(PLAT_RDA) && !defined(BOARD_EC800NCN_LA) && !defined(BOARD_EC800NCN_LA_VOLTE) && !defined(BOARD_EC600GCN_LD) + { MP_ROM_QSTR(MP_QSTR_I2C2), MP_ROM_INT(HELIOS_I2C2) }, +#if !defined(PLAT_Qualcomm) && !defined(BOARD_EC800MCN_LA) && !defined(BOARD_EC800MCN_GA) && !defined(BOARD_EC800GCN_GA) && !defined(BOARD_EC800GCN_LD) && !defined(BOARD_EC800GCN_LD_HRXM) + { MP_ROM_QSTR(MP_QSTR_I2C3), MP_ROM_INT(HELIOS_I2C3) }, +#endif +#endif + { MP_ROM_QSTR(MP_QSTR_STANDARD_MODE), MP_ROM_INT(HELIOS_STANDARD_MODE) }, + { MP_ROM_QSTR(MP_QSTR_FAST_MODE), MP_ROM_INT(HELIOS_FAST_MODE) }, +}; + +MP_DEFINE_CONST_DICT(mp_machine_hard_i2c_locals_dict, machine_i2c_locals_dict_table); + + +MP_DEFINE_CONST_OBJ_TYPE( + machine_hard_i2c_type, + MP_QSTR_I2C, + MP_TYPE_FLAG_NONE, + make_new, machine_hard_i2c_make_new, + print, machine_hard_i2c_print, + locals_dict, &mp_machine_hard_i2c_locals_dict + ); + +#endif diff --git a/ports/quectel/machine_pin.c b/ports/quectel/machine_pin.c new file mode 100644 index 0000000000000..b9953c9f774c1 --- /dev/null +++ b/ports/quectel/machine_pin.c @@ -0,0 +1,333 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mphal.h" + +#if MICROPY_QPY_MACHINE_PIN + +#include "helios_gpio.h" +#include "helios_debug.h" + + +#define HELIOS_PIN_LOG(msg, ...) custom_log("machine_pin", msg, ##__VA_ARGS__) + +#define GPIO_OBJ_DEF(n) {{&machine_pin_type}, HELIOS_GPIO##n, 0, 0, 0} +#define PLAT_GPIO_OBJ_DEF(n) BOOST_PP_REPEAT_1(n,GPIO_OBJ_DEF) + + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + uint pin; + uint dir; + uint pull; + uint value; +} machine_pin_obj_t; + +const mp_obj_type_t machine_pin_type; + + +static machine_pin_obj_t *machine_pin_obj[PLAT_GPIO_NUM] = {NULL}; + + +static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_pin_obj_t *self = self_in; + mp_printf(print, "", self->pin); +} + +// pin.init(mode, pull=None, *, value) +static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_dir, ARG_pull, ARG_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_dir, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_pull, MP_ARG_INT, {.u_int = -1}}, + { MP_QSTR_value, MP_ARG_INT, {.u_int = -1}}, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get dir mode + switch (args[ARG_dir].u_int) { + case -1: + break; + case HELIOS_GPIO_INPUT: + case HELIOS_GPIO_OUTPUT: + self->dir = args[ARG_dir].u_int; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin dir, range{0:dir in, 1:dir out}")); + break; + } + // get pull mode + switch (args[ARG_pull].u_int) { + case -1: + break; + case HELIOS_PULL_NONE: + case HELIOS_PULL_UP: + case HELIOS_PULL_DOWN: + self->pull = args[ARG_pull].u_int; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin pull, range{0:PIN_PULL_DISABLE, 1:PIN_PULL_PU, 2:PIN_PULL_PD}")); + break; + } + // get initial value + switch (args[ARG_value].u_int) { + case -1: + break; + case HELIOS_LVL_LOW: + case HELIOS_LVL_HIGH: + self->value = args[ARG_value].u_int; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin value, range{0:PIN_LEVEL_LOW, 1:PIN_LEVEL_HIGH}")); + break; + } + Helios_GPIOInitStruct gpio_struct = {0}; + gpio_struct.dir = self->dir; + gpio_struct.pull = self->pull; + gpio_struct.value = self->value; + HELIOS_PIN_LOG("pin = %d dir = %d, pull = %d value=%d\n",self->pin, self->dir, self->pull, self->value); + int ret = Helios_GPIO_Init((Helios_GPIONum) self->pin, &gpio_struct); + HELIOS_PIN_LOG("gpio init ret = %d\n",ret); + + if (ret != 0){ + mp_raise_ValueError(MP_ERROR_TEXT("GPIO initialization error")); + } + + + return mp_const_none; +} + +// constructor(drv_name, pin, ...) +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 pin object + //unsigned int i = 0; + int wanted_pin = 0; + wanted_pin = mp_obj_get_int(args[0]); + + if(wanted_pin == 0 || wanted_pin > PLAT_GPIO_NUM) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); + } + + HELIOS_PIN_LOG("wanted_pin = %d\n",wanted_pin); + machine_pin_obj_t *self = NULL; + + if (machine_pin_obj[wanted_pin-1] == NULL) + { + machine_pin_obj[wanted_pin-1] = mp_obj_malloc_with_finaliser(machine_pin_obj_t, &machine_pin_type); + } + self = machine_pin_obj[wanted_pin-1]; + self->base.type = &machine_pin_type; + self->pin = wanted_pin; + + // default settings + self->dir = HELIOS_GPIO_OUTPUT; + self->pull = HELIOS_PULL_NONE; + self->value = HELIOS_LVL_LOW; + HELIOS_PIN_LOG("dir = %d, pull = %d value=%d\n",self->dir, self->pull, self->value); + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + HELIOS_PIN_LOG("n_args = %d\n",n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + HELIOS_PIN_LOG("n_kw = %d\n",n_kw); + } else { + + Helios_GPIOInitStruct gpio_struct = {0}; + gpio_struct.dir = self->dir; + gpio_struct.pull = self->pull; + gpio_struct.value = self->value; + Helios_GPIO_Init((Helios_GPIONum) self->pin, &gpio_struct); + } + + HELIOS_PIN_LOG("pin init end\n"); + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + int pin_val = Helios_GPIO_GetLevel((Helios_GPIONum) self->pin); + return MP_OBJ_NEW_SMALL_INT(pin_val); + } else { + Helios_GPIO_SetLevel((Helios_GPIONum) self->pin, (Helios_LvlMode) mp_obj_is_true(args[0])); + return mp_const_none; + } +} + +// pin.init(mode, pull) +static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +static mp_obj_t machine_pin_off(mp_obj_t self_in) { + machine_pin_obj_t *self = self_in; + Helios_GPIO_SetLevel((Helios_GPIONum) self->pin, (Helios_LvlMode) HELIOS_LVL_LOW); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_off_obj, machine_pin_off); + +static mp_obj_t machine_pin_on(mp_obj_t self_in) { + machine_pin_obj_t *self = self_in; + Helios_GPIO_SetLevel((Helios_GPIONum) self->pin, (Helios_LvlMode) HELIOS_LVL_HIGH); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_on_obj, machine_pin_on); + +static mp_obj_t machine_pin_write(mp_obj_t self_in, mp_obj_t value) +{ + machine_pin_obj_t *self = self_in; + int pin_value = mp_obj_get_int(value); + int ret = Helios_GPIO_SetLevel((Helios_GPIONum) self->pin, (Helios_LvlMode) pin_value); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_pin_write_obj, machine_pin_write); + +static mp_obj_t machine_pin_read(size_t n_args, const mp_obj_t *args) +{ + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_read_obj, 1, 2, machine_pin_read); + + +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: { + int pin_val = Helios_GPIO_GetLevel((Helios_GPIONum) self->pin); + return pin_val; + } + case MP_PIN_WRITE: { + Helios_GPIO_SetLevel((Helios_GPIONum) self->pin, (Helios_LvlMode) arg); + return 0; + } + } + return -1; +} + +static mp_obj_t machine_pin_set_dir(mp_obj_t self_in, mp_obj_t dir) +{ + machine_pin_obj_t *self = self_in; + int ret = -1; + + int pin_value = mp_obj_get_int(dir); + if((pin_value != HELIOS_GPIO_INPUT) && (pin_value != HELIOS_GPIO_OUTPUT)){ + mp_raise_ValueError(MP_ERROR_TEXT("Invalid direction parameter")); + } + ret = Helios_GPIO_SetDirection((Helios_GPIONum) self->pin, (Helios_GPIODir) pin_value); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_pin_set_dir_obj, machine_pin_set_dir); + +static mp_obj_t machine_pin_get_dir(mp_obj_t self_in) +{ + machine_pin_obj_t *self = self_in; + int ret = Helios_GPIO_GetDirection((Helios_GPIONum) self->pin); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_get_dir_obj, machine_pin_get_dir); + + +static mp_obj_t machine_pin_deinit(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + //HELIOS_PIN_LOG("machine pin deinit.pin=%d.\r\n", self->pin); + + machine_pin_obj[self->pin - 1] = NULL; + + int ret = Helios_GPIO_Deinit((Helios_GPIONum)self->pin); + if (ret != 0) + { + HELIOS_PIN_LOG("GPIO%d deinit failed.\r\n", self->pin); + } + return mp_obj_new_int(ret); + +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_deinit_obj, machine_pin_deinit); + + +static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_pin_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_off_obj) }, + { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_on_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_pin_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_pin_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_dir), MP_ROM_PTR(&machine_pin_set_dir_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_dir), MP_ROM_PTR(&machine_pin_get_dir_obj) }, + // class constants + //GPIO DEFINE + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(HELIOS_GPIO_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(HELIOS_GPIO_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_PULL_PU), MP_ROM_INT(HELIOS_PULL_UP) }, + { MP_ROM_QSTR(MP_QSTR_PULL_PD), MP_ROM_INT(HELIOS_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DISABLE), MP_ROM_INT(HELIOS_PULL_NONE) }, + PLAT_GPIO_DEF(PLAT_GPIO_NUM), +}; + +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, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_pin_type, + MP_QSTR_Pin, + MP_TYPE_FLAG_NONE, + make_new, mp_pin_make_new, + print, machine_pin_print, + call, machine_pin_call, + protocol, &machine_pin_pin_p, + locals_dict, &machine_pin_locals_dict + ); + +#endif + diff --git a/ports/quectel/machine_rtc.c b/ports/quectel/machine_rtc.c new file mode 100644 index 0000000000000..720e6c0a3d6fc --- /dev/null +++ b/ports/quectel/machine_rtc.c @@ -0,0 +1,228 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" + +#if MICROPY_QPY_MACHINE_RTC + +#include "modmachine.h" +#include "helios_debug.h" +#include "helios_rtc.h" +#include "callbackdeal.h" + +#define HELIOS_RTC_LOG(fmt, ...) custom_log(machine_rtc, fmt, ##__VA_ARGS__) + + +typedef struct _machine_rtc_obj_t { + mp_obj_base_t base; +} machine_rtc_obj_t; + +static c_callback_t *callback_cur = NULL; + + +// singleton RTC object +//static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; +const mp_obj_type_t machine_rtc_type; +static machine_rtc_obj_t *machine_rtc_obj_ptr = NULL; + + + +static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + if (machine_rtc_obj_ptr != NULL) + { + // singleton RTC object + return MP_OBJ_FROM_PTR(machine_rtc_obj_ptr); + } + machine_rtc_obj_ptr = mp_obj_malloc_with_finaliser(machine_rtc_obj_t, &machine_rtc_type); + machine_rtc_obj_ptr->base.type = &machine_rtc_type; + return MP_OBJ_FROM_PTR(machine_rtc_obj_ptr); +} + + + +static mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *args) { + //ql_rtc_time_t tm = {0}; //Jayceon-2020/08/27:Resolved that calling the init() function would cause system dump + Helios_RTCTime tm = {0}; + if (n_args == 1) { + // Pawn 2021/4/28 Solve the problem of different initial time zones between RDA and ASR + // Pawn Fix the bug SW1PQUECPYTHON-119 + Helios_RTC_GetLocalTime(&tm); + // Pawn Fix the bug SW1PQUECPYTHON-119 end + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(0) + }; + + return mp_obj_new_tuple(8, tuple); + } else { + // Set time + + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 8, &items); + tm.tm_year = mp_obj_get_int(items[0]); + tm.tm_mon = mp_obj_get_int(items[1]); + tm.tm_mday = mp_obj_get_int(items[2]); + tm.tm_hour = mp_obj_get_int(items[4]); + tm.tm_min = mp_obj_get_int(items[5]); + tm.tm_sec = mp_obj_get_int(items[6]); + + //int ret = ql_rtc_set_time(&tm); + + // Pawn Fix the bug SW1PQUECPYTHON-119 + int ret = Helios_RTC_NtpSetTime(&tm, 1); + // Pawn Fix the bug SW1PQUECPYTHON-119 end + + return mp_obj_new_int(ret); + } +} + + +static mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { + return machine_rtc_datetime_helper(n_args, args); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, (mp_obj_t)machine_rtc_datetime); + +#if !defined(PLAT_Qualcomm) +static mp_obj_t machine_rtc_set_alarm(mp_obj_t self_in, mp_obj_t time) +{ + Helios_RTCTime tm = {0}; + mp_obj_t *items; + + mp_obj_get_array_fixed_n(time, 8, &items); + tm.tm_year = mp_obj_get_int(items[0]); + tm.tm_mon = mp_obj_get_int(items[1]); + tm.tm_mday = mp_obj_get_int(items[2]); + tm.tm_hour = mp_obj_get_int(items[4]); + tm.tm_min = mp_obj_get_int(items[5]); + tm.tm_sec = mp_obj_get_int(items[6]); + + int ret = Helios_RTC_Set_Alarm(&tm); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_set_alarm_obj, machine_rtc_set_alarm); + + +static mp_obj_t machine_rtc_enable_alarm(mp_obj_t self_in, mp_obj_t enable) +{ + int on_off = mp_obj_get_int(enable); + + int ret = Helios_RTC_Enable_Alarm((unsigned char)on_off); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_enable_alarm_obj, machine_rtc_enable_alarm); + + +void rtc_callback_to_python(void) +{ + if(callback_cur == NULL) { + return; + } + + mp_sched_schedule_ex(callback_cur, mp_const_none); + +#if defined(PLAT_RDA) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + Helios_Rtc_Set_Called(Helios_Rtc_Call_NoCalled); +#endif +} + + +static mp_obj_t machine_rtc_register_callback(mp_obj_t self_in, mp_obj_t callback) +{ + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + callback_cur = &cb; + mp_sched_schedule_callback_register(callback_cur, callback); + +#if defined(PLAT_RDA) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + if ( Helios_Rtc_Get_Called() == Helios_Rtc_Call_Called ) + { + mp_sched_schedule_ex(callback_cur, NULL); + Helios_Rtc_Set_Called(Helios_Rtc_Call_NoCalled); + } +#endif + int ret = Helios_RTC_Register_cb(rtc_callback_to_python); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_register_callback_obj, machine_rtc_register_callback); +#endif + +static mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { + mp_obj_t args[2] = {self_in, date}; + machine_rtc_datetime_helper(2, args); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_init_obj, machine_rtc_init); + +static mp_obj_t machine_rtc_deinit(mp_obj_t self_in) { + HELIOS_RTC_LOG("machine rtc deinit.\r\n"); + callback_cur = NULL; + machine_rtc_obj_ptr = NULL; + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_deinit_obj, machine_rtc_deinit); + + +static const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rtc_init_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_rtc_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) }, +#if !defined(PLAT_Qualcomm) + { MP_ROM_QSTR(MP_QSTR_set_alarm), MP_ROM_PTR(&machine_rtc_set_alarm_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_alarm), MP_ROM_PTR(&machine_rtc_enable_alarm_obj) }, + { MP_ROM_QSTR(MP_QSTR_register_callback), MP_ROM_PTR(&machine_rtc_register_callback_obj) }, +#endif +}; +static MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_rtc_type, + MP_QSTR_RTC, + MP_TYPE_FLAG_NONE, + make_new, machine_rtc_make_new, + locals_dict, &machine_rtc_locals_dict + ); + +#endif /* MICROPY_QPY_MACHINE_RTC */ + diff --git a/ports/quectel/machine_timer.c b/ports/quectel/machine_timer.c new file mode 100644 index 0000000000000..01e9d153305f3 --- /dev/null +++ b/ports/quectel/machine_timer.c @@ -0,0 +1,311 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "modmachine.h" +#include "mphalport.h" + + +#if MICROPY_QPY_MACHINE_TIMER + +#include "helios_timer.h" +#include "helios_debug.h" +#include "callbackdeal.h" + + +typedef unsigned long int UINT32; +#define HELIOS_TIMER_LOG(msg, ...) custom_log("machine_timer", msg, ##__VA_ARGS__) + + +typedef enum +{ + Timer0 = 0, + Timer1 = 1, + Timer2 = 2, + Timer3 = 3 +}Timern; + +typedef enum /* The meaning of the API flag*/ +{ + TIMER_PERIODIC = 0x1, /* periodic execution */ + TIMER_AUTO_DELETE = 0x2 /* one execution */ +}TIMER_FLAG; + +typedef enum +{ + MP_TIMER_CREATED, + MP_TIMER_RUNNING, + MP_TIMER_STOP +}MP_TIMER_STATUS; + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + //ONE_SHOT OR PERIODIC + mp_uint_t mode; + mp_uint_t period; + c_callback_t callback; + mp_int_t timer_id; + mp_int_t timer_id_real; + MP_TIMER_STATUS timer_status; + //for traverse all the timers, such as soft reset while input CTRL+B + struct _machine_timer_obj_t *next; +} machine_timer_obj_t; + +static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)(print); + (void)(kind); + machine_timer_obj_t *self = self_in; + HELIOS_TIMER_LOG("period=%d\r\n", self->period); + HELIOS_TIMER_LOG("timer_id=%d\r\n", self->timer_id); +} + +#if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350)//forrest.liu@20231228 add for FAE-102027 dump issue +#define HELIOS_TIMER_MAX_NUM (4) +static st_CallBack_Timer Timer_t[HELIOS_TIMER_MAX_NUM] = {{{0,0,0,0,0}},{{0,0,0,0,0}},{{0,0,0,0,0}},{{0,0,0,0,0}}}; +#endif + +static void machine_timer_isr(UINT32 self_in) { + + machine_timer_obj_t *self = (void*)self_in; + if(self != NULL) + { + #if defined(BOARD_BC32RA) || defined(BOARD_BC92RB) || defined(PLAT_SONY_ALT1350)//forrest.liu@20231228 modify for FAE-102027 dump issue + Timer_t[self->timer_id].callback = self->callback; + qpy_send_msg_to_callback_deal_thread(CALLBACK_TYPE_ID_TIMER, &Timer_t[self->timer_id]); + #else + mp_sched_schedule_ex(&self->callback, self); + #endif + } + +} + + +static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_mode, + ARG_callback, + ARG_period, + ARG_tick_hz, + ARG_freq, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_PERIODIC} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, + #if MICROPY_PY_BUILTINS_FLOAT + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + #else + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + #endif + }; + + int ret; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + #if MICROPY_PY_BUILTINS_FLOAT + if (args[ARG_freq].u_obj != mp_const_none) { + self->period = (mp_uint_t)(1000 / mp_obj_get_float(args[ARG_freq].u_obj)); + } + #else + if (args[ARG_freq].u_int != 0xffffffff) { + self->period = 1000 / ((mp_uint_t)args[ARG_freq].u_int); + } + #endif + else { + self->period = (((uint64_t)args[ARG_period].u_int) * 1000) / args[ARG_tick_hz].u_int; + } + + self->mode = args[ARG_mode].u_int; + + mp_sched_schedule_callback_register(&self->callback, args[ARG_callback].u_obj); + + HELIOS_TIMER_LOG("mode: %d, period: %d,callback:%#X\r\n", self->mode, self->period,self->callback.cb); + +// Check whether the timer is already running, if so return 0 + if (MP_TIMER_RUNNING == self->timer_status) { + return mp_obj_new_int(0); + } + + int timer = 0; + if(0 == self->timer_id_real) + { + + if(!self) + { + HELIOS_TIMER_LOG("timer test 147!\n"); + } + HELIOS_TIMER_LOG("self = %x\n",self); + timer = Helios_Timer_init( (void* )machine_timer_isr, self); + } + else + { + HELIOS_TIMER_LOG("timer test 153!\n"); + timer = self->timer_id_real; + } + + if(!timer) { + return mp_const_false; + } + + uint8_t cyclicalEn = 0; + if(self->mode == TIMER_PERIODIC) { + cyclicalEn = 1; + } + HELIOS_TIMER_LOG("timer test 165!\n"); + ret = Helios_Timer_Start(timer, (uint32_t) self->period, cyclicalEn); + HELIOS_TIMER_LOG("timer test 167!\n"); + + if(!ret) + { + //get time_id_real + HELIOS_TIMER_LOG("timer_id: %#x\r\n", timer); + self->timer_id_real = timer; + self->timer_status = MP_TIMER_RUNNING; + } + else + { + HELIOS_TIMER_LOG("timer create failed.ret=%d\r\n",ret); + } + return mp_obj_new_int(0); +} + + + +static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)(type); + mp_int_t timer_id=0; + + if (n_args > 0) { + timer_id = mp_obj_get_int(args[0]); + --n_args; + ++args; + } + + if ((timer_id < 0) || (timer_id > 3)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, Timern should be in (Timer0~Timer3).")); + } + + machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); + self->base.type = &machine_timer_type; + self->timer_id = timer_id; + self->timer_id_real = 0; + self->timer_status = MP_TIMER_CREATED; + + if (n_args > 0 || n_kw > 0) { + // Start the timer + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_timer_init_helper(self, n_args, args, &kw_args); + } + + return MP_OBJ_FROM_PTR(self); +} + + +static mp_obj_t machine_timer_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + return machine_timer_init_helper(self, n_args - 1, args + 1, kw_args); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); + +static mp_obj_t machine_timer_stop(mp_obj_t self_in) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if(self->timer_id_real) + { + HELIOS_TIMER_LOG("machine_timer_stop (%#X) \r\n",self->timer_id_real); + Helios_Timer_Stop(self->timer_id_real); + self->timer_status = MP_TIMER_STOP; + return mp_obj_new_int(0); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_stop_obj, machine_timer_stop); + + +static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if(self->timer_id_real) + { + HELIOS_TIMER_LOG("machine_timer_deinit (%#X) \r\n",self->timer_id_real); + Helios_Timer_Stop(self->timer_id_real); + Helios_Timer_Deinit(self->timer_id_real); + self->timer_id_real = 0; + self->timer_status = MP_TIMER_STOP; + return mp_obj_new_int(0); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); + + +static mp_obj_t machine_timer_initialize() { + static int initialized = 0; + if (!initialized) { + + HELIOS_TIMER_LOG("machine_timer_initialize\r\n"); + initialized = 1; + } + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_0(machine_timer_initialize_obj, machine_timer_initialize); + +static const mp_rom_map_elem_t machine_timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_timer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&machine_timer_initialize_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_timer_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_timer_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(TIMER_AUTO_DELETE) }, + { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(TIMER_PERIODIC) }, + { MP_ROM_QSTR(MP_QSTR_Timer0), MP_ROM_INT(Timer0) }, + { MP_ROM_QSTR(MP_QSTR_Timer1), MP_ROM_INT(Timer1) }, + { MP_ROM_QSTR(MP_QSTR_Timer2), MP_ROM_INT(Timer2) }, + { MP_ROM_QSTR(MP_QSTR_Timer3), MP_ROM_INT(Timer3) }, +}; +static MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_timer_type, + MP_QSTR_Timer, + MP_TYPE_FLAG_NONE, + make_new, machine_timer_make_new, + print, machine_timer_print, + locals_dict, &machine_timer_locals_dict + ); + +#endif diff --git a/ports/quectel/machine_uart.c b/ports/quectel/machine_uart.c new file mode 100644 index 0000000000000..a52d7d1e3db69 --- /dev/null +++ b/ports/quectel/machine_uart.c @@ -0,0 +1,492 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include + +#include "runtime.h" +#include "stream.h" +#include "mperrno.h" +#include "mphalport.h" + +#if MICROPY_QPY_MACHINE_UART + +#include "helios_uart.h" +#include "helios_pin.h" +#include "helios_debug.h" +#include "callbackdeal.h" + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + unsigned int uart_num; + Helios_UARTConfig config; +} machine_uart_obj_t; + +static c_callback_t *callback_cur[HELIOS_UARTMAX] = {0}; + +static machine_uart_obj_t *uart_self_obj[HELIOS_UARTMAX] = {0}; + + +const mp_obj_type_t machine_uart_type; +static const char *_parity_name[] = {"None", "1", "0"}; + +/******************************************************************************/ +// MicroPython bindings for UART + +static void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, flow=%u", + self->uart_num, self->config.baudrate, self->config.data_bit, _parity_name[self->config.parity_bit], + self->config.stop_bit, self->config.flow_ctrl); + mp_printf(print, ")"); +} + +static void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_flow}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_parity, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_flow, MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get baudrate bits + switch (args[ARG_baudrate].u_int) { + case HELIOS_UART_BAUD_300: + case HELIOS_UART_BAUD_600: + case HELIOS_UART_BAUD_1200: + case HELIOS_UART_BAUD_2400: + case HELIOS_UART_BAUD_3600: + case HELIOS_UART_BAUD_4800: + case HELIOS_UART_BAUD_7200: + case HELIOS_UART_BAUD_9600: + case HELIOS_UART_BAUD_14400: + case HELIOS_UART_BAUD_19200: + case HELIOS_UART_BAUD_28800: + case HELIOS_UART_BAUD_33600: + case HELIOS_UART_BAUD_38400: + case HELIOS_UART_BAUD_57600: + case HELIOS_UART_BAUD_115200: + case HELIOS_UART_BAUD_230400: + case HELIOS_UART_BAUD_460800: + case HELIOS_UART_BAUD_921600: + case HELIOS_UART_BAUD_1000000: + case HELIOS_UART_BAUD_1842000: + case HELIOS_UART_BAUD_3686400: + case HELIOS_UART_BAUD_4468750: + self->config.baudrate = args[ARG_baudrate].u_int; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid baudrate")); + break; + } + + // get data bits +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + switch (args[ARG_bits].u_int) { + case 8: + self->config.data_bit = HELIOS_UART_DATABIT_8; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid data bits, Unisoc platform only support 8 data bit.")); + break; + } +#else + switch (args[ARG_bits].u_int) { + case 5: + self->config.data_bit = HELIOS_UART_DATABIT_5; + break; + case 6: + self->config.data_bit = HELIOS_UART_DATABIT_6; + break; + case 7: + self->config.data_bit = HELIOS_UART_DATABIT_7; + break; + case 8: + self->config.data_bit = HELIOS_UART_DATABIT_8; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid data bits")); + break; + } +#endif + // get parity bits + switch (args[ARG_parity].u_int) { + case 0: + self->config.parity_bit = HELIOS_UART_PARITY_NONE; + break; + case 1: + self->config.parity_bit = HELIOS_UART_PARITY_EVEN; + break; + case 2: + self->config.parity_bit = HELIOS_UART_PARITY_ODD; + break; + #if defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1602) + case 3: + self->config.parity_bit = HELIOS_UART_PARITY_MARK; + break; + case 4: + self->config.parity_bit = HELIOS_UART_PARITY_SPACE; + break; + #endif + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid parity bits")); + break; + } + + // get stop bits + switch (args[ARG_stop].u_int) { + case 1: + self->config.stop_bit = HELIOS_UART_STOP_1; + break; + case 2: + self->config.stop_bit = HELIOS_UART_STOP_2; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid stop bits")); + break; + } + + // get flow bits + switch (args[ARG_flow].u_int) { + case 0: + self->config.flow_ctrl = HELIOS_UART_FC_NONE; + break; + case 1: + self->config.flow_ctrl = HELIOS_UART_FC_HW; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid flow bits")); + break; + } + Helios_UARTInitStruct uart_para = {0}; + Helios_UARTConfig uart_config = {0}; + uart_para.uart_config = &uart_config; + + memcpy((void*)&uart_config,(void*)&self->config, sizeof(Helios_UARTConfig)); + if(self->uart_num == QPY_REPL_UART) { + mp_hal_port_open(0); + } +//Helios_UART_Init has created a timer in kernal and Helios_UART_Deinit will delete this timer of Sony. +//So if we deint uart first, moudle will dump. +#ifndef PLAT_SONY_ALT1350 + Helios_UART_Deinit((Helios_UARTNum) self->uart_num); +#endif + + if(Helios_UART_Init((Helios_UARTNum) self->uart_num, &uart_para) != 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) init fail"), self->uart_num); + } +} + +static mp_obj_t machine_uart_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 uart id + mp_int_t uart_num = mp_obj_get_int(args[0]); + if (uart_num < HELIOS_UART0 || uart_num > HELIOS_UARTMAX) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) does not exist"), uart_num); + } + + // create instance + if (uart_self_obj[uart_num] == NULL) + { + uart_self_obj[uart_num] = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type); + } + machine_uart_obj_t *self = uart_self_obj[uart_num]; + + self->base.type = &machine_uart_type; + self->uart_num = uart_num; + self->config.baudrate = HELIOS_UART_BAUD_115200; + self->config.data_bit = HELIOS_UART_DATABIT_8; + self->config.stop_bit = HELIOS_UART_STOP_1; + self->config.parity_bit = HELIOS_UART_PARITY_NONE; + self->config.flow_ctrl = HELIOS_UART_FC_NONE; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init); + +static mp_obj_t machine_uart_deinit(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int ret = Helios_UART_Deinit((Helios_UARTNum) self->uart_num); + if(self->uart_num == QPY_REPL_UART) { + mp_hal_port_open(1); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_deinit_obj, machine_uart_deinit); + + +static mp_obj_t machine_uart_any(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t rxbufsize; + rxbufsize = Helios_UART_Any((Helios_UARTNum) self->uart_num); + return MP_OBJ_NEW_SMALL_INT(rxbufsize); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); + +#if defined (PLAT_ASR) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8850_R02) \ + || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_EIGEN) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ASR_1602) || defined(PLAT_aic8800m40) || defined(PLAT_Unisoc_8910_R06) +enum{ARG_self, ARG_gipo_n, ARG_direc, ARG_block}; +static mp_obj_t machine_uart_485_control(mp_uint_t n_args, const mp_obj_t *args){ + int ret = -1; + if(n_args < 3) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid parameter")); + } + machine_uart_obj_t *self = MP_OBJ_TO_PTR(args[ARG_self]); + size_t gpio = mp_obj_get_int(args[ARG_gipo_n]); + Helios_UART_Direc dire = (Helios_UART_Direc)mp_obj_get_int(args[ARG_direc]); + ret = Helios_UART_SetCtl_485(self->uart_num,gpio,dire); +#if defined(PLAT_Unisoc_8850) + int set = 0; + if(n_args >= 4) + { + set = mp_obj_get_int(args[ARG_block]); + } + Helios_UART_SetBlockMode_485((char)set); +#endif + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_uart_485_control_obj, 3, 4, (mp_obj_t)machine_uart_485_control); + +#endif + +void helios_uart_callback_to_python(uint64_t ind_type, Helios_UARTNum port, uint64_t size) +{ + if(callback_cur[port] == NULL) { + return; + } + st_CallBack_Uart *Uart_t = malloc(sizeof(st_CallBack_Uart)); + if(NULL != Uart_t) + { + Uart_t->ind_type = ind_type; + Uart_t->port = port; + Uart_t->size = size; + Uart_t->callback = *callback_cur[port]; + qpy_send_msg_to_callback_deal_thread(CALLBACK_TYPE_ID_UART, Uart_t); + } +} + + + +static mp_obj_t helios_uart_set_callback(mp_obj_t self_in, mp_obj_t callback) +{ + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + static c_callback_t cb[sizeof(callback_cur) / sizeof(callback_cur[0])] = {0}; + memset(&cb[self->uart_num], 0, sizeof(c_callback_t)); + cb[self->uart_num].arg = mp_obj_new_tuple(3, NULL); + callback_cur[self->uart_num] = &cb[self->uart_num]; + mp_sched_schedule_callback_register(callback_cur[self->uart_num], callback); + + int ret = Helios_UART_SetCallback((Helios_UARTNum) self->uart_num, (Helios_UARTCallback) helios_uart_callback_to_python); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_uart_set_callback_obj, helios_uart_set_callback); + +static mp_obj_t helios_uart_deinit(mp_obj_t self_in) +{ + int ret = 0; + + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uart_self_obj[self->uart_num] = NULL; + callback_cur[self->uart_num] = NULL; + ret = Helios_UART_Deinit((Helios_UARTNum) self->uart_num); + + if(self->uart_num == QPY_REPL_UART) { + mp_hal_port_open(1); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(helios_uart_deinit_obj, helios_uart_deinit); + +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) +static mp_obj_t machine_uart_rx_disable(mp_obj_t self_in,mp_obj_t set) +{ + int ret = -1; + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t set_value = mp_obj_get_int(set); + if(set_value > 1) + { + mp_raise_ValueError(MP_ERROR_TEXT("setting_value must be 0 or 1")); + } + ret = Helios_Uart_Disable_Rx(self->uart_num,set_value); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_2(machine_uart_rx_disable_obj, machine_uart_rx_disable); +#endif + +static const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&helios_uart_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&machine_uart_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_callback), MP_ROM_PTR(&machine_uart_set_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeOnce), MP_ROM_PTR(&mp_stream_write1_obj) }, + { MP_ROM_QSTR(MP_QSTR_readOnce), MP_ROM_PTR(&mp_stream_read1_obj) }, + +#if defined(PLAT_RDA) + { MP_ROM_QSTR(MP_QSTR_UART1), MP_ROM_INT(HELIOS_UART1) }, +#endif +#if defined (PLAT_ASR) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8850_R02) \ + || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_EIGEN) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ASR_1602) || defined(PLAT_aic8800m40) || defined(PLAT_Unisoc_8910_R06) + { MP_ROM_QSTR(MP_QSTR_control_485), MP_ROM_PTR(&machine_uart_485_control_obj) }, + PLAT_GPIO_DEF(PLAT_GPIO_NUM), +#endif +#if defined (PLAT_ASR) || defined(PLAT_Qualcomm) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_EIGEN) \ + || defined(PLAT_ASR_1602) || defined(PLAT_ECR6600) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(BOARD_BC32RA) || defined(BOARD_BC92RB) \ + || defined(PLAT_EIGEN_718) || defined(PLAT_aic8800m40) + { MP_ROM_QSTR(MP_QSTR_UART0), MP_ROM_INT(HELIOS_UART0) }, +#endif + +#if !defined(PLAT_RDA) +#if !defined(PLAT_ECR6600) + { MP_ROM_QSTR(MP_QSTR_UART1), MP_ROM_INT(HELIOS_UART1) }, +#endif + { MP_ROM_QSTR(MP_QSTR_UART2), MP_ROM_INT(HELIOS_UART2) }, +#if !defined(PLAT_ECR6600) + { MP_ROM_QSTR(MP_QSTR_UART3), MP_ROM_INT(HELIOS_UART3) }, +#endif +#endif + +#if defined(PLAT_Qualcomm) \ + || defined (PLAT_Unisoc) \ + || defined(PLAT_Unisoc_8910_R05) \ + || defined(PLAT_Unisoc_8910_R06) \ + || defined(BOARD_EC600GCN_LD) \ + || defined(BOARD_EC600GCN_LA) \ + || defined(BOARD_EC600GCN_LA_CDD) \ + || defined(BOARD_EC600GCN_LD_YM) \ + || defined(BOARD_EG700GCN_LC) \ + || defined (PLAT_ASR) \ + || MICROPY_QPY_UART4 + { MP_ROM_QSTR(MP_QSTR_UART4), MP_ROM_INT(HELIOS_UART4) }, +#endif + +#if defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8850_R02) || MICROPY_QPY_USB_DOUBLE_CDC + { MP_ROM_QSTR(MP_QSTR_UART5), MP_ROM_INT(HELIOS_UART5) }, +#endif + +#if defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8850_R02) + { MP_ROM_QSTR(MP_QSTR_UART6), MP_ROM_INT(HELIOS_UART6) }, +#endif + +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + { MP_ROM_QSTR(MP_QSTR_rx_auto_disable), MP_ROM_PTR(&machine_uart_rx_disable_obj) }, +#endif + { MP_ROM_QSTR(MP_QSTR_REPL_UART), MP_ROM_INT(QPY_REPL_UART) }, +}; + +static MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); + +static mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + int bytes_read = Helios_UART_Read((Helios_UARTNum) self->uart_num, buf_in, size); + if (bytes_read < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + return bytes_read; +} + +static mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + int bytes_written = Helios_UART_Write((Helios_UARTNum) self->uart_num, (void*)buf_in, size); + if (bytes_written < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + // return number of bytes written + return bytes_written; +} + +static mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + //machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + mp_uint_t flags = (mp_uint_t)arg; + ret = 0; + size_t rxbufsize = 1; + if ((flags & MP_STREAM_POLL_RD) && rxbufsize > 0) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && 1) { // FIXME: uart_tx_any_room(self->uart_num) + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +static const mp_stream_p_t uart_stream_p = { + .read = machine_uart_read, + .write = machine_uart_write, + .ioctl = machine_uart_ioctl, + .is_text = false, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + machine_uart_type, + MP_QSTR_UART, + MP_TYPE_FLAG_NONE, + make_new, machine_uart_make_new, + print, machine_uart_print, + protocol, &uart_stream_p, + locals_dict, &machine_uart_locals_dict + ); + +#endif diff --git a/ports/quectel/machine_wdt.c b/ports/quectel/machine_wdt.c new file mode 100644 index 0000000000000..7ef3b6eda2146 --- /dev/null +++ b/ports/quectel/machine_wdt.c @@ -0,0 +1,101 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "py/runtime.h" +#include "modmachine.h" +#include "mphalport.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/smallint.h" +#include "utime_mphal.h" + +#include "helios_wdt.h" +typedef struct _machine_wdt_init_t +{ + mp_obj_base_t base; +}machine_wdt_init_t; +const mp_obj_type_t machine_wdt_type; + +typedef struct _machine_wdt_obj_t +{ + int status; // Y/N + int period; +}machine_wdt_obj_t; + +static machine_wdt_init_t machine_wdt_obj = {0}; + +static mp_obj_t machine_wdt_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, 1, true); + + machine_wdt_init_t *self = &machine_wdt_obj; + machine_wdt_obj_t info = {0}; + self->base.type = &machine_wdt_type; + if (n_args > 0) + { + info.period = mp_obj_get_int(args[0]); + --n_args; + ++args; + } + Helios_WDT_Deinit(); + if(0 != Helios_WDT_Init((uint64_t) info.period)) { + return mp_const_false; + } + return MP_OBJ_FROM_PTR(self); +} + + +static mp_obj_t machine_wdt_feed(const mp_obj_t arg0) +{ + Helios_WDT_Feed(); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed); + + +static mp_obj_t machine_wdt_stop(const mp_obj_t arg0) +{ + Helios_WDT_Deinit(); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_stop_obj, machine_wdt_stop); + + +static const mp_rom_map_elem_t machine_wdt_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&machine_wdt_feed_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_wdt_stop_obj) }, +}; +static MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + machine_wdt_type, + MP_QSTR_WDT, + MP_TYPE_FLAG_NONE, + make_new, machine_wdt_make_new, + locals_dict, &machine_wdt_locals_dict + ); diff --git a/ports/quectel/main.c b/ports/quectel/main.c new file mode 100644 index 0000000000000..71b6a2ae8969f --- /dev/null +++ b/ports/quectel/main.c @@ -0,0 +1,312 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include + +#include "py/builtin.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/objmodule.h" +#include "py/stackctrl.h" +#include "py/mphal.h" +#include "shared/runtime/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include +#include "helios.h" +#include "helios_os.h" +#include "helios_debug.h" +#include "mphalport.h" +#include "callbackdeal.h" +#if MICROPY_KBD_EXCEPTION +#include "shared/runtime/interrupt_char.h" +#endif + +#if CONFIG_MBEDTLS +#include "mbedtls_init.h" +#endif + + +Helios_Thread_t ql_micropython_task_ref; + + +#define MP_TASK_STACK_SIZE (MP_QPY_TASK_STACK_SIZE) +#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE/sizeof(uint32_t)) + +#if (defined(PLAT_ASR) || defined(PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Qualcomm) \ + || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) || defined(PLAT_ASR_1602)) //support +#define QPY_ASSERT_SUPPORT 1 +#endif + +void nlr_jump_fail(void *val) { +#if QPY_ASSERT_SUPPORT + Helios_Assert(__func__, __FILE__, __LINE__, ""); +#endif + while(1); +} + +void NORETURN __fatal_error(const char *msg) { +#if QPY_ASSERT_SUPPORT + Helios_Assert(msg, __FILE__, __LINE__, ""); +#endif + while(1); +} + +#ifndef NDEBUG +#if !defined(PLAT_Qualcomm) +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { +#if QPY_ASSERT_SUPPORT + Helios_Assert(expr, file, line, ""); +#else + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); +#endif + __fatal_error("Assertion failed"); +} + +#else +void __assert_fail(const char *__message, + const char *__file, int __line, + const char *__function) { +#if QPY_ASSERT_SUPPORT + Helios_Assert(__message, __file, __line, ""); +#else + printf("Assertion '%s' failed, at file %s:%d\n", __message, __file, __line); +#endif + __fatal_error("Assertion failed"); +} +#endif + +#endif + +static char *stack_top; +#if MICROPY_ENABLE_GC +#if defined(PLAT_ECR6600) +static char __attribute__((__section__(".data"))) heap[MICROPY_GC_HEAP_SIZE]; +#elif defined(PLAT_SONY_ALT1350) +static char __attribute__((__section__("gpm1_working_data"))) heap[MICROPY_GC_HEAP_SIZE]; +#else +static char heap[MICROPY_GC_HEAP_SIZE]; +#endif +#endif + +extern pyexec_mode_kind_t pyexec_mode_kind; + +#if MICROPY_PY_KBD_EXCEPTION +MAINPY_RUNNING_FLAG_DEF +MAINPY_INTERRUPT_BY_KBD_FLAG_DEF +SET_MAINPY_RUNNING_TIMER_DEF +#endif + +#if MICROPY_PY_SOFT_RESET +static int vm_softreset_flag = 0; +#define SOFTRESET_FLAG_SET() vm_softreset_flag = 1; +#define SOFTRESET_FLAG_CLEAR() vm_softreset_flag = 0; +#define SOFTRESET_FLAG_TRUE() (1 == vm_softreset_flag) +#define SOFTRESET_FLAG_FALSE() (0 == vm_softreset_flag) +#endif +void quecpython_task(void *arg) +{ + int stack_dummy; + Helios_Thread_t id = 0; + void *stack_ptr = NULL; + #if MICROPY_QPY_MODULE_POC && CONFIG_POC_BND_XIN + helios_debug( "start qpy_poc_register_task"); + extern void qpy_poc_register_task(); + qpy_poc_register_task(); + #endif + mp_mthread_sleep_deal_init(); + +#if CONFIG_MBEDTLS + mbedtls_platform_setup(NULL); +#endif + + #if MICROPY_PY_THREAD + id = Helios_Thread_GetID(); + ql_micropython_task_ref = id; + stack_ptr = Helios_Thread_GetStaskPtr(id); +#if defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) + mp_thread_init(stack_ptr, MP_TASK_STACK_SIZE); // unit: Word +#else + mp_thread_init(stack_ptr, MP_TASK_STACK_LEN); +#endif + #endif + + if(mp_hal_stdio_init()) return; + +soft_reset: + mp_stack_set_top((void *)&stack_dummy); +#if defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) // unit: Byte + mp_stack_set_limit(MP_TASK_STACK_SIZE * sizeof(uint32_t) - 1024); +#else + mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024); +#endif + stack_top = (char*)&stack_dummy; + #if MICROPY_ENABLE_GC + gc_init(heap, heap + sizeof(heap)); + #endif + mp_init(); + #if MICROPY_ENABLE_CALLBACK_DEAL + qpy_callback_deal_init(); + #endif + readline_init0(); + + // run boot-up scripts +#if defined(PLAT_RDA) + pyexec_frozen_module("_boot_RDA.py"); +#elif defined(PLAT_Qualcomm) + pyexec_frozen_module("_boot_Qualcomm.py"); +#elif defined(BOARD_EC800ECN_LC_WDF) + pyexec_frozen_module("_boot_WDF.py");//EIGEN WDF CUNSTOMER BOOT WITH SINGEL FILE SYSTEM +#elif defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) + pyexec_frozen_module("_boot_WIFI.py"); +#elif defined(BOARD_EC600GCN_LA_CDD) + pyexec_frozen_module("_boot_dsds.py"); +#elif defined(PLAT_SONY_ALT1350) + pyexec_frozen_module("_boot_SONY.py", false); +#else + pyexec_frozen_module("_boot.py", false); +#endif + + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL + #if MICROPY_PY_KBD_EXCEPTION + && MAINPY_INTERRUPT_BY_KBD_FLAG_FALSE() + #endif + ) + { + #if MICROPY_PY_SOFT_RESET + SOFTRESET_FLAG_CLEAR(); + #endif + + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_RUNNING_FLAG_SET(); + #endif + + int ret = pyexec_file_if_exists("/usr/main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_RUNNING_FLAG_CLEAR(); + if(RET_KBD_INTERRUPT == ret) + { + MAINPY_INTERRUPT_BY_KBD_FLAG_SET(); + } + #endif + + #if MICROPY_PY_SOFT_RESET + if((PYEXEC_SOFTRESET & ret) == PYEXEC_SOFTRESET) { + SOFTRESET_FLAG_SET(); + } + #endif + } + else + { + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_INTERRUPT_BY_KBD_FLAG_CLEAR(); + #endif + #if MICROPY_PY_SOFT_RESET + SOFTRESET_FLAG_CLEAR(); + #endif + } + + if(1 + #if MICROPY_PY_KBD_EXCEPTION + && MAINPY_INTERRUPT_BY_KBD_FLAG_FALSE() + #endif + #if MICROPY_PY_SOFT_RESET + && SOFTRESET_FLAG_FALSE() + #endif + ) + { + #if MICROPY_PY_SOFT_RESET + nlr_buf_t nlr; + nlr.ret_val = NULL; + if (nlr_push(&nlr) == 0) { + #endif + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + #if MICROPY_PY_SOFT_RESET + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + #endif + } + +soft_reset_exit: + + #if MICROPY_PY_THREAD + mp_thread_deinit(); + #endif + + gc_sweep_all(); + + mp_hal_stdout_tx_str("MPY: soft reboot\r\n"); + #if MICROPY_ENABLE_CALLBACK_DEAL + qpy_callback_deal_deinit(); + qpy_callback_para_link_free_all(); + #endif + mp_deinit(); +#if !defined(PLAT_RDA) + fflush(stdout); +#endif + goto soft_reset; +} + +#if !MICROPY_VFS +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { + mp_raise_OSError(MP_ENOENT); +} + +mp_import_stat_t mp_import_stat(const char *path) { + (void)path; + return MP_IMPORT_STAT_NO_EXIST; +} + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +#endif + +application_init(quecpython_task, "quecpython_task", (MP_TASK_STACK_SIZE)/1024, 0); diff --git a/ports/quectel/misc_adc.c b/ports/quectel/misc_adc.c new file mode 100644 index 0000000000000..7f867498ab7f6 --- /dev/null +++ b/ports/quectel/misc_adc.c @@ -0,0 +1,119 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 "stdio.h" +#include "stdlib.h" +#include +#include "py/obj.h" +#include "py/runtime.h" +#include "mphalport.h" + +#if MICROPY_QPY_MISC_ADC + +#include "helios_adc.h" + +typedef enum +{ + ADC0 = 0, + ADC1 = 1, + ADC2 = 2, + ADC3 = 3, +}ADCn; + +typedef struct _misc_adc_obj_t { + mp_obj_base_t base; + unsigned int pin; +} misc_adc_obj_t; + +const mp_obj_type_t misc_adc_type; + +static mp_obj_t Helios_adc_init(mp_obj_t self_in) +{ + int ret = Helios_ADC_Init(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(Helios_adc_init_obj, Helios_adc_init); + +static mp_obj_t Helios_adc_read(mp_obj_t self_in, mp_obj_t adc_channel) +{ + int ret = -1; + int channel = mp_obj_get_int(adc_channel); + if ((channel == 0) || (channel == 1) || (channel == 2) || (channel == 3)) + { + unsigned int chl = channel; + //ret = ql_adc_read(chl, &batvol); + + ret = Helios_ADC_Read((Helios_ADCNum) chl); + return mp_obj_new_int(ret); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_2(Helios_adc_read_obj, Helios_adc_read); + +static mp_obj_t Helios_adc_deinit(mp_obj_t self_in) +{ + int ret = Helios_ADC_Deinit(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(Helios_adc_deinit_obj, Helios_adc_deinit); + + + +static const mp_rom_map_elem_t misc_adc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&Helios_adc_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&Helios_adc_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&Helios_adc_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_ADC0), MP_ROM_INT(ADC0) }, +#if !defined(PLAT_Qualcomm) + { MP_ROM_QSTR(MP_QSTR_ADC1), MP_ROM_INT(ADC1) }, +#if !defined(PLAT_Unisoc_8850) && !defined(BOARD_EC600ECN_LC) && !defined(PLAT_SONY_ALT1350) + { MP_ROM_QSTR(MP_QSTR_ADC2), MP_ROM_INT(ADC2) }, + { MP_ROM_QSTR(MP_QSTR_ADC3), MP_ROM_INT(ADC3) }, +#endif +#endif +}; +static MP_DEFINE_CONST_DICT(misc_adc_locals_dict, misc_adc_locals_dict_table); + +static mp_obj_t misc_adc_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, MP_OBJ_FUN_ARGS_MAX, true); + + // create ADC object + misc_adc_obj_t *self = m_new_obj(misc_adc_obj_t); + self->base.type = &misc_adc_type; + return MP_OBJ_FROM_PTR(self); +} + +MP_DEFINE_CONST_OBJ_TYPE( + misc_adc_type, + MP_QSTR_ADC, + MP_TYPE_FLAG_NONE, + make_new, misc_adc_make_new, + locals_dict, &misc_adc_locals_dict + ); +#endif /* MICROPY_QPY_MISC_ADC */ + + diff --git a/ports/quectel/misc_power.c b/ports/quectel/misc_power.c new file mode 100644 index 0000000000000..93eafd8fcfc03 --- /dev/null +++ b/ports/quectel/misc_power.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "stdlib.h" +#include "mpconfigport.h" +#include "py/obj.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/mperrno.h" + +#if MICROPY_QPY_MISC_POWER + +#include "modmisc.h" +#include "helios_power.h" + +const mp_obj_type_t misc_power_type; + +static mp_obj_t misc_power_reset() +{ + int ret = 1; + Helios_Power_Reset(ret); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(misc_power_reset_obj, misc_power_reset); + + +static mp_obj_t misc_power_down() +{ + int ret = 1; + Helios_Power_Down(ret); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(misc_power_down_obj, misc_power_down); + +static mp_obj_t misc_power_get_down_reason() +{ + int ret; + ret = Helios_Power_GetDownReason(); + return mp_obj_new_int(ret); +} + +#if !defined(PLAT_RDA) +static MP_DEFINE_CONST_FUN_OBJ_0(misc_power_get_down_reason_obj, misc_power_get_down_reason); +#endif +static mp_obj_t misc_power_get_up_reason() +{ + int ret = 0; + ret = Helios_Power_GetUpReason(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_0(misc_power_get_up_reason_obj, misc_power_get_up_reason); + + +static mp_obj_t misc_power_get_batt() +{ + unsigned int ret; + ret = Helios_Power_GetBatteryVol(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_0(misc_power_get_batt_obj, misc_power_get_batt); + +static const mp_rom_map_elem_t misc_power_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_misc) }, + { MP_ROM_QSTR(MP_QSTR_powerRestart), MP_ROM_PTR(&misc_power_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_powerDown), MP_ROM_PTR(&misc_power_down_obj) }, + { MP_ROM_QSTR(MP_QSTR_powerOnReason), MP_ROM_PTR(&misc_power_get_up_reason_obj) }, +#if !defined(PLAT_RDA) + { MP_ROM_QSTR(MP_QSTR_powerDownReason), MP_ROM_PTR(&misc_power_get_down_reason_obj) }, +#endif + { MP_ROM_QSTR(MP_QSTR_getVbatt), MP_ROM_PTR(&misc_power_get_batt_obj) }, +}; + +static MP_DEFINE_CONST_DICT(misc_power_locals_dict, misc_power_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + misc_power_type, + MP_QSTR_Power, + MP_TYPE_FLAG_NONE, + locals_dict, &misc_power_locals_dict +); +#endif /* MICROPY_QPY_MISC_POWER */ diff --git a/ports/quectel/misc_pwm.c b/ports/quectel/misc_pwm.c new file mode 100644 index 0000000000000..bf592d1279a73 --- /dev/null +++ b/ports/quectel/misc_pwm.c @@ -0,0 +1,390 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include "runtime.h" + +#if MICROPY_QPY_MISC_PWM + +#include "helios_pwm.h" +#include "helios_debug.h" + + +#define PWM_LOG(msg, ...) custom_log("pwm", msg, ##__VA_ARGS__) + + + +// Forward dec'l +const mp_obj_type_t misc_pwm_type; +const mp_obj_type_t misc_pwm_v2_type; + +typedef enum +{ + PWM_V1=0, + PWM_V2 +}pwm_type_t; + +typedef struct _misc_pwm_obj_t { + mp_obj_base_t base; + unsigned int pin; + unsigned int cycle_range; + unsigned short high_time; + unsigned short cycle_time; + unsigned char duty; + float frequency; + pwm_type_t type_pwm; +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + unsigned char pwm_en_sleep; +#endif +} misc_pwm_obj_t; + +typedef enum HELIOS_PWM_CYCLE_RANGE_ENUM +{ + HELIOS_PWM_CYCLE_ABOVE_1US, + HELIOS_PWM_CYCLE_ABOVE_1MS, + HELIOS_PWM_CYCLE_ABOVE_10US, + HELIOS_PWM_CYCLE_ABOVE_BELOW_US, +} HELIOS_PWM_CYCLE_RANGE_E; + +static misc_pwm_obj_t *misc_pwm_obj[PWMMAX] = {NULL}; + +static void misc_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + misc_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "PWM(pin:%u high time:%u cycle_time:%u)", self->pin, self->high_time, self->cycle_time); +} + + +typedef struct { + uint16_t Left_coordinate; + uint16_t Right_coordinata; +}Helios_PWM_range; + + +//helios_pwm[0]:1US +//helios_pwm[1]:1ms +//helios_pwm[2]:10us +//helios_pwm[3]:ns +#if defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1602) +const Helios_PWM_range helios_pwm[4] = {{0, 157}, {0, 1023}, {1, 1575}, {0, 1024}}; + +#elif defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ECR6600) || defined(PLAT_Unisoc_8850_R02) || defined(PLAT_Unisoc_8910_R06) +const Helios_PWM_range helios_pwm[4] = {{0, 10000}, {0, 10}, {1, 10000}, {99, 65535}}; +#elif defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) +#elif defined(PLAT_EIGEN) +#elif defined(PLAT_Qualcomm) +#elif defined(PLAT_SONY_ALT1350) + +#else +#error "Please check whether the platform supports PWM" +#endif + + +#if !defined(PLAT_ASR_1803s) && !defined(PLAT_ASR_1803sc) && !defined(PLAT_EIGEN) && !defined(PLAT_Qualcomm) && !defined(PLAT_SONY_ALT1350) +static int check_pwm_para(uint16_t cycle_range, uint16_t cycle_time, uint16_t hight_time) { + + if(cycle_range > HELIOS_PWM_CYCLE_ABOVE_BELOW_US || + hight_time <= helios_pwm[cycle_range].Left_coordinate || hight_time > helios_pwm[cycle_range].Right_coordinata || + cycle_time <= helios_pwm[cycle_range].Left_coordinate || cycle_time > helios_pwm[cycle_range].Right_coordinata || + hight_time > cycle_time) { + return -1; + } + return 0; +} +#endif +static void misc_pwm_init_helper(misc_pwm_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_cycle_range, ARG_high_time, ARG_cycle_time }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_cycle_range, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_high_time, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_cycle_time, MP_ARG_INT, {.u_int = 0} }, +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + { MP_QSTR_pwm_en_sleep, MP_ARG_INT, {.u_int = 0} }, +#endif + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + //PWM_LOG("cycle_range=%d, high_time=%d, cycle_time=%d\n",args[ARG_cycle_range].u_int, args[ARG_high_time].u_int, args[ARG_cycle_time].u_int); +#if !(defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) \ + || defined(PLAT_EIGEN) || defined(PLAT_Qualcomm) || defined(PLAT_ECR6600) || defined(PLAT_ASR_1602) || defined(PLAT_SONY_ALT1350)) + if(-1 == check_pwm_para(args[ARG_cycle_range].u_int, args[ARG_cycle_time].u_int, args[ARG_high_time].u_int)) { + mp_raise_ValueError(MP_ERROR_TEXT("PWM parameter error, please refer to wiki instructions to correct the parameters")); + } +#endif + + self->cycle_range = args[ARG_cycle_range].u_int; + self->high_time = args[ARG_high_time].u_int; + self->cycle_time = args[ARG_cycle_time].u_int; + self->type_pwm = PWM_V1; + PWM_LOG("cycle_range=%d, high_time=%d, cycle_time=%d\n",self->cycle_range, self->high_time, self->cycle_time); + + if(Helios_PWM_Init(self->pin) != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("fail")); + } + +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + Helios_PWM_En_Sleep(self->pin,args[3].u_int); + PWM_LOG("Helios_PWM_En_Sleep->%d\n",args[3].u_int); +#endif +} + +static mp_obj_t misc_pwm_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, 4, 5, true); + int pin_id = mp_obj_get_int(args[0]); + + PWM_LOG("n_args = %d\n",n_args); + + if ((pin_id < 0) || (pin_id > PWMMAX-1)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid PWMn value, must be in {0~3}.")); + } + + // create PWM object from the given pin + if (misc_pwm_obj[pin_id] == NULL) + { + misc_pwm_obj[pin_id] = mp_obj_malloc_with_finaliser(misc_pwm_obj_t, &misc_pwm_type); + } + misc_pwm_obj_t *self = misc_pwm_obj[pin_id]; + self->base.type = &misc_pwm_type; + self->pin = pin_id; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + misc_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t misc_pwm_init(size_t n_args, + const mp_obj_t *args, mp_map_t *kw_args) { + misc_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(misc_pwm_init_obj, 1, misc_pwm_init); + +//pwm v2 init by pass duty and frequency 20220624 @jimmy +static void misc_pwm_v2_init_helper(misc_pwm_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) +{ + enum {ARG_frequency,ARG_duty}; + static const mp_arg_t allowed_args[] = + { + { MP_QSTR_frequency, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_duty, MP_ARG_INT, {.u_int = 0} }, +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + { MP_QSTR_pwm_en_sleep, MP_ARG_INT, {.u_int = 0} }, +#endif + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(mp_obj_is_float(args[ARG_frequency].u_obj) == 0) + { + mp_raise_TypeError(MP_ERROR_TEXT("frequency type isn't float")); + } + if(args[ARG_duty].u_int > 100) + { + mp_raise_ValueError(MP_ERROR_TEXT("PWM parameter error,range duty should be 0~100")); + } + self->duty = args[ARG_duty].u_int; + self->frequency = mp_obj_float_get(args[ARG_frequency].u_obj); + self->type_pwm = PWM_V2; + + if(Helios_PWM_Init(self->pin) != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("fail")); + } +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + Helios_PWM_En_Sleep(self->pin,args[2].u_int); + PWM_LOG("Helios_PWM_En_Sleep->%d\n",args[2].u_int); +#endif + +} +static mp_obj_t misc_pwm_v2_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, 3, 4, true); + int pin_id = mp_obj_get_int(args[0]); + + if ((pin_id < 0) || (pin_id > PWMMAX-1)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid PWMn value, must be in {0~3}.")); + } + + // create PWM object from the given pin + if (misc_pwm_obj[pin_id] == NULL) + { + misc_pwm_obj[pin_id] = mp_obj_malloc_with_finaliser(misc_pwm_obj_t, &misc_pwm_type); + } + misc_pwm_obj_t *self = misc_pwm_obj[pin_id]; + self->base.type = &misc_pwm_v2_type; + self->pin = pin_id; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + misc_pwm_v2_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t misc_pwm_enable(size_t n_args, const mp_obj_t *args) +{ + misc_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if(self->type_pwm == PWM_V1) + { + PWM_LOG("cycle_range=%d, high_time=%d, cycle_time=%d\n",self->cycle_range, self->high_time, self->cycle_time); + double frequency = 0; + float duty = 0; + if(n_args == 4) + { +#if !(defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) \ + || defined(PLAT_EIGEN) || defined(PLAT_Qualcomm) || defined(PLAT_ECR6600) || defined(PLAT_ASR_1602) || defined(PLAT_SONY_ALT1350)) + if(-1 == check_pwm_para(mp_obj_get_int(args[1]), mp_obj_get_int(args[3]), mp_obj_get_int(args[2]))) + { + mp_raise_ValueError(MP_ERROR_TEXT("PWM parameter error, please refer to wiki instructions to correct the parameters")); + } +#endif + self->cycle_range = mp_obj_get_int(args[1]); + self->cycle_time = mp_obj_get_int(args[3]); + self->high_time = mp_obj_get_int(args[2]); + } + + if(self->cycle_range == HELIOS_PWM_CYCLE_ABOVE_1US || self->cycle_range == HELIOS_PWM_CYCLE_ABOVE_10US) + { + frequency = 1000000 /(double)self->cycle_time; + } + else if (self->cycle_range == HELIOS_PWM_CYCLE_ABOVE_1MS) + { + frequency = 1000 /(double)self->cycle_time; + } + else if(self->cycle_range == HELIOS_PWM_CYCLE_ABOVE_BELOW_US) + { + frequency = 1000000000 /(double)self->cycle_time; + } + duty = (float)self->high_time / (float)self->cycle_time; + + PWM_LOG("misc_pwm_enable = %lf %f\n",frequency, duty); + + int ret = Helios_PWM_Start(self->pin, frequency, duty); + + return mp_obj_new_int(ret); + } + else + { + if(n_args == 3) + { + if((mp_obj_get_int(args[2]) < 0) || (mp_obj_get_int(args[2]) > 100)) + { + mp_raise_ValueError(MP_ERROR_TEXT("PWM parameter error,range duty should be 0~100")); + } + if(mp_obj_is_float(args[1]) == 0) + { + mp_raise_TypeError(MP_ERROR_TEXT("frequency type isn't float")); + } + self->frequency = mp_obj_float_get(args[1]); + self->duty = mp_obj_get_int(args[2]); + } + PWM_LOG("duty=%d, frequency=%f\n",self->duty, self->frequency); + + int ret = Helios_PWM_Start(self->pin, self->frequency, (self->duty*1.0)/100); + + return mp_obj_new_int(ret); + } +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(misc_pwm_enable_obj, 1, 4, misc_pwm_enable); + +static mp_obj_t misc_pwm_disable(mp_obj_t self_in) +{ + misc_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int ret = Helios_PWM_Stop(self->pin); + if (ret != 0) + { + ret = -1; + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(misc_pwm_disable_obj, misc_pwm_disable); + +static mp_obj_t misc_pwm_deinit(mp_obj_t self_in) +{ + misc_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + + misc_pwm_obj[self->pin] = NULL; + + int ret = Helios_PWM_Deinit((Helios_PwnNum)self->pin); + if (ret != 0) + { + PWM_LOG("pwm%d deinit failed.\r\n", self->pin); + } + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(misc_pwm_deinit_obj, misc_pwm_deinit); + +static const mp_rom_map_elem_t misc_pwm_locals_dict_table[] = { + // { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&misc_pwm_init_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&misc_pwm_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&misc_pwm_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&misc_pwm_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_ABOVE_1US), MP_ROM_INT(HELIOS_PWM_CYCLE_ABOVE_1US) }, + { MP_ROM_QSTR(MP_QSTR_ABOVE_MS), MP_ROM_INT(HELIOS_PWM_CYCLE_ABOVE_1MS) }, + { MP_ROM_QSTR(MP_QSTR_ABOVE_10US), MP_ROM_INT(HELIOS_PWM_CYCLE_ABOVE_10US) }, + { MP_ROM_QSTR(MP_QSTR_ABOVE_BELOW_US), MP_ROM_INT(HELIOS_PWM_CYCLE_ABOVE_BELOW_US) }, + { MP_ROM_QSTR(MP_QSTR_PWM0), MP_ROM_INT(PWM0) }, + #if !defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) || defined(PLAT_SONY_ALT1350) + PLAT_PWM_DEF(PLAT_PWM_NUMMAX), + #endif +}; + +static MP_DEFINE_CONST_DICT(misc_pwm_locals_dict, + misc_pwm_locals_dict_table); + + +MP_DEFINE_CONST_OBJ_TYPE( + misc_pwm_type, + MP_QSTR_PWM, + MP_TYPE_FLAG_NONE, + make_new, misc_pwm_make_new, + locals_dict, &misc_pwm_locals_dict + ); + +MP_DEFINE_CONST_OBJ_TYPE( + misc_pwm_v2_type, + MP_QSTR_PWM_V2, + MP_TYPE_FLAG_NONE, + make_new, misc_pwm_v2_make_new, + locals_dict, &misc_pwm_locals_dict + ); + +#endif /* MICROPY_QPY_MISC_PWM */ + diff --git a/ports/quectel/misc_usbnet.c b/ports/quectel/misc_usbnet.c new file mode 100644 index 0000000000000..24f61cc07b6f1 --- /dev/null +++ b/ports/quectel/misc_usbnet.c @@ -0,0 +1,213 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include + +#include "mpconfigport.h" +#include "py/obj.h" +#include "py/compile.h" +#include "py/runtime.h" + +#if MICROPY_QPY_MISC_USBNET + +#include "helios_usbnet.h" +#include "helios_debug.h" + +#define HELIOS_MODUSB_LOG(msg, ...) custom_log("modusbnet", msg, ##__VA_ARGS__) + +STATIC mp_obj_t misc_usbnet_set_type(mp_obj_t type_in) +{ + int type = mp_obj_get_int(type_in); + if (type != HELIOS_USBNET_TYPE_ECM && type != HELIOS_USBNET_TYPE_RNDIS) + { + HELIOS_MODUSB_LOG("USBNET type must be Type_ECM or Type_RNDIS."); + return mp_obj_new_int(-1); + } + int ret = Helios_USBNET_SetType(type); + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(misc_usbnet_set_type_obj, misc_usbnet_set_type); + +STATIC mp_obj_t misc_usbnet_get_type(void) +{ + Helios_USBNET_Type_e type = 0; + int ret = Helios_USBNET_GetType(&type); + if (ret == 0) + { + ret = type; + } + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(misc_usbnet_get_type_obj, misc_usbnet_get_type); + +STATIC mp_obj_t misc_usbnet_get_status(void) +{ + uint8_t status = 0; + int ret = Helios_USBNET_GetStatus(&status); + if (ret == 0) + { + ret = status; + } + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(misc_usbnet_get_status_obj, misc_usbnet_get_status); + +STATIC mp_obj_t misc_usbnet_open(void) +{ + int ret = Helios_USBNET_Open(); + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(misc_usbnet_open_obj, misc_usbnet_open); + +STATIC mp_obj_t misc_usbnet_close(void) +{ + int ret = Helios_USBNET_Close(); + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(misc_usbnet_close_obj, misc_usbnet_close); + +#if defined(PLAT_Unisoc) +STATIC mp_obj_t misc_usbnet_get_nat(size_t n_args, const mp_obj_t *args) +{ + if (n_args == 2) + { + uint8_t sim_id = mp_obj_get_int(args[0]); + uint8_t profile_id = mp_obj_get_int(args[1]); + if (sim_id != 0) + { + HELIOS_MODUSB_LOG("invalid value, now simId only supports 0."); + return mp_obj_new_int(-1); + } + if (profile_id < 1 || profile_id > 8) + { + HELIOS_MODUSB_LOG("invalid value, profileIdx should be in [1,8]."); + return mp_obj_new_int(-1); + } + int nat = Helios_USBNET_GetNat(sim_id, profile_id); + return mp_obj_new_int(nat); + } + return mp_obj_new_int(-1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(misc_usbnet_get_nat_obj, 2, 2, misc_usbnet_get_nat); + +STATIC mp_obj_t misc_usbnet_set_nat(size_t n_args, const mp_obj_t *args) +{ + if (n_args == 3) + { + uint8_t sim_id = mp_obj_get_int(args[0]); + uint8_t profile_id = mp_obj_get_int(args[1]); + uint8_t nat = mp_obj_get_int(args[2]); + if (sim_id != 0) + { + HELIOS_MODUSB_LOG("invalid value, now simId only supports 0."); + return mp_obj_new_int(-1); + } + if (profile_id < 1 || profile_id > 8) + { + HELIOS_MODUSB_LOG("invalid value, profileIdx should be in [1,8]."); + return mp_obj_new_int(-1); + } + if (nat != 0 && nat != 1) + { + HELIOS_MODUSB_LOG("invalid value, Nat should be in [0,1]."); + return mp_obj_new_int(-1); + } + int ret = Helios_USBNET_SetNat(sim_id, profile_id, nat); + return mp_obj_new_int(ret); + } + return mp_obj_new_int(-1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(misc_usbnet_set_nat_obj, 3, 3, misc_usbnet_set_nat); +#endif + +#if defined(PLAT_Unisoc_8850) +STATIC mp_obj_t misc_usbnet_set_MAC(mp_obj_t macinfo) +{ + mp_buffer_info_t m_macinfo; + unsigned char mac[6]; + mp_get_buffer_raise(macinfo, &m_macinfo, MP_BUFFER_READ); + if( m_macinfo.len != 6 ) { + HELIOS_MODUSB_LOG("mac length error"); + return mp_obj_new_int(-1); + } + memcpy(mac, m_macinfo.buf, 6); + + int ret = Helios_USBNET_SetMAC(mac); + + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(misc_usbnet_set_MAC_obj, misc_usbnet_set_MAC); + +STATIC mp_obj_t misc_usbnet_set_product(mp_obj_t macinfo) +{ + mp_buffer_info_t m_product; + char product[32]; + mp_get_buffer_raise(macinfo, &m_product, MP_BUFFER_READ); + if( m_product.len > 16 ) { + HELIOS_MODUSB_LOG("product length error"); + return mp_obj_new_int(-1); + } + memset(product, 0x00, sizeof(product)); + strncpy(product, m_product.buf, m_product.len); + + int ret = Helios_USBNET_SetProduct(product); + + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(misc_usbnet_set_product_obj, misc_usbnet_set_product); + +#endif + +STATIC const mp_rom_map_elem_t misc_usbnet_locals_dict_table[] = { + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_USBNET)}, + {MP_ROM_QSTR(MP_QSTR_set_worktype), MP_ROM_PTR(&misc_usbnet_set_type_obj)}, + {MP_ROM_QSTR(MP_QSTR_get_worktype), MP_ROM_PTR(&misc_usbnet_get_type_obj)}, + {MP_ROM_QSTR(MP_QSTR_get_status), MP_ROM_PTR(&misc_usbnet_get_status_obj)}, + {MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&misc_usbnet_open_obj)}, + {MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&misc_usbnet_close_obj)}, + {MP_ROM_QSTR(MP_QSTR_Type_ECM), MP_ROM_INT(HELIOS_USBNET_TYPE_ECM)}, + {MP_ROM_QSTR(MP_QSTR_Type_RNDIS), MP_ROM_INT(HELIOS_USBNET_TYPE_RNDIS)}, +#if defined(PLAT_Unisoc) + {MP_ROM_QSTR(MP_QSTR_getNat), MP_ROM_PTR(&misc_usbnet_get_nat_obj)}, + {MP_ROM_QSTR(MP_QSTR_setNat), MP_ROM_PTR(&misc_usbnet_set_nat_obj)}, +#endif +#if defined(PLAT_Unisoc_8850) + {MP_ROM_QSTR(MP_QSTR_setMAC), MP_ROM_PTR(&misc_usbnet_set_MAC_obj)}, + {MP_ROM_QSTR(MP_QSTR_setProduct), MP_ROM_PTR(&misc_usbnet_set_product_obj)}, +#endif +}; + +STATIC MP_DEFINE_CONST_DICT(misc_usbnet_locals_dict, misc_usbnet_locals_dict_table); + +const mp_obj_module_t misc_usbnet_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&misc_usbnet_locals_dict, +}; + +#endif /* MICROPY_QPY_MISC_USBNET */ diff --git a/ports/quectel/moddatacall.c b/ports/quectel/moddatacall.c new file mode 100644 index 0000000000000..8d264f170469c --- /dev/null +++ b/ports/quectel/moddatacall.c @@ -0,0 +1,833 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include + +#include "nlr.h" +#include "objlist.h" +#include "objstr.h" +#include "runtime.h" +#include "mperrno.h" + +#if MICROPY_QPY_MODULE_DATACALL + +#include "helios_debug.h" +#include "helios_datacall.h" +#include "callbackdeal.h" + +#if MICROPY_QPY_MODULE_WANINFO +#include "netif.h" +#include "ip6_addr.h" +#endif + +#define MOD_DATACALL_LOG(msg, ...) custom_log(DataCall, msg, ##__VA_ARGS__) + +extern int _get_current_simid(void); + +static mp_obj_t qpy_datacall_set_pdp_context(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + int ip_type = mp_obj_get_int(args[1]); + int auth_type = mp_obj_get_int(args[5]); + + mp_buffer_info_t apninfo = {0}; + mp_buffer_info_t usrinfo = {0}; + mp_buffer_info_t pwdinfo = {0}; + mp_get_buffer_raise(args[2], &apninfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &usrinfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[4], &pwdinfo, MP_BUFFER_READ); + + int min_profile_id = (int)Helios_DataCall_GetProfileIdxMin(); + int max_profile_id = (int)Helios_DataCall_GetProfileIdxMax(); + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + if ((ip_type < 0) || (ip_type > HELIOS_PDP_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, ipType should be in [0~%d]."), HELIOS_PDP_TYPE_NUM - 1); + } + if ((auth_type < 0) || (auth_type > HELIOS_AUTH_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, authType should be in [0~%d]."), HELIOS_AUTH_TYPE_NUM - 1); + } + if (apninfo.len > HELIOS_APN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of apn should be no more than [%d] bytes."), HELIOS_APN_LEN_MAX); + } + if (usrinfo.len > HELIOS_USR_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of username should be no more than [%d] bytes."), HELIOS_USR_LEN_MAX); + } + if (pwdinfo.len > HELIOS_PWD_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of password should be no more than [%d] bytes."), HELIOS_PWD_LEN_MAX); + } + if (usrinfo.len == 0 && pwdinfo.len == 0 && auth_type != 0) { + auth_type = 0; + } + + int cur_simid = _get_current_simid(); + if (n_args ==7) { + cur_simid = mp_obj_get_int(args[6]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + + Helios_DataCallStartStruct pdp_context = {0}; + pdp_context.ip_type = ip_type; + pdp_context.auth = auth_type; + memcpy((void *)pdp_context.apn, apninfo.buf, apninfo.len); + memcpy((void *)pdp_context.user, usrinfo.buf, usrinfo.len); + memcpy((void *)pdp_context.pwd, pwdinfo.buf, pwdinfo.len); + MP_THREAD_GIL_EXIT(); + #if MICROPY_QPY_MODULE_DSDS + ret = Helios_DataCall_SetPDPContext(cur_simid,profile_id, &pdp_context); + #else + ret = Helios_DataCall_SetPDPContext(profile_id, &pdp_context); + #endif + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_set_pdp_context_obj, 6, 7, qpy_datacall_set_pdp_context); + + +static mp_obj_t qpy_datacall_get_pdp_context(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + Helios_DataCallStartStruct pdp_context = {0}; + + int min_profile_id = (int)Helios_DataCall_GetProfileIdxMin(); + int max_profile_id = (int)Helios_DataCall_GetProfileIdxMax(); + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + int cur_simid = _get_current_simid(); + if (n_args ==2) { + cur_simid = mp_obj_get_int(args[1]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + #if MICROPY_QPY_MODULE_DSDS + ret = Helios_DataCall_GetPDPContext(cur_simid,profile_id, &pdp_context); + #else + ret = Helios_DataCall_GetPDPContext(profile_id, &pdp_context); + #endif + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + mp_obj_t pdp_ctx[5] = + { + mp_obj_new_int(pdp_context.ip_type), + mp_obj_new_str(pdp_context.apn, strlen(pdp_context.apn)), + mp_obj_new_str(pdp_context.user, strlen(pdp_context.user)), + mp_obj_new_str(pdp_context.pwd, strlen(pdp_context.pwd)), + mp_obj_new_int(pdp_context.auth) + }; + return mp_obj_new_tuple(5, pdp_ctx); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_pdp_context_obj, 1, 2, qpy_datacall_get_pdp_context); + +/*=============================================================================*/ +/* FUNCTION: qpy_datacall_start */ +/*=============================================================================*/ +/*!@brief : set APN and datacall. + * @profile_idx [in] profile_idx, 1~HELIOS_PROFILE_IDX_MAX + * @ip_type [in] 0-IPV4, 1-IPV6, 2-IPV4 and IPV6 + * @apn_name [in] anp name + * @usr_name [in] user name + * @password [in] password + * @auth_type [in] auth_type, 0-None, 1-PAP, 2-CHAP + * @return : + * - 0--successful + * - -1--error + */ +/*=============================================================================*/ +static mp_obj_t qpy_datacall_start(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + int ip_type = mp_obj_get_int(args[1]); + int auth_type = mp_obj_get_int(args[5]); + + mp_buffer_info_t apninfo = {0}; + mp_buffer_info_t usrinfo = {0}; + mp_buffer_info_t pwdinfo = {0}; + mp_get_buffer_raise(args[2], &apninfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &usrinfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[4], &pwdinfo, MP_BUFFER_READ); + + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + if ((ip_type < 0) || (ip_type > HELIOS_PDP_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, ipType should be in [0~%d]."), HELIOS_PDP_TYPE_NUM - 1); + } + if ((auth_type < 0) || (auth_type > HELIOS_AUTH_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, authType should be in [0~%d]."), HELIOS_AUTH_TYPE_NUM - 1); + } + if (apninfo.len > HELIOS_APN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of apn should be no more than [%d] bytes."), HELIOS_APN_LEN_MAX); + } + if (usrinfo.len > HELIOS_USR_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of username should be no more than [%d] bytes."), HELIOS_USR_LEN_MAX); + } + if (pwdinfo.len > HELIOS_PWD_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of password should be no more than [%d] bytes."), HELIOS_PWD_LEN_MAX); + } + + MOD_DATACALL_LOG("[datacall] profile_idx=%d, ip_version=%d, auth_type=%d\r\n", profile_id, ip_type, auth_type); + MOD_DATACALL_LOG("[datacall] anp_name=%s, usr_name=%s, password=%s\r\n", apninfo.buf, usrinfo.buf, pwdinfo.buf); + + Helios_DataCallStartStruct DataCallStartStruct = {0}; + DataCallStartStruct.ip_type = (int32_t)ip_type; + DataCallStartStruct.auth = (int32_t)auth_type; + snprintf(DataCallStartStruct.apn, sizeof(DataCallStartStruct.apn), "%s", (char *)apninfo.buf); + snprintf(DataCallStartStruct.user, sizeof(DataCallStartStruct.user), "%s", (char *)usrinfo.buf); + snprintf(DataCallStartStruct.pwd, sizeof(DataCallStartStruct.pwd), "%s", (char *)pwdinfo.buf); + + int cur_simid = _get_current_simid(); + if (n_args ==7) { + cur_simid = mp_obj_get_int(args[6]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_Start(profile_id, cur_simid, &DataCallStartStruct); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_start_obj, 5, 7, qpy_datacall_start); + + +static mp_obj_t qpy_datacall_stop(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + int ip_type = mp_obj_get_int(args[1]); + int cur_simid = _get_current_simid(); + + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + if ((ip_type < 0) || (ip_type > 2)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, ipTpye should be in [0,2].")); + } + if (n_args ==3) { + cur_simid = mp_obj_get_int(args[2]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_Stop((int32_t)profile_id, cur_simid, (int32_t)ip_type); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_stop_obj, 2, 3, qpy_datacall_stop); + + +/*=============================================================================*/ +/* FUNCTION: qpy_datacall_set_autoconnect */ +/*=============================================================================*/ +/*!@brief : set auto connect + * @profile_idx [in] profile_idx, 1~7 + * @enable [in] 0-disable, 1-enable + * @return : + * - 0--successful + * - -1--error + */ +/*=============================================================================*/ +static mp_obj_t qpy_datacall_set_autoconnect(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + bool enable = mp_obj_is_true(args[1]); + + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + int cur_simid = _get_current_simid(); + if (n_args ==3) { + cur_simid = mp_obj_get_int(args[2]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + ret = Helios_DataCall_SetAutoConnect((int32_t)profile_id, cur_simid, enable); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_set_autoconnect_obj, 1, 3, qpy_datacall_set_autoconnect); + + +/*=============================================================================*/ +/* FUNCTION: qpy_datacall_set_asynmode */ +/*=============================================================================*/ +/*!@brief : Set asynchronous mode to datacall + * @mode [in] 0-disable, 1-enable + * @return : + * - 0--successful + * - -1--error + */ +/*=============================================================================*/ +static mp_obj_t qpy_datacall_set_asynmode(mp_obj_t mode) +{ + bool enable = mp_obj_is_true(mode); + int cur_simid = _get_current_simid(); + Helios_DataCall_SetAsynMode((int32_t)Helios_DataCall_GetCurrentPDP(), cur_simid, enable); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_datacall_set_asynmode_obj, qpy_datacall_set_asynmode); + + +static mp_obj_t qpy_datacall_get_info(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int profile_id = mp_obj_get_int(args[0]); + int ip_type = mp_obj_get_int(args[1]); + char ip4_ip_addr[16] = {0}; + char ip4_pri_dns[16] = {0}; + char ip4_sec_dns[16] = {0}; + char ip6_ip_addr[64] = {0}; + char ip6_pri_dns[64] = {0}; + char ip6_sec_dns[64] = {0}; + Helios_DataCallInfoStruct info = {0}; + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileID should be in [%d,%d]."), min_profile_id, max_profile_id); + } + if ((ip_type < 0) || (ip_type > HELIOS_PDP_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, ipType should be in [0~%d]."), HELIOS_PDP_TYPE_NUM - 1); + } + info.ip_version = ip_type; + int cur_simid = _get_current_simid(); + if (n_args ==3) { + cur_simid = mp_obj_get_int(args[2]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_GetInfo(profile_id, cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (0 == ret) + { + inet_ntop(AF_INET, &info.v4.addr.ip, ip4_ip_addr, sizeof(ip4_ip_addr)); + inet_ntop(AF_INET, &info.v4.addr.pri_dns, ip4_pri_dns, sizeof(ip4_pri_dns)); + inet_ntop(AF_INET, &info.v4.addr.sec_dns, ip4_sec_dns, sizeof(ip4_sec_dns)); + inet_ntop(AF_INET6, &info.v6.addr.ip, ip6_ip_addr, sizeof(ip6_ip_addr)); + inet_ntop(AF_INET6, &info.v6.addr.pri_dns, ip6_pri_dns, sizeof(ip6_pri_dns)); + inet_ntop(AF_INET6, &info.v6.addr.sec_dns, ip6_sec_dns, sizeof(ip6_sec_dns)); + + mp_obj_t ip4_list[5] = { + mp_obj_new_int(info.v4.state), + mp_obj_new_int(info.v4.reconnect), + + mp_obj_new_str(ip4_ip_addr, strlen(ip4_ip_addr)), + mp_obj_new_str(ip4_pri_dns, strlen(ip4_pri_dns)), + mp_obj_new_str(ip4_sec_dns, strlen(ip4_sec_dns)), + }; + mp_obj_t ip6_list[5] = { + mp_obj_new_int(info.v6.state), + mp_obj_new_int(info.v6.reconnect), + + mp_obj_new_str(ip6_ip_addr, strlen(ip6_ip_addr)), + mp_obj_new_str(ip6_pri_dns, strlen(ip6_pri_dns)), + mp_obj_new_str(ip6_sec_dns, strlen(ip6_sec_dns)), + }; + + if (ip_type == 0) + { + mp_obj_t tuple[3] = { + mp_obj_new_int(info.profile_idx), + mp_obj_new_int(info.ip_version), + mp_obj_new_list(5, ip4_list), + }; + return mp_obj_new_tuple(3, tuple); + } + else if (ip_type == 1) + { + mp_obj_t tuple[3] = { + mp_obj_new_int(info.profile_idx), + mp_obj_new_int(info.ip_version), + mp_obj_new_list(5, ip6_list), + }; + return mp_obj_new_tuple(3, tuple); + } + else if (ip_type == 2) + { + mp_obj_t tuple[4] = { + mp_obj_new_int(info.profile_idx), + mp_obj_new_int(info.ip_version), + mp_obj_new_list(5, ip4_list), + mp_obj_new_list(5, ip6_list), + }; + + return mp_obj_new_tuple(4, tuple); + } + } + return mp_obj_new_int(ret); +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_info_obj, 1, 3, qpy_datacall_get_info); + + + +static c_callback_t * g_usr_callback = NULL; + +static void datacall_callback(int32_t profile_idx, uint8_t sim_id, int32_t nw_status) +{ + + MOD_DATACALL_LOG("[datacall] pdp = %d, nwsta = %d\r\n", profile_idx, nw_status); + if (g_usr_callback) + { + st_CallBack_Datacall *Datacall_param = (st_CallBack_Datacall *)malloc(sizeof(st_CallBack_Datacall)); + if(NULL != Datacall_param) + { + Datacall_param->profile_idx = profile_idx; + Datacall_param->nw_status = nw_status; + Datacall_param->sim_id = sim_id; + Datacall_param->callback = *g_usr_callback; + + qpy_send_msg_to_callback_deal_thread(CALLBACK_TYPE_ID_DATACALL, Datacall_param); + } + } +} + + +/*=============================================================================*/ +/* FUNCTION: qpy_datacall_register_usr_callback */ +/*=============================================================================*/ +/*!@brief : register the callback function for user + * @user_cb [in] callback function + * @return : + * - 0--successful + * - -1--error + */ +/*=============================================================================*/ +static mp_obj_t qpy_datacall_register_usr_callback(mp_obj_t callback) +{ + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + cb.arg = mp_obj_new_tuple(3, NULL); + g_usr_callback = &cb; + mp_sched_schedule_callback_register(g_usr_callback, callback); + + Helios_DataCallInitStruct DataCallInitStruct = {datacall_callback}; + int cur_simid = _get_current_simid(); + Helios_DataCall_Init((int32_t)Helios_DataCall_GetCurrentPDP(), cur_simid, &DataCallInitStruct); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_register_usr_callback_obj, qpy_datacall_register_usr_callback); + +static mp_obj_t qpy_datacall_get_pdp_range(void) +{ + uint32_t min = Helios_DataCall_GetProfileIdxMin(); + uint32_t max = Helios_DataCall_GetProfileIdxMax(); + mp_obj_t tuple[2] = + { + mp_obj_new_int(min), + mp_obj_new_int(max) + }; + + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_get_pdp_range_obj, qpy_datacall_get_pdp_range); + +static mp_obj_t qpy_datacall_get_range(mp_obj_t kind) +{ + int para_kind = mp_obj_get_int(kind); + + if (para_kind == 0) //apn + { + return mp_obj_new_int(HELIOS_APN_LEN_MAX); + } + else if (para_kind == 1) //password + { + return mp_obj_new_int(HELIOS_PWD_LEN_MAX); + } + else if (para_kind == 2) //usrname + { + return mp_obj_new_int(HELIOS_USR_LEN_MAX); + } + else if (para_kind == 3) //pid + { + return mp_obj_new_int(HELIOS_PROFILE_IDX_MAX); + } + else if (para_kind == 4) //iptype + { + return mp_obj_new_int(HELIOS_PDP_TYPE_NUM); + } + else if (para_kind == 5) //authtype + { + return mp_obj_new_int(HELIOS_AUTH_TYPE_NUM); + } + else + { + return mp_obj_new_int(-1); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_datacall_get_range_obj, qpy_datacall_get_range); + +static mp_obj_t qpy_datacall_get_apn(size_t n_args, const mp_obj_t *args) +{ +#if defined(PLAT_ASR) || defined(PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc)|| defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) \ + || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ASR_1602) || defined(PLAT_Unisoc_8910_R06) + int sim_id = mp_obj_get_int(args[0]); + int ret = 0; + char apn[99+1] = {0}; + if (sim_id != 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + if (n_args == 1) + { + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_GetApn(2, sim_id, apn); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_str(apn, strlen(apn)); + } + return mp_obj_new_int(-1); + } + else if (n_args == 2) + { + int pid = mp_obj_get_int(args[1]); + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_GetApn(3, sim_id, apn, pid); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_str(apn, strlen(apn)); + } + return mp_obj_new_int(-1); + } + else + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, The number of parameters cannot be greater than 2.")); + } +#else + mp_raise_ValueError(MP_ERROR_TEXT("NOT SUPPORT")); +#endif +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_apn_obj, 1, 2, qpy_datacall_get_apn); + +#if defined(PLAT_ASR) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) +static mp_obj_t qpy_datacall_set_dns_server(size_t n_args, const mp_obj_t *args) +{ + int profile_id = mp_obj_get_int(args[0]); + int sim_id = mp_obj_get_int(args[1]); + + mp_buffer_info_t new_pri_dns = {0}; + mp_buffer_info_t new_sec_dns = {0}; + mp_get_buffer_raise(args[2], &new_pri_dns, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &new_sec_dns, MP_BUFFER_READ); + + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, "invalid value, profileID should be in [%d,%d].", min_profile_id, max_profile_id); + } + + char new_pri[128]; + char new_sec[128]; + memset(&new_pri, 0, sizeof(new_pri)); + memset(&new_sec, 0, sizeof(new_sec)); + + memcpy(new_pri, new_pri_dns.buf, new_pri_dns.len); + memcpy(new_sec, new_sec_dns.buf, new_sec_dns.len); + + int ret = 0; + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_SetDnsServer(profile_id, sim_id, new_pri, new_sec); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_set_dns_server_obj, 4, 4, qpy_datacall_set_dns_server); +#endif + +static mp_obj_t qpy_datacall_get_date_speed(size_t n_args, const mp_obj_t *args) +{ + uint32_t rx = 0, tx = 0; +#if defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) + MP_THREAD_GIL_EXIT(); + Helios_DataCall_GetDataSpeed(&rx, &tx); + MP_THREAD_GIL_ENTER(); +#endif + mp_obj_t date_speed[2] = { + mp_obj_new_int_from_uint(rx), //bps + mp_obj_new_int_from_uint(tx), //bps + }; + return mp_obj_new_tuple(2,date_speed); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_date_speed_obj, 0, 0, qpy_datacall_get_date_speed); + +static mp_obj_t qpy_datacall_get_address_info(size_t n_args, const mp_obj_t *args) +{ + int profile_id = mp_obj_get_int(args[0]); + int ip_type = 0; + + if (n_args == 2) + { + ip_type = mp_obj_get_int(args[1]); + } + +#if MICROPY_QPY_MODULE_WANINFO + struct netif* netif_cfg = netif_get_by_cid(profile_id-1); + + if(netif_cfg) + { + static uint8_t g_mac[6] = {0}; + g_mac[0] = netif_cfg->hwaddr[0]; + g_mac[1] = netif_cfg->hwaddr[1]; + g_mac[2] = netif_cfg->hwaddr[2]; + g_mac[3] = netif_cfg->hwaddr[3]; + g_mac[4] = netif_cfg->hwaddr[4]; + g_mac[5] = netif_cfg->hwaddr[5]; + + char mac[64] = {0}; + sprintf(mac, "%02X-%02X-%02X-%02X-%02X-%02X", g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]); + + if (ip_type == 0)//IPV4 + { + char mask[16] = {0}; + char gw[16] = {0}; + + struct sockaddr_in local_v4; + + local_v4.sin_addr.s_addr = netif_cfg->netmask.addr; + inet_ntop(AF_INET, &local_v4.sin_addr.s_addr, mask, sizeof(mask)); + + local_v4.sin_addr.s_addr = netif_cfg->gw.addr; + inet_ntop(AF_INET, &local_v4.sin_addr.s_addr, gw, sizeof(gw)); + + mp_obj_t addrinfo_tuple[3] = { + mp_obj_new_str(mac, strlen(mac)), + mp_obj_new_str(mask, strlen(mask)), + mp_obj_new_str(gw, strlen(gw)) + }; + + return mp_obj_new_tuple(3,addrinfo_tuple); + } + else if (ip_type == 1)//IPV6 + { + struct sockaddr_in6 local_v6; + char ip6_gw[64] = {0}; + ip6_addr_t * ip6_addr_ptr = NULL; + + ip6_addr_ptr = &(netif_cfg->ip6_gw); + memcpy(&local_v6.sin6_addr, ip6_addr_ptr, sizeof(ip6_addr_t)); + inet_ntop(AF_INET6, &local_v6.sin6_addr, ip6_gw, sizeof(ip6_gw)); + + mp_obj_t addrinfo_tuple[2] = { + mp_obj_new_str(mac, strlen(mac)), + mp_obj_new_str(ip6_gw, strlen(ip6_gw)) + }; + + return mp_obj_new_tuple(2,addrinfo_tuple); + } + else if (ip_type == 2)//IPV4&6 + { + char mask[16] = {0}; + char gw[16] = {0}; + + struct sockaddr_in local_v4; + + local_v4.sin_addr.s_addr = netif_cfg->netmask.addr; + inet_ntop(AF_INET, &local_v4.sin_addr.s_addr, mask, sizeof(mask)); + + local_v4.sin_addr.s_addr = netif_cfg->gw.addr; + inet_ntop(AF_INET, &local_v4.sin_addr.s_addr, gw, sizeof(gw)); + + mp_obj_t addrinfo_tuple[3] = { + mp_obj_new_str(mac, strlen(mac)), + mp_obj_new_str(mask, strlen(mask)), + mp_obj_new_str(gw, strlen(gw)) + }; + + char ip6_gw[64] = {0}; + struct sockaddr_in6 local_v6; + ip6_addr_t * ip6_addr_ptr = NULL; + + ip6_addr_ptr = &(netif_cfg->ip6_gw); + memcpy(&local_v6.sin6_addr, ip6_addr_ptr, sizeof(ip6_addr_t)); + inet_ntop(AF_INET6, &local_v6.sin6_addr, ip6_gw, sizeof(ip6_gw)); + + mp_obj_t addr6info_tuple[2] = { + mp_obj_new_str(mac, strlen(mac)), + mp_obj_new_str(ip6_gw, strlen(ip6_gw)) + }; + + mp_obj_t ipconfig_list[2] = { + mp_obj_new_tuple(3, addrinfo_tuple), + mp_obj_new_tuple(2, addr6info_tuple) + }; + return mp_obj_new_list(2, ipconfig_list); + } + } +#else + (void)profile_id;(void)ip_type; +#endif + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_address_info_obj, 1, 2, qpy_datacall_get_address_info); + +static mp_obj_t qpy_datacall_get_sim_info(size_t n_args, const mp_obj_t *args) +{ +#if MICROPY_QPY_MODULE_WANINFO + int sim_id = mp_obj_get_int(args[0]); + Helios_DataCallSimUsed info; + + Helios_DataCall_GetSimUsed(sim_id, &info); + + mp_obj_t siminfo_tuple[4] = { + mp_obj_new_int(info.recv_MBytes_sim), + mp_obj_new_int(info.recv_Bytes_sim), + mp_obj_new_int(info.used_MBytes_sim), + mp_obj_new_int(info.used_Bytes_sim) + }; + + return mp_obj_new_tuple(4,siminfo_tuple); +#endif + + return mp_obj_new_int(-1); +}static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_get_sim_info_obj, 1, 1, qpy_datacall_get_sim_info); + +static mp_obj_t qpy_module_datacall_deinit(void) +{ + g_usr_callback = NULL; + MOD_DATACALL_LOG("module datacall deinit.\r\n"); +#if defined(PLAT_ASR) || defined(PLAT_Unisoc) || defined(PLAT_Qualcomm) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + Helios_DataCall_Deinit(); +#endif + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_module_datacall_deinit_obj, qpy_module_datacall_deinit); + + +static mp_obj_t qpy_datacall_use_attach_apn(size_t n_args, const mp_obj_t *args) +{ +#if defined(PLAT_ASR) + int ret = 0; + bool is_using_attach_apn = false; + if (n_args == 1) { + is_using_attach_apn = mp_obj_is_true(args[0]); + MP_THREAD_GIL_EXIT(); + ret = Helios_DataCall_SetIsUseAttachApn(is_using_attach_apn ? 0 : 1); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); + } else { + MP_THREAD_GIL_EXIT(); + is_using_attach_apn = Helios_DataCall_GetIsUseAttachApn(); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_bool(!is_using_attach_apn); + } +#else + mp_raise_ValueError(MP_ERROR_TEXT("NOT SUPPORT")); +#endif +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_datacall_use_attach_apn_obj, 0, 1, qpy_datacall_use_attach_apn); + + +static const mp_rom_map_elem_t mp_module_datacall_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_dial) }, + { MP_ROM_QSTR(MP_QSTR___qpy_module_deinit__), MP_ROM_PTR(&qpy_module_datacall_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_setPDPContext), MP_ROM_PTR(&qpy_datacall_set_pdp_context_obj) }, + { MP_ROM_QSTR(MP_QSTR_getPDPContext), MP_ROM_PTR(&qpy_datacall_get_pdp_context_obj) }, + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&qpy_datacall_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&qpy_datacall_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_setAutoConnect), MP_ROM_PTR(&qpy_datacall_set_autoconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_getInfo), MP_ROM_PTR(&qpy_datacall_get_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_setCallback), MP_ROM_PTR(&qpy_register_usr_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_setAsynMode), MP_ROM_PTR(&qpy_datacall_set_asynmode_obj) }, + { MP_ROM_QSTR(MP_QSTR_getPdpRange), MP_ROM_PTR(&qpy_get_pdp_range_obj) }, + { MP_ROM_QSTR(MP_QSTR_getRange), MP_ROM_PTR(&qpy_datacall_get_range_obj) }, + { MP_ROM_QSTR(MP_QSTR_getApn), MP_ROM_PTR(&qpy_datacall_get_apn_obj) }, + #if defined(PLAT_ASR) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + { MP_ROM_QSTR(MP_QSTR_setDnsserver), MP_ROM_PTR(&qpy_datacall_set_dns_server_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_useAttachApn), MP_ROM_PTR(&qpy_datacall_use_attach_apn_obj) }, + { MP_ROM_QSTR(MP_QSTR_getSpeed), MP_ROM_PTR(&qpy_datacall_get_date_speed_obj) }, + { MP_ROM_QSTR(MP_QSTR_getAddressinfo), MP_ROM_PTR(&qpy_datacall_get_address_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_getSiminfo), MP_ROM_PTR(&qpy_datacall_get_sim_info_obj) }, +}; +static MP_DEFINE_CONST_DICT(mp_module_datacall_globals, mp_module_datacall_globals_table); + + +const mp_obj_module_t mp_module_dial = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_datacall_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_dial, mp_module_dial); +#endif /* MICROPY_QPY_MODULE_DATACALL */ diff --git a/ports/quectel/moddatacall.h b/ports/quectel/moddatacall.h new file mode 100644 index 0000000000000..880548c5d494f --- /dev/null +++ b/ports/quectel/moddatacall.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MODDATACALL_H__ +#define __MODDATACALL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void(* nw_status_cb)(int profile_idx, int nw_status); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ports/quectel/moddev.c b/ports/quectel/moddev.c new file mode 100644 index 0000000000000..aa62f787b5c40 --- /dev/null +++ b/ports/quectel/moddev.c @@ -0,0 +1,288 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "obj.h" +#include "runtime.h" +#include "mphalport.h" + +#if MICROPY_QPY_MODULE_MODEM + +#include "helios_dev.h" + + +static mp_obj_t queclib_dev_product_id() +{ + char product_id_str[64] = {0}; + int ret = Helios_Dev_GetPID((void *)product_id_str, sizeof(product_id_str)); + if(ret == 0) + { + return mp_obj_new_str(product_id_str, strlen(product_id_str)); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_product_id_obj, queclib_dev_product_id); + +extern int _get_current_simid(void); + +static mp_obj_t queclib_dev_serial_number(size_t n_args, const mp_obj_t *args) +{ +#if !defined(PLAT_aic8800m40) && !defined(PLAT_ECR6600) && !defined(PLAT_SONY_ALT1350) + int cur_simid = _get_current_simid(); + if (n_args ==1) { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } +#endif + char serial_number_str[64] = {0}; + #if MICROPY_QPY_MODULE_DSDS + int ret = Helios_Dev_GetSN((void *)serial_number_str, sizeof(serial_number_str), cur_simid); + #else + int ret = Helios_Dev_GetSN((void *)serial_number_str, sizeof(serial_number_str),0); + #endif + if(ret == 0) + { + return mp_obj_new_str(serial_number_str, strlen(serial_number_str)); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(queclib_dev_serial_number_obj, 0, 1, queclib_dev_serial_number); + +static mp_obj_t queclib_dev_model() +{ + char model_str[64] = {0}; + int ret = Helios_Dev_GetModel((void *)model_str, sizeof(model_str)); + if(ret == 0) + { + return mp_obj_new_str(model_str, strlen(model_str)); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_model_obj, queclib_dev_model); + + +mp_obj_t queclib_dev_fw_version() +{ + char fw_version_str[64] = {0}; + int ret = Helios_Dev_GetFwVersion((void *)fw_version_str, sizeof(fw_version_str)); + if(ret == 0) + { + return mp_obj_new_str(fw_version_str, strlen(fw_version_str)); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_fw_version_obj, queclib_dev_fw_version); + + +#if !defined(PLAT_aic8800m40) && !defined(PLAT_ECR6600) + +static mp_obj_t queclib_dev_imei(size_t n_args, const mp_obj_t *args) +{ + #if !defined(PLAT_SONY_ALT1350) + int cur_simid = _get_current_simid(); + #else + int cur_simid = 0; + #endif + if (n_args ==1) { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + //the imei length is 15-17 bytes + char imei_str[19] = {0}; +#if MICROPY_QPY_MODULE_DSDS + int ret = Helios_Dev_GetIMEI((void *)imei_str, sizeof(imei_str), cur_simid); +#else + #if MICROPY_QPY_MODULE_DUALIMEI + cur_simid = (n_args ==1) ? cur_simid + 2 : 0; + int ret = Helios_Dev_GetIMEI((void *)imei_str, sizeof(imei_str),cur_simid); + #else + int ret = Helios_Dev_GetIMEI((void *)imei_str, sizeof(imei_str),0); + #endif +#endif + if(ret == 0) + { + return mp_obj_new_str(imei_str, strlen(imei_str)); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(queclib_dev_imei_obj, 0, 1, queclib_dev_imei); +#endif + +#if defined (PLAT_Qualcomm) +static mp_obj_t queclib_dev_backup() +{ + MP_THREAD_GIL_EXIT(); + int ret = Helios_Dev_Backup(); + MP_THREAD_GIL_ENTER(); + if( ret != 0 ) return mp_obj_new_int(-1); + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_backup_obj, queclib_dev_backup); + +static mp_obj_t queclib_dev_backup_status() +{ + Helios_DevBackupStatusStruct backup_status_info = {0}; + + int ret = Helios_Dev_Backup_Status(&backup_status_info); + if( ret == 0 ) + { + mp_obj_t info[3] = + { + mp_obj_new_int(backup_status_info.backup_valid), + mp_obj_new_int(backup_status_info.backup_cnt), + mp_obj_new_int(backup_status_info.restore_cnt), + }; + return mp_obj_new_tuple(3, info); + } + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_backup_status_obj, queclib_dev_backup_status); + +static mp_obj_t set_usbmode(mp_obj_t usbmode) { + int mode = mp_obj_get_int(usbmode); + extern int Helios_Dev_SetUSBMode(int mode); + return mp_obj_new_int(Helios_Dev_SetUSBMode(mode)); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(set_usbmode_obj, set_usbmode); + +//forrest.liu@20220112 add for main uart change from AT to general uart +extern void Helios_Dev_Main_UART_Enable_Set(int onoff); +extern int Helios_Dev_Main_UART_Enable_Get(void); + +static mp_obj_t main_uart_enable_set(mp_obj_t onoff) { + int mode = mp_obj_get_int(onoff); + Helios_Dev_Main_UART_Enable_Set(mode); + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(main_uart_enable_set_obj, main_uart_enable_set); + +static mp_obj_t main_uart_enable_get() { + int mode = Helios_Dev_Main_UART_Enable_Get(); + return mp_obj_new_int(mode); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(main_uart_enable_get_obj, main_uart_enable_get); + +#endif + +#if defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) + +static mp_obj_t queclib_dev_mac() +{ + char serial_number_str[64] = {0}; + int ret = Helios_Dev_GetMAC((void *)serial_number_str, sizeof(serial_number_str)); + sprintf(serial_number_str,"%02x:%02x:%02x:%02x:%02x:%02x", + serial_number_str[0]&0xff, + serial_number_str[1]&0xff, + serial_number_str[2]&0xff, + serial_number_str[3]&0xff, + serial_number_str[4]&0xff, + serial_number_str[5]&0xff); + + if(ret == 0) + { + return mp_obj_new_str(serial_number_str, strlen(serial_number_str)); + } + + return mp_obj_new_int(-1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(queclib_dev_mac_obj, queclib_dev_mac); +#endif + + +#if defined(BOARD_EG915ULA_AC_USBUP) || defined(BOARD_EG915UCN_AC_USBUP) \ + || defined(BOARD_EG915UEU_AC_USBUP) || defined(BOARD_EG915UEC_AC_USBUP) +int Helios_Dev_SetIMEI(void *buffer); + +static mp_obj_t queclib_dev_imei_set(mp_obj_t imei) { + char *imei_str = (char *)mp_obj_str_get_str(imei); + MP_THREAD_GIL_EXIT(); + int ret = Helios_Dev_SetIMEI(imei_str); + MP_THREAD_GIL_ENTER(); + if(ret != 0) + { + return mp_obj_new_int(-1); + } + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(queclib_dev_imei_set_obj, queclib_dev_imei_set); +#endif + +static const mp_rom_map_elem_t mp_module_modem_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modem) }, +#if !defined(PLAT_aic8800m40) + { MP_ROM_QSTR(MP_QSTR_getDevSN), MP_ROM_PTR(&queclib_dev_serial_number_obj) }, +#endif +#if !defined(PLAT_aic8800m40) && !defined(PLAT_ECR6600) + { MP_ROM_QSTR(MP_QSTR_getDevImei), MP_ROM_PTR(&queclib_dev_imei_obj) }, +#endif + { MP_ROM_QSTR(MP_QSTR_getDevModel), MP_ROM_PTR(&queclib_dev_model_obj) }, + { MP_ROM_QSTR(MP_QSTR_getDevFwVersion), MP_ROM_PTR(&queclib_dev_fw_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_getDevProductId), MP_ROM_PTR(&queclib_dev_product_id_obj) }, + +#if defined (PLAT_Qualcomm) + { MP_ROM_QSTR(MP_QSTR_backup), MP_ROM_PTR(&queclib_dev_backup_obj) }, + { MP_ROM_QSTR(MP_QSTR_backupStatus), MP_ROM_PTR(&queclib_dev_backup_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_setUSBMode), MP_ROM_PTR(&set_usbmode_obj) }, + { MP_ROM_QSTR(MP_QSTR_main_uart_enable_set), MP_ROM_PTR(&main_uart_enable_set_obj) }, + { MP_ROM_QSTR(MP_QSTR_main_uart_enable_get), MP_ROM_PTR(&main_uart_enable_get_obj) }, +#endif +#if defined(BOARD_EG915ULA_AC_USBUP) || defined(BOARD_EG915UCN_AC_USBUP) \ + || defined(BOARD_EG915UEU_AC_USBUP) || defined(BOARD_EG915UEC_AC_USBUP) + { MP_ROM_QSTR(MP_QSTR_setDevImei), MP_ROM_PTR(&queclib_dev_imei_set_obj) }, +#endif +#if defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) + { MP_ROM_QSTR(MP_QSTR_getDevMAC), MP_ROM_PTR(&queclib_dev_mac_obj) }, +#endif +}; +static MP_DEFINE_CONST_DICT(mp_module_modem_globals, mp_module_modem_globals_table); + + +const mp_obj_module_t mp_module_modem = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_modem_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_modem, mp_module_modem); +#endif /* MICROPY_PORT_BUILTIN_MODULES_MODEM */ diff --git a/ports/quectel/modexample.c b/ports/quectel/modexample.c new file mode 100644 index 0000000000000..ceb1400e72636 --- /dev/null +++ b/ports/quectel/modexample.c @@ -0,0 +1,127 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include +#include "py/compile.h" +#include "py/runtime0.h" +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "shared/runtime/pyexec.h" +#include "shared/runtime/interrupt_char.h" + + +#if MICROPY_QPY_MODULE_EXAMPLE + +static mp_obj_t example_exec(const mp_obj_t arg0) +{ + int ret = 0; + + mp_buffer_info_t bufinfo; + char fname[128] = {0}; + char path[128] = {0}; + mp_get_buffer_raise(arg0, &bufinfo, MP_BUFFER_READ); + + memcpy(path, bufinfo.buf, bufinfo.len); + + if(bufinfo.buf != NULL) + { + // Pawn 2021-01-18 for JIRA STASR3601-2428 begin + if (path[0] != '/') + { + snprintf(fname, sizeof(fname), "/%s", (char *)bufinfo.buf); + } + else + { + snprintf(fname, sizeof(fname), "%s", (char *)bufinfo.buf); + } + + ret = pyexec_file_if_exists(fname); + } + if ( ret == -1 ) + { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("File path error or not exist: [%s]"), (char *)bufinfo.buf); + } + #if MICROPY_PY_KBD_EXCEPTION + else if(ret == RET_KBD_INTERRUPT) + { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + MAINPY_INTERRUPT_BY_KBD_FLAG_SET(); + mp_raise_msg_varg(&mp_type_SystemExit, MP_ERROR_TEXT("CTRL_C Interrupt")); + } + #endif + #if MICROPY_PY_SOFT_RESET + else if((ret & PYEXEC_SOFTRESET) == PYEXEC_SOFTRESET) + { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + mp_raise_msg_varg(&mp_type_SystemExit, MP_ERROR_TEXT("SoftReset")); + } + #endif + // Pawn 2021-01-18 for JIRA STASR3601-2428 end + return mp_const_none; +} + + + +static MP_DEFINE_CONST_FUN_OBJ_1(example_exec_obj, example_exec); + + +static mp_obj_t example_initialize() +{ + static int initialized = 0; + if (!initialized) { + initialized = 1; + } + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_0(example_initialize_obj, example_initialize); + +static const mp_rom_map_elem_t mp_module_example_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) }, + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&example_initialize_obj) }, + { MP_ROM_QSTR(MP_QSTR_exec), MP_ROM_PTR(&example_exec_obj) }, + +}; + + +static MP_DEFINE_CONST_DICT(mp_module_example_globals, mp_module_example_globals_table); + + +const mp_obj_module_t example_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_example_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_example, example_module); +#endif /* MICROPY_QPY_MODULE_EXAMPLE */ diff --git a/ports/quectel/modflashdev.c b/ports/quectel/modflashdev.c new file mode 100644 index 0000000000000..b2aa939ceb425 --- /dev/null +++ b/ports/quectel/modflashdev.c @@ -0,0 +1,181 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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/runtime.h" + +#if MICROPY_VFS +#include "extmod/vfs.h" +#endif + +#include "helios_flash.h" +#include "helios_debug.h" + +#define MOD_FLASHDEV_LOG(msg, ...) custom_log('flashdev', msg, ##__VA_ARGS__) + +const mp_obj_type_t helios_flash_device_type; + +struct lfs_flash_info +{ + uint32_t FlashType; + uint32_t LfsStartAddress; + uint32_t LfsEndAddress; +}; + +typedef struct _helios_flash_device_obj_t { + mp_obj_base_t base; + int block_size; + int block_count; + struct lfs_flash_info info; + char partition_name[32]; +} helios_flash_device_obj_t; + +static mp_obj_t helios_flash_device_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, 2, 2, false); + helios_flash_device_obj_t *self = mp_obj_malloc(helios_flash_device_obj_t, type); + + mp_buffer_info_t bufinfo; + memset(self->partition_name, 0x0, sizeof(self->partition_name)); + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + if( bufinfo.len >= sizeof(self->partition_name)) { + mp_raise_ValueError(MP_ERROR_TEXT("partition name is invalid")); + } + memcpy(self->partition_name, bufinfo.buf, bufinfo.len); + + self->block_size = mp_obj_get_int(args[1]); + + if (self->block_size <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid block size")); + } + + HeliosFlashPartiCtx *FlashPartiCtx = Helios_Flash_GetPartiCtx((const char*)self->partition_name); + if(!FlashPartiCtx) mp_raise_OSError(19); + + uint32_t FlashPartiAddr = FlashPartiCtx->addr; + size_t FlashPartiSize = FlashPartiCtx->size; + MOD_FLASHDEV_LOG("flash addr:%x", FlashPartiAddr); + MOD_FLASHDEV_LOG("flash size:%x", FlashPartiSize); + self->info.LfsStartAddress = (FlashPartiAddr) / self->block_size * self->block_size; +#if MICROPY_VFS_LFS2 + self->info.LfsEndAddress = ((FlashPartiAddr + FlashPartiSize) / self->block_size) * self->block_size; +#else + self->info.LfsEndAddress = ((FlashPartiAddr + FlashPartiSize - 1) / self->block_size) * self->block_size; +#endif + + self->block_count = FlashPartiSize / self->block_size; + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t helios_flash_device_readblocks(size_t n_args, const mp_obj_t *args) +{ + helios_flash_device_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t FlashAddrss = 0; + uint32_t block_num = mp_obj_get_int(args[1]); + uint32_t offset = block_num * self->block_size; + mp_buffer_info_t bufinfo; + int ret; + + if (n_args >= 4) { + offset += mp_obj_get_int(args[3]); + } + + FlashAddrss = self->info.LfsStartAddress + offset; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + ret = Helios_Flash_Read((uint32_t)FlashAddrss, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(helios_flash_device_readblocks_obj, 3, 4, helios_flash_device_readblocks); + +static mp_obj_t helios_flash_device_writeblocks(size_t n_args, const mp_obj_t *args) +{ + helios_flash_device_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + uint32_t offset = block_num * self->block_size; + mp_buffer_info_t bufinfo; + int ret; + uint32_t FlashAddrss = self->info.LfsStartAddress + offset; + + if (n_args == 3) { + ret = Helios_Flash_Erase(FlashAddrss, self->block_size); + if (ret) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + } else { + offset += mp_obj_get_int(args[3]); + } + FlashAddrss = self->info.LfsStartAddress + offset; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + ret = Helios_Flash_Write(FlashAddrss, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(helios_flash_device_writeblocks_obj, 3, 4, helios_flash_device_writeblocks); + +static mp_obj_t helios_flash_device_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) +{ + helios_flash_device_obj_t *self = self_in; + mp_int_t cmd = mp_obj_get_int(cmd_in); + mp_int_t block_num = mp_obj_get_int(arg_in); + int ret; + uint32_t FlashAddrss; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + return MP_OBJ_NEW_SMALL_INT(0); + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(self->block_count); + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(self->block_size); + + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: + FlashAddrss = self->info.LfsStartAddress + (block_num * self->block_size); + ret = Helios_Flash_Erase(FlashAddrss, self->block_size); + return MP_OBJ_NEW_SMALL_INT(ret); + + default: + return MP_OBJ_NEW_SMALL_INT(-1); + } +} +static MP_DEFINE_CONST_FUN_OBJ_3(helios_flash_device_ioctl_obj, helios_flash_device_ioctl); + +static const mp_rom_map_elem_t helios_flash_device_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&helios_flash_device_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&helios_flash_device_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&helios_flash_device_ioctl_obj) }, +}; +static MP_DEFINE_CONST_DICT(helios_flash_device_locals_dict, helios_flash_device_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + helios_flash_device_type, + MP_QSTR_FlashDevice, + MP_TYPE_FLAG_NONE, + make_new, helios_flash_device_make_new, + locals_dict, &helios_flash_device_locals_dict + ); diff --git a/ports/quectel/modfota.c b/ports/quectel/modfota.c new file mode 100644 index 0000000000000..6a656051a93ac --- /dev/null +++ b/ports/quectel/modfota.c @@ -0,0 +1,446 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "py/obj.h" +#include +#include "py/runtime.h" +#include "mphalport.h" +#include "gccollect.h" + + +#if MICROPY_QPY_MODULE_FOTA + +#include "helios_fota.h" +#include "helios_power.h" +#include "helios_debug.h" +#include "callbackdeal.h" +#include "shared/runtime/pyexec.h" + + +#define MOD_FOTA_LOG(msg, ...) custom_log(FOTA, msg, ##__VA_ARGS__) + +typedef struct _fota_obj_t { + mp_obj_base_t base; + int ctx; +}fota_obj_t; + +const mp_obj_type_t mp_fota_type; +static fota_obj_t *fota_self_obj = NULL; + +static char *temp_server_address1 = NULL; + +extern void Helios_Fota_SslConfig(char *rootCA, char *clientCert, char *clientKey); + +#if defined(PLAT_Qualcomm) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_EIGEN) +extern int Helios_Fota_Download_Cancel(void); +extern int Helios_Fota_Reset_Disable(int flag); +extern int Helios_Fota_APN_Set(int ip_type, char *apn, char *user, char *pass); +#endif + +static mp_obj_t fota_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) +{ + if (fota_self_obj == NULL) + { + fota_self_obj = mp_obj_malloc_with_finaliser(fota_obj_t, &mp_fota_type); + } + fota_obj_t *self = fota_self_obj; + + self->base.type = &mp_fota_type; + self->ctx = Helios_Fota_Init(); + +#if defined (PLAT_ASR) || defined (PLAT_Unisoc) || defined(PLAT_Qualcomm) || defined(PLAT_ASR_1803s) || defined(PLAT_EIGEN) + const byte *key = NULL; + const byte *cert = NULL; + const byte *root_cert = NULL; +#if defined(PLAT_Qualcomm) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_EIGEN) + int reset_disable = 0; +#endif + + if (n_args > 0 || n_kw > 0) + { + //ssl args + enum { ARG_key, ARG_cert, ARG_root_cert, ARG_reset_disable}; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_root_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_reset_disable, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + // parse args + mp_arg_val_t args_dest[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args_dest); + + if (args_dest[ARG_key].u_obj != mp_const_none) + { + size_t key_len; + key = (const byte *)mp_obj_str_get_data(args_dest[ARG_key].u_obj, &key_len); + } + + if (args_dest[ARG_cert].u_obj != mp_const_none) + { + size_t cert_len; + cert = (const byte *)mp_obj_str_get_data(args_dest[ARG_cert].u_obj, &cert_len); + } + + if (args_dest[ARG_root_cert].u_obj != mp_const_none) + { + size_t root_cert_len; + root_cert = (const byte *)mp_obj_str_get_data(args_dest[ARG_root_cert].u_obj, &root_cert_len); + } + #if defined(PLAT_Qualcomm) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_EIGEN) + if (args_dest[ARG_reset_disable].u_int != -1) + { + reset_disable = args_dest[ARG_reset_disable].u_int; + } + #endif + } +#if defined(PLAT_Qualcomm) || defined (PLAT_Unisoc) || defined(PLAT_ASR_1803s) || defined(PLAT_EIGEN) + Helios_Fota_Reset_Disable(reset_disable); +#endif + Helios_Fota_SslConfig((char *)(root_cert), (char *)(cert), (char *)(key)); +#endif + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t fota_write(size_t n_args, const mp_obj_t *args) +{ + int ret; + int file_size; + + fota_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if(n_args > 2) + { + file_size = mp_obj_get_int(args[2]); + } + else + { + MOD_FOTA_LOG("*** input param invalid \r\n***"); + return mp_obj_new_int(-1); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + + MOD_FOTA_LOG(" buff len : %d file_size : %d\r\n", bufinfo.len, file_size); + + MP_THREAD_GIL_EXIT(); + ret = Helios_Fota_PackageWrite(self->ctx, bufinfo.buf, bufinfo.len, (size_t)file_size); + MP_THREAD_GIL_ENTER(); + + if(ret) + { + MOD_FOTA_LOG("*** fota package write fail ***\r\n"); + return mp_obj_new_int(-1); + } + if(ret < 0) + { + MOD_FOTA_LOG("*** fota package file read fail ***\r\n"); + return mp_obj_new_int(-1); + } + + return mp_obj_new_int(0); +} +#if !defined(PLAT_RDA) +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fota_write_obj, 1, 3, fota_write); +#endif + + +static mp_obj_t fota_flush(const mp_obj_t arg0) +{ + int ret = 0; + fota_obj_t *self = MP_OBJ_TO_PTR(arg0); + + ret = Helios_Fota_PackageFlush(self->ctx); + if(ret) + { + MOD_FOTA_LOG("*** fota package flush fail ***\r\n"); + return mp_obj_new_int(-1); + } + MOD_FOTA_LOG("fota package write done, verifing ...\r\n"); + + return mp_obj_new_int(0); +} +#if !defined(PLAT_RDA) +static MP_DEFINE_CONST_FUN_OBJ_1(fota_flush_obj, fota_flush); +#endif +static mp_obj_t fota_verify(const mp_obj_t arg0) +{ + int ret = 0; + fota_obj_t *self = MP_OBJ_TO_PTR(arg0); + MP_THREAD_GIL_EXIT(); + ret = Helios_Fota_PackageVerify(self->ctx); + MP_THREAD_GIL_ENTER(); + + if(ret) + { + MOD_FOTA_LOG("*** fota package verify fail ***\r\n"); + return mp_obj_new_int(-1); + } + MOD_FOTA_LOG("fota package verify done, will restart to update ...\r\n"); + + return mp_obj_new_int(0); +} +#if !defined(PLAT_RDA) +static MP_DEFINE_CONST_FUN_OBJ_1(fota_verify_obj, fota_verify); +#endif +static c_callback_t *fota_callback = NULL; + + +static void mpFotaProgressCB(int sta, int progress) +{ + if(fota_callback != NULL){ + MP_THREAD_GIL_ENTER(); + GC_STACKTOP_SET(); + mp_obj_t fota_list[2] = { + mp_obj_new_int(sta), + mp_obj_new_int(progress), + }; + + mp_sched_schedule_ex(fota_callback, mp_obj_new_list(2, fota_list)); + GC_STACKTOP_CLEAR(); + MP_THREAD_GIL_EXIT(); + } + + if(sta == 1) + { + MOD_FOTA_LOG("fota test downloading (%d)%d ...\r\n", sta, progress); + } + else if(sta == 0) + { + MOD_FOTA_LOG("fota test downloading (%d)%d ...\r\n", sta, progress); + } + else if(sta == 2) + { + MOD_FOTA_LOG("fota test update flag setted, will restart to update ...\r\n"); + #if defined (PLAT_ASR) + Helios_Power_Reset(1); + #endif + } + else if(sta == -1) + { + MOD_FOTA_LOG("fota test download failed (%d)%d\r\n", sta, progress); + MOD_FOTA_LOG("========== fota test end ==========\r\n"); + } +} + +static mp_obj_t fota_get_url() +{ + return mp_obj_new_str(temp_server_address1, strlen(temp_server_address1)); +} +#if defined(PLAT_SONY_ALT1350) +static MP_DEFINE_CONST_FUN_OBJ_0(fota_get_url_obj, fota_get_url); +#endif +static mp_obj_t fota_get_obj() +{ + return MP_OBJ_FROM_PTR(fota_self_obj); +} +#if defined(PLAT_SONY_ALT1350) +static MP_DEFINE_CONST_FUN_OBJ_0(fota_get_obj_obj, fota_get_obj); +#endif + +static mp_obj_t fota_firmware_download(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + int ret; + char *server_address1 = NULL; + char *server_address2 = NULL; + + enum { + ARG_url1, + ARG_url2, + ARG_callback, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_url1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_url2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args_parse[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args-1, args+1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args_parse); + + if (args_parse[ARG_url1].u_obj != mp_const_none) { + server_address1 = (char *)(mp_obj_str_get_str(args_parse[ARG_url1].u_obj)); + } + + if (args_parse[ARG_url2].u_obj != mp_const_none) { + server_address2 = (char *)(mp_obj_str_get_str(args_parse[ARG_url2].u_obj)); + } + + if (args_parse[ARG_callback].u_obj != mp_const_none) { + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + fota_callback = &cb; + mp_sched_schedule_callback_register(fota_callback, args_parse[ARG_callback].u_obj); + } + + + MP_THREAD_GIL_EXIT(); + ret = Helios_Fota_firmware_download(server_address1, server_address2, mpFotaProgressCB); + MP_THREAD_GIL_ENTER(); +#if !defined(PLAT_SONY_ALT1350) + if(ret) + { + MOD_FOTA_LOG("*** fota firmware download fail ***\r\n"); + return mp_obj_new_int(-1); + } + return mp_obj_new_int(0); +#else + temp_server_address1 = server_address1; + ret = pyexec_frozen_module("fota_BG950S.py", false); + if(ret == 0) + { + MOD_FOTA_LOG("*** fota execute fail ***\r\n"); + return mp_obj_new_int(-1); + } + return mp_obj_new_int(0); +#endif +} + +static MP_DEFINE_CONST_FUN_OBJ_KW(fota_firmware_download_obj, 1, fota_firmware_download); + +#if defined(PLAT_Qualcomm) +static mp_obj_t fota_download_cancel(const mp_obj_t arg0) +{ + int ret = 0; + + ret = Helios_Fota_Download_Cancel(); + if(ret) + { + MOD_FOTA_LOG("*** fota download cancel fail ***\r\n"); + return mp_obj_new_int(-1); + } + + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(fota_download_cancel_obj, fota_download_cancel); + +static mp_obj_t fota_apn_set(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + int ret; + int ip_type = 0; + char *apn = NULL; + char *user = NULL; + char *password = NULL; + + enum { + ARG_ip_type, + ARG_apn, + ARG_user, + ARG_password, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ip_type, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_fota_apn, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_fota_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_fota_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args_parse[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args-1, args+1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args_parse); + + if (args_parse[ARG_ip_type].u_int != -1) + { + ip_type = args_parse[ARG_ip_type].u_int; + } + + if (args_parse[ARG_apn].u_obj != mp_const_none) { + apn = (char *)(mp_obj_str_get_str(args_parse[ARG_apn].u_obj)); + } + + if (args_parse[ARG_user].u_obj != mp_const_none) { + user = (char *)(mp_obj_str_get_str(args_parse[ARG_user].u_obj)); + } + + if (args_parse[ARG_password].u_obj != mp_const_none) { + password = (char *)(mp_obj_str_get_str(args_parse[ARG_password].u_obj)); + } + + ret = Helios_Fota_APN_Set(ip_type, apn, user, password); + + if(ret) + { + return mp_obj_new_int(-1); + } + + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_KW(fota_apn_set_obj, 1, fota_apn_set); + +#endif + +static mp_obj_t fota___del__(mp_obj_t self_in) +{ + fota_obj_t *self = MP_OBJ_TO_PTR(self_in); + + Helios_Fota_Deinit(self->ctx); + fota_callback = NULL; + fota_self_obj = NULL; + + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(fota___del___obj, fota___del__); + + +static const mp_rom_map_elem_t fota_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fota___del___obj) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fota) }, +#if defined(PLAT_SONY_ALT1350) + { MP_ROM_QSTR(MP_QSTR_fota_get_url), MP_ROM_PTR(&fota_get_url_obj) }, + { MP_ROM_QSTR(MP_QSTR_fota_get_obj), MP_ROM_PTR(&fota_get_obj_obj) }, +#endif +#if !defined(PLAT_RDA) + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&fota_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&fota_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_verify), MP_ROM_PTR(&fota_verify_obj) }, +#endif + { MP_ROM_QSTR(MP_QSTR_httpDownload), MP_ROM_PTR(&fota_firmware_download_obj) }, +#if defined(PLAT_Qualcomm) + { MP_ROM_QSTR(MP_QSTR_download_cancel), MP_ROM_PTR(&fota_download_cancel_obj) }, + { MP_ROM_QSTR(MP_QSTR_apn_set), MP_ROM_PTR(&fota_apn_set_obj) }, +#endif +}; +static MP_DEFINE_CONST_DICT(fota_locals_dict, fota_locals_dict_table); + + +MP_DEFINE_CONST_OBJ_TYPE( + mp_fota_type, + MP_QSTR_fota, + MP_TYPE_FLAG_NONE, + make_new, fota_make_new, + locals_dict, &fota_locals_dict + ); +MP_REGISTER_MODULE(MP_QSTR_fota, mp_fota_type); + +#endif /* MICROPY_QPY_MODULE_FOTA */ diff --git a/ports/quectel/modhelios.c b/ports/quectel/modhelios.c new file mode 100644 index 0000000000000..47aa360a3a081 --- /dev/null +++ b/ports/quectel/modhelios.c @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include + +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "quectel_version.h" + +static mp_obj_t helios_platform(void) { + char platform[64] = {0}; + snprintf(platform, sizeof(platform), "%s%d.%d.%d", "heliossdk-v", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + return mp_obj_new_str(platform, strlen(platform)); +} +static MP_DEFINE_CONST_FUN_OBJ_0(helios_platform_obj, helios_platform); + +static const mp_rom_map_elem_t helios_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_helios) }, + + { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&helios_platform_obj) }, +}; + +static MP_DEFINE_CONST_DICT(helios_module_globals, helios_module_globals_table); + +const mp_obj_module_t helios_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&helios_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_helios, helios_module); \ No newline at end of file diff --git a/ports/quectel/modmachine.c b/ports/quectel/modmachine.c new file mode 100644 index 0000000000000..2bb59786e980c --- /dev/null +++ b/ports/quectel/modmachine.c @@ -0,0 +1,113 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "mpconfigport.h" + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/mperrno.h" + +#include "modmachine.h" +#include "shared/runtime/pyexec.h" +#include "mphalport.h" + +#if MICROPY_QPY_MODULE_MACHINE +#if MICROPY_PY_SOFT_RESET +static mp_obj_t machine_soft_reset(void) { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + MP_STATE_VM(mp_softreset_exception).traceback_data = NULL; + MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_softreset_exception)); +#if MICROPY_ENABLE_SCHEDULER + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } +#endif + + mp_mthread_wakeup(); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); +#endif + + +static const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) }, +#if MICROPY_QPY_MACHINE_PIN + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, +#endif +#if MICROPY_QPY_MACHINE_EXTINT + { MP_ROM_QSTR(MP_QSTR_ExtInt), MP_ROM_PTR(&machine_extint_type) }, +#endif +#if MICROPY_QPY_MACHINE_UART + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, +#endif +#if MICROPY_QPY_MACHINE_SPI + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, +#endif +#if MICROPY_QPY_MACHINE_I2C + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, +#endif +#if MICROPY_QPY_MACHINE_I2C_SOFT + { MP_ROM_QSTR(MP_QSTR_I2C_simulation), MP_ROM_PTR(&machine_simulation_i2c_type) }, +#endif +#if MICROPY_QPY_MACHINE_TIMER + { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, +#endif +#if MICROPY_PY_SOFT_RESET + { MP_ROM_QSTR(MP_QSTR_SoftReset), MP_ROM_PTR(&machine_soft_reset_obj) }, +#endif +#if MICROPY_QPY_MACHINE_RTC + { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, +#endif +#if MICROPY_QPY_MACHINE_LCD + { MP_ROM_QSTR(MP_QSTR_LCD), MP_ROM_PTR(&machine_lcd_type) }, +#endif +#if MICROPY_QPY_MACHINE_WDT + { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, +#endif +#if MICROPY_QPY_MACHINE_KEYPAD + { MP_ROM_QSTR(MP_QSTR_KeyPad), MP_ROM_PTR(&machine_keypad_type) }, +#endif +#if MICROPY_QPY_MACHINE_NANDFLASH + { MP_ROM_QSTR(MP_QSTR_NANDFLASH), MP_ROM_PTR(&machine_nandflash_type) }, +#endif +}; + +static MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&machine_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_machine, mp_module_machine); + +#endif /* MICROPY_QPY_MODULE_MACHINE */ + diff --git a/ports/quectel/modmachine.h b/ports/quectel/modmachine.h new file mode 100644 index 0000000000000..31bf023fcbd39 --- /dev/null +++ b/ports/quectel/modmachine.h @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MOD_MACHINE_H_ +#define __MOD_MACHINE_H_ + +#include "py/obj.h" + +extern const mp_obj_type_t machine_lcd_type; +extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_uart_type; +extern const mp_obj_type_t machine_extint_type; +extern const mp_obj_type_t machine_rtc_type; +extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_hard_i2c_type; +extern const mp_obj_type_t machine_simulation_i2c_type; +extern const mp_obj_type_t machine_hard_spi_type; +extern const mp_obj_type_t machine_wdt_type; +extern const mp_obj_type_t machine_nandflash_type; +#if defined(PLAT_ASR) || defined(PLAT_Unisoc) ||defined(PLAT_ASR_1606) +extern const mp_obj_type_t machine_keypad_type; +#endif + + + +// void machine_timer_deinit_all(void); + +#endif /* __MOD_MACHINE_H_ */ + + diff --git a/ports/quectel/modmisc.c b/ports/quectel/modmisc.c new file mode 100644 index 0000000000000..62f88e2bec98d --- /dev/null +++ b/ports/quectel/modmisc.c @@ -0,0 +1,253 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include "mpconfigport.h" + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/mperrno.h" +#include "py/obj.h" // Pawn 2020-12-19 Add replEnable + +#if MICROPY_QPY_MODULE_MISC + +#include "helios_dev.h" +#include "modmisc.h" + +#if MICROPY_QPY_MISC_PWM +#include "helios_gpio.h" +#endif + +int repl_protect_enable=0; +#if MICROPY_PY_REPL_PASSWORD_PROTECT +extern int repl_protect_password_enable; +extern char g_repl_protect_pswd[64+1]; +#endif + +static mp_obj_t qpy_misc_set_replEnable(size_t n_args, const mp_obj_t *args) +{ + int flag; + + flag = mp_obj_get_int(args[0]); +#if !MICROPY_PY_REPL_PASSWORD_PROTECT + if (flag != 0 && flag != 1) + { + return mp_obj_new_int(-1); + } +#else + if (flag != 0 && flag != 1 && flag != 2) + { + return mp_obj_new_int(-1); + } + + if (flag == 2) { + if (n_args > 1) { + return mp_obj_new_int(-1); + } else { + if ((strlen(g_repl_protect_pswd) > 0) && (repl_protect_enable == 1)) { + return mp_obj_new_int(4); //repl-protection by password + } else if ((strlen(g_repl_protect_pswd) <= 0) && (repl_protect_enable == 1)) { + return mp_obj_new_int(3); //repl refuse + } else if ((strlen(g_repl_protect_pswd) > 0) && (repl_protect_enable == 0)) { + return mp_obj_new_int(2); //repl enable but The password has already been set + } else { + return mp_obj_new_int(1); //repl enable + } + } + } + + mp_buffer_info_t bufinfo = {0}; + if (strlen(g_repl_protect_pswd) > 0) + { + if (n_args > 1) + { + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + if ((bufinfo.len > 0) && (strcmp(g_repl_protect_pswd, bufinfo.buf) == 0)) + { + repl_protect_enable = flag; + } + else + { + return mp_obj_new_int(-1); + } + } + else + { + return mp_obj_new_int(-1); + } + } + else +#endif + { + repl_protect_enable = flag; + } + + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_misc_set_replEnable_obj, 1, 2, qpy_misc_set_replEnable); + +static mp_obj_t qpy_misc_set_repl_password(size_t n_args, const mp_obj_t *args) +{ +#if MICROPY_PY_REPL_PASSWORD_PROTECT + mp_buffer_info_t bufinfo[2] = {0}; + mp_get_buffer_raise(args[0], &bufinfo[0], MP_BUFFER_READ); + + if (strlen(g_repl_protect_pswd) > 0) /*repl-protection by password*/ + { + if (n_args < 2) + return mp_obj_new_int(-1); + + mp_get_buffer_raise(args[1], &bufinfo[1], MP_BUFFER_READ); + if (bufinfo[1].len > 0) + { + if (strcmp(g_repl_protect_pswd, bufinfo[1].buf) == 0) + { + memset(g_repl_protect_pswd, 0, sizeof(g_repl_protect_pswd)); + if (bufinfo[0].len > 0) + { + repl_protect_password_enable = 1; + strncpy(g_repl_protect_pswd, bufinfo[0].buf, bufinfo[0].len); + } + else + { + repl_protect_password_enable = 0; + } + } + else + { + return mp_obj_new_int(-1); + } + } + else + { + return mp_obj_new_int(-1); + } + } + else + { + memset(g_repl_protect_pswd, 0, sizeof(g_repl_protect_pswd)); + if (bufinfo[0].len > 0) + { + repl_protect_password_enable = 1; + strncpy(g_repl_protect_pswd, bufinfo[0].buf, bufinfo[0].len); + } + else + { + repl_protect_password_enable = 0; + } + } + +#endif + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_misc_set_repl_password_obj, 1, 2, qpy_misc_set_repl_password); + +#if !defined(PLAT_ASR_1803s) +#if defined(PLAT_ASR) +static mp_obj_t qpy_misc_IncCoreVoltage(void) +{ + if(0 != Helios_Dev_IncreaseCoreVoltage()) { + mp_obj_new_int(-1); + } + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_misc_IncCoreVoltage_obj, qpy_misc_IncCoreVoltage); +#endif + +#endif + +#if MICROPY_QPY_MISC_CFG_NETLIGHT //20220620 add net light nv @jimmy +static mp_obj_t net_light_enable(size_t n_args,const mp_obj_t *args) +{ + int ret = 0; + if(n_args == 1) + { + int value = mp_obj_get_int(args[0]); + if((value == 0) || (value == 1)) + { + ret = Helios_netlight_enable(value); + } + else + { + ret = -1; + } + } + else + { + ret = Helios_netlight_state_get(); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(net_light_enable_obj, 0, 1, net_light_enable); +#endif + +static const mp_rom_map_elem_t misc_module_globals_table[] = { + + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_misc) }, +#if MICROPY_QPY_MISC_POWER + { MP_ROM_QSTR(MP_QSTR_Power), MP_ROM_PTR(&misc_power_type) }, +#endif +#if MICROPY_QPY_MISC_ADC + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&misc_adc_type) }, +#endif +#if MICROPY_QPY_MISC_POWERKEY + { MP_ROM_QSTR(MP_QSTR_PowerKey), MP_ROM_PTR(&misc_powerkey_type) }, +#endif +#if MICROPY_QPY_MISC_TEMPERATURE + { MP_ROM_QSTR(MP_QSTR_Temperature), MP_ROM_PTR(&machine_temperature_type) }, +#endif +#if MICROPY_QPY_MISC_PWM + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&misc_pwm_type) }, + { MP_ROM_QSTR(MP_QSTR_PWM_V2), MP_ROM_PTR(&misc_pwm_v2_type) }, +#endif +#if MICROPY_QPY_MISC_USB + { MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&misc_usb_type) }, +#endif +#if MICROPY_QPY_MISC_USBNET + { MP_ROM_QSTR(MP_QSTR_USBNET), MP_ROM_PTR(&misc_usbnet_module) }, +#endif + { MP_ROM_QSTR(MP_QSTR_replEnable), MP_ROM_PTR(&qpy_misc_set_replEnable_obj) }, + { MP_ROM_QSTR(MP_QSTR_replUpdatePassswd), MP_ROM_PTR(&qpy_misc_set_repl_password_obj) }, +#if defined(PLAT_ASR) + { MP_ROM_QSTR(MP_QSTR_IncCoreVoltage), MP_ROM_PTR(&qpy_misc_IncCoreVoltage_obj)}, +#endif +#if MICROPY_QPY_MISC_CFG_NETLIGHT + { MP_ROM_QSTR(MP_QSTR_net_light), MP_ROM_PTR(&net_light_enable_obj) }, +#endif +}; +static MP_DEFINE_CONST_DICT(misc_module_globals, misc_module_globals_table); + +const mp_obj_module_t mp_module_misc = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&misc_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_misc, mp_module_misc); +#endif /* MICROPY_QPY_MODULE_MISC */ + diff --git a/ports/quectel/modmisc.h b/ports/quectel/modmisc.h new file mode 100644 index 0000000000000..5c71ad5c217e8 --- /dev/null +++ b/ports/quectel/modmisc.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MOD_MISC_H_ +#define __MOD_MISC_H_ + +#include "py/obj.h" + + +extern const mp_obj_type_t misc_power_type; +extern const mp_obj_type_t misc_pwm_type; +extern const mp_obj_type_t misc_pwm_v2_type; +extern const mp_obj_type_t misc_adc_type; +extern const mp_obj_type_t misc_usb_type; +#if MICROPY_QPY_MISC_USBNET +extern const mp_obj_module_t misc_usbnet_module; +#endif +extern const mp_obj_type_t misc_powerkey_type; + +#if defined(PLAT_RDA) +extern const mp_obj_module_t machine_temperature_type; +#endif + +#endif /* __MOD_MISC_H_ */ + diff --git a/ports/quectel/modnet.c b/ports/quectel/modnet.c new file mode 100644 index 0000000000000..167e360f766ef --- /dev/null +++ b/ports/quectel/modnet.c @@ -0,0 +1,2423 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 "obj.h" +#include "runtime.h" +#include "mphalport.h" +#include "callbackdeal.h" +#include "gccollect.h" + +#if MICROPY_QPY_MODULE_NET + +#include "helios_debug.h" +#include "helios_nw.h" +#include "helios_dev.h" +#include "helios_sim.h" +#include "helios_datacall.h" +#include "helios_atcmd.h" + +#define QPY_NET_LOG(msg, ...) custom_log(modnet, msg, ##__VA_ARGS__) + +extern int _get_current_simid(void); +/*=============================================================================*/ +/* FUNCTION: qpy_net_set_mode */ +/*=============================================================================*/ +/*!@brief: set network mode + * + * @mode [in] network mode + * @roaming [in] enable or disable roaming + * + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_set_configuration(size_t n_args, const mp_obj_t *args) +{ + int mode; + int roaming; + int ret = 0; + Helios_NwConfigInfoStruct config_info; + memset(&config_info, 0, sizeof(Helios_NwConfigInfoStruct)); + + mode = mp_obj_get_int(args[0]); + QPY_NET_LOG("[network] set config, mode=%d\r\n", mode); + + int cur_simid = _get_current_simid(); + + if (n_args == 2) + { + roaming = mp_obj_get_int(args[1]); + if((roaming != 0) && (roaming != 1)) + { + QPY_NET_LOG("[network] invalid roaming value, roaming=%d\r\n", roaming); + return mp_obj_new_int(-1); + } + config_info.roaming_switch = roaming; + QPY_NET_LOG("[network] set config, roaming=%d\r\n", roaming); + } + if (n_args ==3) + { + cur_simid = mp_obj_get_int(args[2]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + config_info.net_mode = mode; + + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_SetConfiguration(cur_simid, &config_info); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_set_configuration_obj, 1, 3, qpy_net_set_configuration); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_mode */ +/*=============================================================================*/ +/*!@brief: get network mode + * + * @return: + * returns net mode on success. + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_configuration(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + Helios_NwConfigInfoStruct config_info; + memset(&config_info, 0, sizeof(Helios_NwConfigInfoStruct)); + + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetConfiguration(cur_simid, &config_info); + MP_THREAD_GIL_ENTER(); + if(ret == 0) + { + mp_obj_t tuple[2] = {mp_obj_new_int(config_info.net_mode), mp_obj_new_bool(config_info.roaming_switch)}; + return mp_obj_new_tuple(2, tuple); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_configuration_obj, 0, 1, qpy_net_get_configuration); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_csq */ +/*=============================================================================*/ +/*!@brief: get CSQ signal strength + * + * @return: + * returns CSQ on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_csq(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int csq = -1; + uint8_t status = 0; + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetCardStatus(cur_simid, (Helios_SIM_Status_e *)&status); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + if (status != 1) + { + return mp_obj_new_int(99); + } + } + else + { + return mp_obj_new_int(-1); + } + MP_THREAD_GIL_EXIT(); + csq = Helios_Nw_GetCSQ(cur_simid); + MP_THREAD_GIL_ENTER(); + if(csq != -1) + { + return mp_obj_new_int(csq); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_csq_obj, 0, 1, qpy_net_get_csq); + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_nitz_time */ +/*=============================================================================*/ +/*!@brief: get nitz time + * + * @return: + * returns nitz time on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_nitz_time(void) +{ + int ret = 0; + Helios_NwNITZTimeInfoStruct info; + memset(&info, 0, sizeof(Helios_NwNITZTimeInfoStruct)); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetNITZTime(&info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + QPY_NET_LOG("nitz_time:%s\r\n",info.nitz_time); + mp_obj_t tuple[3] = { + mp_obj_new_str(info.nitz_time, strlen(info.nitz_time)), + mp_obj_new_int_from_ull(info.abs_time), + mp_obj_new_int(info.leap_sec)}; + + return mp_obj_new_tuple(3, tuple); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_nitz_time_obj, qpy_net_get_nitz_time); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_operator_name */ +/*=============================================================================*/ +/*!@brief: get operator name + * + * @return: + * returns operator name on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_operator_name(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + Helios_NwOperatorInfoStruct info; + Helios_NwRegisterStatusInfoStruct reg_info; + memset(&info, 0, sizeof(Helios_NwOperatorInfoStruct)); + memset(®_info, 0, sizeof(Helios_NwRegisterStatusInfoStruct)); + + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetRegisterStatus(cur_simid, ®_info); + MP_THREAD_GIL_ENTER(); + + if (ret == 0) + { + if ((reg_info.data_reg.status != 1) && (reg_info.data_reg.status != 5)) + { + QPY_NET_LOG("nw is not registered\r\n"); + return mp_obj_new_int(-1); + } + } + else + { + QPY_NET_LOG("get nw register status failed.\r\n"); + return mp_obj_new_int(-1); + } + + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetOperatorName(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + + if (ret == 0) + { + mp_obj_t tuple[4] = { + mp_obj_new_str(info.long_name, strlen(info.long_name)), + mp_obj_new_str(info.short_name, strlen(info.short_name)), + mp_obj_new_str(info.mcc, strlen(info.mcc)), + mp_obj_new_str(info.mnc, strlen(info.mnc))}; + + return mp_obj_new_tuple(4, tuple); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_operator_name_obj, 0, 1, qpy_net_get_operator_name); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_selection */ +/*=============================================================================*/ +/*!@brief: get network selection + * + * @return: + * returns (nw_selection,mcc, mnc, act) on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_selection(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + Helios_NwSelectionInfoStruct info; + memset(&info, 0, sizeof(Helios_NwSelectionInfoStruct)); + + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetSelection(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + mp_obj_t tuple[4] = { + mp_obj_new_int(info.nw_selection_mode), + mp_obj_new_str(info.mcc, strlen(info.mcc)), + mp_obj_new_str(info.mnc, strlen(info.mnc)), + mp_obj_new_int(info.act)}; + + return mp_obj_new_tuple(4, tuple); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_selection_obj, 0, 1, qpy_net_get_selection); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_reg_status */ +/*=============================================================================*/ +/*!@brief: get information about network registration + * + * @return: + * returns information about network registration on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_reg_status(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + Helios_NwRegisterStatusInfoStruct info; + memset(&info, 0, sizeof(Helios_NwRegisterStatusInfoStruct)); + + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetRegisterStatus(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + mp_obj_t voice_list[6] = { + mp_obj_new_int(info.voice_reg.status), + mp_obj_new_int(info.voice_reg.lac), + mp_obj_new_int(info.voice_reg.cid), + mp_obj_new_int(info.voice_reg.act), + mp_obj_new_int(info.voice_reg.reject_cause), + mp_obj_new_int(info.voice_reg.psc) + }; + + mp_obj_t data_list[6] = { + mp_obj_new_int(info.data_reg.status), + mp_obj_new_int(info.data_reg.lac), + mp_obj_new_int(info.data_reg.cid), + mp_obj_new_int(info.data_reg.act), + mp_obj_new_int(info.data_reg.reject_cause), + mp_obj_new_int(info.data_reg.psc) + }; + + mp_obj_t tuple[2] = { + mp_obj_new_list(6,voice_list), + mp_obj_new_list(6,data_list)}; + return mp_obj_new_tuple(2, tuple); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_reg_status_obj, 0, 1, qpy_net_get_reg_status); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_signal_strength */ +/*=============================================================================*/ +/*!@brief: get signal strength + * + * @return: + * returns information about signal strength on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_signal_strength(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + Helios_NwSignalStrengthInfoStruct info; + memset(&info, 0, sizeof(Helios_NwSignalStrengthInfoStruct)); + int cur_simid = _get_current_simid(); + if (n_args ==2) + { + cur_simid = mp_obj_get_int(args[1]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetSignalStrength(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + + if (ret == 0) + { + mp_obj_t gw_list[4] = { + mp_obj_new_int(info.gw_signal_strength.rssi), + mp_obj_new_int(info.gw_signal_strength.bit_error_rate), + mp_obj_new_int(info.gw_signal_strength.rscp), + mp_obj_new_int(info.gw_signal_strength.ecno)}; + + #if !defined (PLAT_RDA) //defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || defined(PLAT_Qualcomm) || defined(PLAT_Unisoc) || defined(PLAT_ASR) + if (n_args > 0) + { + int flag = mp_obj_get_int(args[0]); + if (flag == 1) + { + mp_obj_t lte_list[5] = { + mp_obj_new_int(info.lte_signal_strength.rssi), + mp_obj_new_int(info.lte_signal_strength.rsrp), + mp_obj_new_int(info.lte_signal_strength.rsrq), + mp_obj_new_int(info.lte_signal_strength.cqi), + mp_obj_new_int(info.lte_signal_strength.sinr)}; + + mp_obj_t tuple[2] = { + mp_obj_new_list(4,gw_list), + mp_obj_new_list(5,lte_list)}; + + return mp_obj_new_tuple(2, tuple); + } + } + #endif + mp_obj_t lte_list[4] = { + mp_obj_new_int(info.lte_signal_strength.rssi), + mp_obj_new_int(info.lte_signal_strength.rsrp), + mp_obj_new_int(info.lte_signal_strength.rsrq), + mp_obj_new_int(info.lte_signal_strength.cqi)}; + + mp_obj_t tuple[2] = { + mp_obj_new_list(4,gw_list), + mp_obj_new_list(4,lte_list)}; + + return mp_obj_new_tuple(2, tuple); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_signal_strength_obj, 0, 2, qpy_net_get_signal_strength); + + +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_mnc */ +/*=============================================================================*/ +/*!@brief: get mnc + * + * @return: + * returns mnc on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_mnc(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int i = 0; + mp_obj_t mnc_list = mp_obj_new_list(0, NULL); + Helios_NwCellInfoStruct info; + memset(&info, 0, sizeof(Helios_NwCellInfoStruct)); + int cur_simid = _get_current_simid(); + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetCellInfo(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + + if (ret == 0) + { + if (info.gsm_info_num > 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 3)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, net_type should be in [0,%d]."), HELIOS_BAND_TYPE_MAX_NUM); + } + if ((gsm_band < 0) || (gsm_band > 0x0f)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, gsm_band should be in [0,%x]."), HELIOS_BAND_GSM_MAXVALUE); + } + + size_t len = 0; + mp_obj_t *elem = NULL; + mp_obj_get_array(args[2], &len, &elem); + QPY_NET_LOG("[helios_nw_band]elem num : %d\r\n", len); + if (len != 4) + { + mp_raise_ValueError("invalid value, band tuple should be 4 uint32 elements."); + } + + uint32_t band_hh = mp_obj_get_int_truncated(elem[0]); + uint32_t band_hl = mp_obj_get_int_truncated(elem[1]); + uint32_t band_lh = mp_obj_get_int_truncated(elem[2]); + uint32_t band_ll = mp_obj_get_int_truncated(elem[3]); + QPY_NET_LOG("[helios_nw_band]band_hh=%x, band_hl=%x, band_lh=%x, band_ll=%x\r\n", band_hh, band_hl, band_lh, band_ll); + + Helios_NwBandStruct band_info; + memset(&band_info, 0, sizeof(Helios_NwBandStruct)); + band_info.band_type = net_type; + band_info.band_gsm = gsm_band; + band_info.band_low = (((uint64_t)band_lh) << 32) | band_ll; + band_info.band_hign = (((uint64_t)band_hh) << 32) | band_hl; + QPY_NET_LOG("[helios_nw_band]bandh=%llx, bandl=%llx\r\n", band_info.band_hign, band_info.band_low); + + int cur_simid = _get_current_simid(); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_SetBand(cur_simid, &band_info); + MP_THREAD_GIL_ENTER(); + return ((ret == 0) ? mp_obj_new_int(0) : mp_obj_new_int(ret)); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_set_band_obj, 3, 3, qpy_net_set_band); + +static mp_obj_t qpy_net_get_band(mp_obj_t net_type) +{ + int band_type = mp_obj_get_int(net_type); + if ((band_type < 0) || (band_type > 3)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, net_type should be in [0,3].")); + } + + int ret = 0; + Helios_NwBandStruct band_info; + memset(&band_info, 0, sizeof(Helios_NwBandStruct)); + band_info.band_type = band_type; + int cur_simid = _get_current_simid(); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetBand(cur_simid, &band_info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + char str[64] = {0}; + if (band_type == HELIOS_GSM_BAND) + { + snprintf(str, 64, "0x%x", band_info.band_gsm); + } + else + { + if (band_info.band_hign == 0) + { +#if MICROPY_QPY_MODULE_NET_BAND + uint32_t band_lh = band_info.band_low >> 32; + uint32_t band_ll = band_info.band_low & 0xFFFFFFFF; + QPY_NET_LOG("[helios_nw_band]band_hign=%lx, band_low=%lx\r\n", band_lh, band_ll); + if (band_lh ==0) { + snprintf(str, 64, "0x%lx", band_ll); + } else { + snprintf(str, 64, "0x%lx%08lx", band_lh, band_ll); + } +#else + snprintf(str, 64, "0x%llx", band_info.band_low); +#endif + } + else + { + snprintf(str, 64, "0x%llx%016llx", band_info.band_hign, band_info.band_low); + } + } + return mp_obj_new_str(str, strlen(str)); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_net_get_band_obj, qpy_net_get_band); +#endif + +#if defined(PLAT_ASR) || MICROPY_QPY_MODULE_NET_BAND +static mp_obj_t qpy_net_band_restore(void) +{ + int ret = 0; + int cur_simid = _get_current_simid(); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_Band_Restore(cur_simid); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_band_restore_obj, qpy_net_band_restore); +#endif +/*=============================================================================*/ +/* FUNCTION: qpy_net_get_cell_info */ +/*=============================================================================*/ +/*!@brief: get cell informations + * + * @return: + * returns cell informations on success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_get_cell_info(size_t n_args, const mp_obj_t *args) +{ + int i = 0; + int ret = 0; + + mp_obj_t list_gsm = mp_obj_new_list(0, NULL); + mp_obj_t list_umts = mp_obj_new_list(0, NULL); + mp_obj_t list_lte = mp_obj_new_list(0, NULL); + Helios_NwCellInfoStruct info; + memset(&info, 0, sizeof(Helios_NwCellInfoStruct)); + + int cur_simid = _get_current_simid(); +#if defined(PLAT_RDA) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + int flag_args = -1; +#else + if (n_args ==1) + { + cur_simid = mp_obj_get_int(args[0]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } +#endif + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetCellInfo(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + + if (ret == 0) + { + if (info.gsm_info_num > 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + #if defined(PLAT_RDA) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + if (n_args > 0) + { + flag_args = mp_obj_get_int(args[0]); + + if (flag_args == 1) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i 1 ) + { + rst = mp_obj_get_int(args[1]); + } + + modem_fun = (uint8_t)mp_obj_get_int(args[0]); + + #if MICROPY_QPY_MODULE_DSDS + int cur_simid = _get_current_simid(); + if (n_args ==3) + { + cur_simid = mp_obj_get_int(args[2]); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Dev_SetModemFunction(modem_fun, rst, cur_simid); + MP_THREAD_GIL_ENTER(); + #else + MP_THREAD_GIL_EXIT(); + ret = Helios_Dev_SetModemFunction(modem_fun, rst,0); + MP_THREAD_GIL_ENTER(); + #endif + if (ret == 0) + { + return mp_obj_new_int(0); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_set_modem_fun_obj, 1, 3, qpy_net_set_modem_fun); + +static mp_obj_t qpy_net_set_apn(size_t n_args, const mp_obj_t *args) +{ +#if defined(PLAT_Qualcomm) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || (defined(PLAT_Unisoc) && !defined(BOARD_EG915UEU_AB)) \ + || defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ASR_1602) || defined(PLAT_Unisoc_8910_R06) + if (n_args == 2) + { + mp_buffer_info_t apninfo = {0}; + mp_get_buffer_raise(args[0], &apninfo, MP_BUFFER_READ); + uint8_t net_simid = mp_obj_get_int(args[1]); + int ret = 0; + if (net_simid != 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + if (apninfo.len > HELIOS_APN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of apn should be no more than [%d] bytes."), HELIOS_APN_LEN_MAX); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_SetApn((char *)apninfo.buf, net_simid); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(0); + } + return mp_obj_new_int(-1); + } + else +#endif + { + if (n_args == 7)//pid/iptype/apn/usrname/pwd/authtype/simid + { + int8_t ret = 0; + uint8_t net_simid = mp_obj_get_int(args[6]); + uint8_t profile_id = mp_obj_get_int(args[0]); + int ip_type = mp_obj_get_int(args[1]); + int auth_type = mp_obj_get_int(args[5]); + + mp_buffer_info_t apninfo = {0}; + mp_buffer_info_t usrinfo = {0}; + mp_buffer_info_t pwdinfo = {0}; + mp_get_buffer_raise(args[2], &apninfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &usrinfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[4], &pwdinfo, MP_BUFFER_READ); + + int min_profile_id = (int)HELIOS_PROFILE_IDX_MIN; + int max_profile_id = (int)HELIOS_PROFILE_IDX_MAX; + + #if MICROPY_QPY_MODULE_DSDS + if ((net_simid != 0) && (net_simid != 1)) + #else + if (net_simid != 0) + #endif + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + if ((profile_id < min_profile_id) || (profile_id > max_profile_id)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, profileIdx should be in [%d,%d]."), min_profile_id, max_profile_id); + } + if ((ip_type < 0) || (ip_type > HELIOS_PDP_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, ipType should be in [0~%d]."), HELIOS_PDP_TYPE_NUM-1); + } + if ((auth_type < 0) || (auth_type > HELIOS_AUTH_TYPE_NUM - 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, authType should be in [0~%d]."), HELIOS_AUTH_TYPE_NUM-1); + } + if (apninfo.len > HELIOS_APN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of apn should be no more than [%d] bytes."), HELIOS_APN_LEN_MAX); + } + if (usrinfo.len > HELIOS_USR_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of username should be no more than [%d] bytes."), HELIOS_USR_LEN_MAX); + } + if (pwdinfo.len > HELIOS_PWD_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of password should be no more than [%d] bytes."), HELIOS_PWD_LEN_MAX); + } + + QPY_NET_LOG("[NW-PDNINFO] profile_idx=%d, ip_version=%d, auth_type=%d\r\n", profile_id, ip_type, auth_type); + QPY_NET_LOG("[NW-PDNINFO] anp_name=%s, usr_name=%s, password=%s\r\n", apninfo.buf, usrinfo.buf, pwdinfo.buf); + + Helios_DataCallStartStruct pdpinfo_set; + memset(&pdpinfo_set, 0, sizeof(Helios_DataCallStartStruct)); + + pdpinfo_set.ip_type = (int32_t)ip_type; + pdpinfo_set.auth = (int32_t)auth_type; + snprintf(pdpinfo_set.apn, sizeof(pdpinfo_set.apn), "%s", (char *)apninfo.buf); + snprintf(pdpinfo_set.user, sizeof(pdpinfo_set.user), "%s", (char *)usrinfo.buf); + snprintf(pdpinfo_set.pwd, sizeof(pdpinfo_set.pwd), "%s", (char *)pwdinfo.buf); + MP_THREAD_GIL_EXIT(); + #if MICROPY_QPY_MODULE_DSDS + ret = Helios_DataCall_SetPDPContext(net_simid, profile_id, &pdpinfo_set); + #else + ret = Helios_DataCall_SetPDPContext(profile_id, &pdpinfo_set); + #endif + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); + } + else + { + mp_raise_ValueError(MP_ERROR_TEXT("Incorrect parameter number!")); + } + } +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_set_apn_obj, 2, 7, qpy_net_set_apn); + +static mp_obj_t qpy_net_get_apn(size_t n_args, const mp_obj_t *args) +{ +#if defined(PLAT_Qualcomm) || defined(PLAT_ASR_1803s) || defined(PLAT_ASR_1803sc) || (defined(PLAT_Unisoc) && !defined(BOARD_EG915UEU_AB)) \ + || defined(PLAT_ASR) || defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_ASR_1602) || defined(PLAT_Unisoc_8910_R06) + if (n_args == 1) + { + uint8_t net_simid = mp_obj_get_int(args[0]); + int ret = 0; + char apn[99+1] = {0}; + if (net_simid != 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetApn(apn, net_simid); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_str(apn, strlen(apn)); + } + return mp_obj_new_int(-1); + } + else +#endif + { + if (n_args == 2) //pid/simid + { + uint8_t net_simid = mp_obj_get_int(args[1]); + int ret = 0; + uint8_t profile_id = mp_obj_get_int(args[0]); + Helios_DataCallStartStruct pdpinfo_get; + + #if MICROPY_QPY_MODULE_DSDS + if ((net_simid != 0) && (net_simid != 1)) + #else + if (net_simid != 0) + #endif + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + + if ((profile_id < HELIOS_PROFILE_IDX_MIN) || (profile_id > HELIOS_PROFILE_IDX_MAX)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid profile_id.")); + } + memset(&pdpinfo_get, 0, sizeof(Helios_DataCallStartStruct)); + MP_THREAD_GIL_EXIT(); + #if MICROPY_QPY_MODULE_DSDS + ret = Helios_DataCall_GetPDPContext(net_simid,profile_id, &pdpinfo_get); + #else + ret = Helios_DataCall_GetPDPContext(profile_id, &pdpinfo_get); + #endif + MP_THREAD_GIL_ENTER(); + if (ret != 0) + return mp_obj_new_int(-1); + + mp_obj_t tuple[5] = { + mp_obj_new_int(pdpinfo_get.ip_type), + mp_obj_new_str(pdpinfo_get.apn, strlen(pdpinfo_get.apn)), + mp_obj_new_str(pdpinfo_get.user, strlen(pdpinfo_get.user)), + mp_obj_new_str(pdpinfo_get.pwd, strlen(pdpinfo_get.pwd)), + mp_obj_new_int(pdpinfo_get.auth)}; + + return mp_obj_new_tuple(5, tuple); + } + else + { + mp_raise_ValueError(MP_ERROR_TEXT("Incorrect parameter number!")); + } + } +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_apn_obj, 1, 2, qpy_net_get_apn); + +#if MICROPY_QPY_MODULE_NET_LED +static mp_obj_t qpy_set_networkled_config( const mp_obj_t args) +{ + + typedef struct network_led_control_info{ + int i_gpio_num; // GPIO_NO + uint32_t i_net_type; // TCP UDP 0-tcp 1-udp + uint32_t i_gpio_low_time; // GPIO LOW times ms + uint32_t i_gpio_hight_time; // GPIO hight times ms + uint32_t i_loop_nums; // cycle nums + }network_led_control_info_t; + + int Helios_NW_Led_Config(network_led_control_info_t config); + volatile network_led_control_info_t config; + mp_obj_t *items = NULL; + size_t len = 0; + char *str = NULL; + mp_obj_list_get(args, &len, &items); + if ( len!= 5){ + return mp_obj_new_int(-1); + } + config.i_gpio_num = (int)mp_obj_get_int(items[0]); + config.i_net_type = (int)mp_obj_get_int(items[1]); + config.i_gpio_low_time =(int) mp_obj_get_int(items[2]); + config.i_gpio_hight_time = (int)mp_obj_get_int(items[3]); + config.i_loop_nums =(int) mp_obj_get_int(items[4]); + MP_THREAD_GIL_EXIT(); + Helios_NW_Led_Config(config); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(0); + +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_set_networkled_config_obj, qpy_set_networkled_config); +#endif + +static c_callback_t *g_net_user_callback = NULL; + +static void qpy_net_event_handler(uint8_t sim_id, int32_t event_id, void *ctx) +{ + switch (event_id) + { + case HELIOS_NW_DATA_REG_STATUS_IND: + { + Helios_NwRegisterInfoStruct *nw_register_status = (Helios_NwRegisterInfoStruct *)ctx; + + if (g_net_user_callback) + { + QPY_NET_LOG("[net] callback start.\r\n"); + st_CallBack_Net *Net_param = malloc(sizeof(st_CallBack_Net)); + if(NULL != Net_param) + { + Net_param->event_id = event_id; + Net_param->status = nw_register_status->status; + Net_param->lac = nw_register_status->lac; + Net_param->cid = nw_register_status->cid; + Net_param->act = nw_register_status->act; + Net_param->callback = *g_net_user_callback; + QPY_NET_LOG("[net] callback start 1.\r\n"); + qpy_send_msg_to_callback_deal_thread(CALLBACK_TYPE_ID_NET, Net_param); + } + QPY_NET_LOG("[net] callback end.\r\n"); + } + break; + } + #if MICROPY_QPY_MODULE_JAMDET + case HELIOS_NW_JAMMING_DETECT_IND: + { + /* ... */ + QPY_NET_LOG("[net]jamming event.\r\n", event_id); + uint8_t jam_status = *(uint8_t *)ctx; + if (g_net_user_callback) + { + MP_THREAD_GIL_ENTER(); + GC_STACKTOP_SET(); + mp_obj_t tuple[3] = + { + mp_obj_new_int(event_id), + mp_obj_new_int(jam_status), + mp_obj_new_int(sim_id) + }; + mp_sched_schedule_ex(g_net_user_callback, mp_obj_new_tuple(3, tuple)); + GC_STACKTOP_CLEAR(); + MP_THREAD_GIL_EXIT(); + } + break; + } + #endif + case HELIOS_NW_VOICE_REG_STATUS_IND: + /* ... */ + QPY_NET_LOG("[net] ind_flag = %x\r\n", event_id); + break; + case HELIOS_NW_NITZ_TIME_UPDATE_IND: + /* ... */ + QPY_NET_LOG("[net] ind_flag = %x\r\n", event_id); + break; + case HELIOS_NW_SIGNAL_QUALITY_IND: + /* ... */ + QPY_NET_LOG("[net] ind_flag = %x\r\n", event_id); + break; + default: + QPY_NET_LOG("[net] event handler, ind=%x\r\n", event_id); + break; + } +} + +/*=============================================================================*/ +/* FUNCTION: qpy_net_add_event_handler */ +/*=============================================================================*/ +/*!@brief: registered user callback function + * + * @handler [in] callback function + * + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_net_add_event_handler(mp_obj_t handler) +{ + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + cb.arg = mp_obj_new_tuple(5, NULL); + g_net_user_callback = &cb; + mp_sched_schedule_callback_register(g_net_user_callback, handler); + Helios_NwInitStruct info; + memset(&info, 0, sizeof(Helios_NwInitStruct)); + + info.user_cb = qpy_net_event_handler; + Helios_Nw_Init(&info); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_net_add_event_handler_obj, qpy_net_add_event_handler); + +/*=============================================================================*/ +/* FUNCTION: qpy_module_net_deinit */ +/*=============================================================================*/ +/*!@brief: deinit net module + * + * + * @return: + * 0 - success + */ +/*=============================================================================*/ +static mp_obj_t qpy_module_net_deinit(void) +{ + QPY_NET_LOG("module net deinit.\r\n"); +#if defined(PLAT_ASR) || defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + MP_THREAD_GIL_EXIT(); + Helios_Nw_Deinit(); + MP_THREAD_GIL_ENTER(); +#endif + g_net_user_callback = NULL; + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_module_net_deinit_obj, qpy_module_net_deinit); + +#if MICROPY_QPY_MODULE_FTM_MODE +#include "helios_ftm.h" +#include "helios_os.h" +static mp_obj_t qpy_ftm_mode_switch(mp_obj_t mode) +{ + int ret = -1; + uint8_t ftm_mode = (uint8_t)mp_obj_get_int(mode); + if(ftm_mode > 1) return mp_obj_new_int(-1); + + MP_THREAD_GIL_EXIT(); + if(ftm_mode) + { + ret = Helios_Dev_SetModemFunction(!ftm_mode, 0, 0); + Helios_sleep(1); + ret = (!ret) ? helios_ftm_mode_switch(ftm_mode) : -1; + } + else + { + ret = helios_ftm_mode_switch(ftm_mode); + Helios_sleep(1); + ret = (!ret) ? Helios_Dev_SetModemFunction(!ftm_mode, 0, 0) : -1; + } + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_ftm_mode_switch_obj, qpy_ftm_mode_switch); + +static mp_obj_t qpy_ftm_mode_start(mp_obj_t band, mp_obj_t channel, mp_obj_t power) +{ + int ret = -1; + + MP_THREAD_GIL_EXIT(); + ret = helios_ftm_test_start((uint16_t)mp_obj_get_int(band), (uint32_t)mp_obj_get_int(channel), (uint8_t)mp_obj_get_int(power)); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_3(qpy_ftm_mode_start_obj, qpy_ftm_mode_start); + +static mp_obj_t qpy_ftm_mode_stop(void) +{ + int ret = -1; + MP_THREAD_GIL_EXIT(); + //uint16_t band, uint32_t tx_channel,uint8_t tx_power + ret = helios_ftm_test_stop(); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_ftm_mode_stop_obj, qpy_ftm_mode_stop); +#endif + + + +#if MICROPY_QPY_MODULE_BACKCELL + +static mp_obj_t qpy_add_black_cell(size_t n_args, const mp_obj_t *args) +{ + int8_t ret = -1; + uint8_t rat =5; + if (n_args == 2){ + rat = mp_obj_get_int(args[1]); + } + mp_buffer_info_t cell_info = {0}; + mp_get_buffer_raise(args[0], &cell_info, MP_BUFFER_READ); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_AddBlackCell(rat, (char *)cell_info.buf); + MP_THREAD_GIL_ENTER(); + if (ret != 0){ + ret =-1; + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_add_black_cell_obj, 1, 2, qpy_add_black_cell); + +static mp_obj_t qpy_get_black_cell(void) +{ + int8_t ret = 0; + int8_t i = 0; + mp_obj_t list_gsm = mp_obj_new_list(0, NULL); + mp_obj_t list_lte = mp_obj_new_list(0, NULL); + Helios_NwBackCellInfo info; + memset(&info, 0, sizeof(Helios_NwBackCellInfo)); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_GetBlackCell(&info); + MP_THREAD_GIL_ENTER(); + if (ret == 0){ + if (info.gsm_info_num > 0) + { + for (i=0; i 0) + { + for (i=0; i 1) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, blackcellCfg should be in [0,1].")); + } + uint8_t blackcellCfg_num = mp_obj_get_int(args[1]); + if (blackcellCfg_num == 0 || blackcellCfg_num > 8) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, blackcellCfg_num should be in [1,8].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_SetBlackCellCfg(blackcellCfg, blackcellCfg_num); + MP_THREAD_GIL_ENTER(); + if (ret != 0){ + ret =-1; + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_set_black_cell_cfg_obj, 2, 2, qpy_set_black_cell_cfg); + +static mp_obj_t qpy_delete_black_cell(size_t n_args, const mp_obj_t *args) +{ + int8_t ret = -1; + if (n_args == 0) + { + ret = Helios_Nw_DeleteAllBlackCell(); + } + else if (n_args == 1) + { + uint8_t rat = 5; + mp_buffer_info_t cell_info = {0}; + mp_get_buffer_raise(args[0], &cell_info, MP_BUFFER_READ); + MP_THREAD_GIL_EXIT(); + ret = Helios_Nw_DeleteBlackCell(rat, (char *)cell_info.buf); + MP_THREAD_GIL_ENTER(); + } + if (ret != 0){ + ret =-1; + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_delete_black_cell_obj, 0, 1, qpy_delete_black_cell); +#endif + + +#if MICROPY_QPY_MODULE_DRX_TIMER +static mp_obj_t qpy_net_get_drxtm(void) +{ + uint8_t cur_simid = _get_current_simid(); + uint16_t drx_timer_value = 0; + MP_THREAD_GIL_EXIT(); + if(0 != Helios_Nw_GetDrxtm(cur_simid, &drx_timer_value)) { + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(-1); + } + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(drx_timer_value); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_drxtm_obj, qpy_net_get_drxtm); + +static mp_obj_t qpy_net_set_drxtm(mp_obj_t drx_timer) +{ + uint8_t cur_simid = _get_current_simid(); + uint16_t drx_timer_value = (uint16_t)mp_obj_get_int(drx_timer); + MP_THREAD_GIL_EXIT(); + if(0 != Helios_Nw_SetDrxtm(cur_simid, drx_timer_value)) { + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(-1); + } + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_net_set_drxtm_obj, qpy_net_set_drxtm); +#endif + +#if MICROPY_QPY_MODULE_JAMDET +static mp_obj_t qpy_jamdet_set_switch(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = 0; + + uint8_t opt = (uint8_t)mp_obj_get_int(args[0]); + if ((opt != 0) && (opt != 1)) + { + mp_raise_ValueError("invalid value, opt shuould be 0 or 1,"); + } + + if (n_args == 2) + { + sim_id = (uint8_t)mp_obj_get_int(args[1]); + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + } + + MP_THREAD_GIL_EXIT(); + int ret = Helios_Nw_SetJamdetSwitch(sim_id, opt); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_jamdet_set_switch_obj, 1, 2, qpy_jamdet_set_switch); + +static mp_obj_t qpy_jamdet_get_switch(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = 0; + if (n_args == 1) + { + sim_id = (uint8_t)mp_obj_get_int(args[0]); + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + } + uint8_t opt = 0; + MP_THREAD_GIL_EXIT(); + Helios_Nw_GetJamdetSwitch(sim_id, &opt); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(opt); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_jamdet_get_switch_obj, 0, 1, qpy_jamdet_get_switch); + +static mp_obj_t qpy_jamdet_set_configuration(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = 0; + if (n_args == 8) + { + sim_id = (uint8_t)mp_obj_get_int(args[7]); + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + } + + Helios_NwJamDetConfigStruct cfg = {0}; + cfg.gsm_minch = mp_obj_get_int(args[0]); + cfg.gsm_sinr = mp_obj_get_int(args[1]); + cfg.gsm_rssi = mp_obj_get_int(args[2]); + cfg.lte_rsrp = mp_obj_get_int(args[3]); + cfg.lte_rsrq = mp_obj_get_int(args[4]); + cfg.lte_rssi = mp_obj_get_int(args[5]); + cfg.shake_period = mp_obj_get_int(args[6]); + + if ((cfg.gsm_minch < 0) || (cfg.gsm_minch > 254)) + { + mp_raise_ValueError("invalid value, gsm_minch should be in [0,254]."); + } + if ((cfg.gsm_sinr < 0) || (cfg.gsm_sinr > 63)) + { + mp_raise_ValueError("invalid value, gsm_sinr should be in [0,63]."); + } + if ((cfg.gsm_rssi < -110) || (cfg.gsm_rssi > -50)) + { + mp_raise_ValueError("invalid value, gsm_rssi should be in [-110,-50]."); + } + if ((cfg.lte_rsrp < -140) || (cfg.lte_rsrp > -44)) + { + mp_raise_ValueError("invalid value, lte_rsrp should be in [-140,-44]."); + } + if ((cfg.lte_rsrq < -19) || (cfg.lte_rsrq > -3)) + { + mp_raise_ValueError("invalid value, lte_rsrq should be in [-19,-3]."); + } + if ((cfg.lte_rssi < -120) || (cfg.lte_rssi > -20)) + { + mp_raise_ValueError("invalid value, lte_rssi should be in [-120,-20]."); + } + if ((cfg.shake_period < 1) || (cfg.shake_period > 10)) + { + mp_raise_ValueError("invalid value, shake_period should be in [1,10]."); + } + + MP_THREAD_GIL_EXIT(); + int ret = Helios_Nw_SetJamdetConfiguration(sim_id, &cfg); + MP_THREAD_GIL_ENTER(); + + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_jamdet_set_config_obj, 7, 8, qpy_jamdet_set_configuration); + +static mp_obj_t qpy_jamdet_get_configuration(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = 0; + if (n_args == 1) + { + sim_id = (uint8_t)mp_obj_get_int(args[0]); + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + } + + Helios_NwJamDetConfigStruct cfg = {0}; + MP_THREAD_GIL_EXIT(); + Helios_Nw_GetJamdetConfiguration(sim_id, &cfg); + MP_THREAD_GIL_ENTER(); + mp_obj_t info[7] = + { + mp_obj_new_int(cfg.gsm_minch), + mp_obj_new_int(cfg.gsm_sinr), + mp_obj_new_int(cfg.gsm_rssi), + mp_obj_new_int(cfg.lte_rsrp), + mp_obj_new_int(cfg.lte_rsrq), + mp_obj_new_int(cfg.lte_rssi), + mp_obj_new_int(cfg.shake_period) + }; + return mp_obj_new_tuple(7, info); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_jamdet_get_config_obj, 0, 1, qpy_jamdet_get_configuration); + +static mp_obj_t qpy_jamdet_get_stauts(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = 0; + if (n_args == 1) + { + sim_id = (uint8_t)mp_obj_get_int(args[0]); + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + } + + uint8_t opt = 0; + MP_THREAD_GIL_EXIT(); + Helios_Nw_GetJamdetSwitch(sim_id, &opt); + MP_THREAD_GIL_ENTER(); + if (opt == 0) + { + mp_raise_ValueError("Please enable the jamdet function first."); + } + + uint8_t status = 0; + MP_THREAD_GIL_EXIT(); + int ret = Helios_Nw_GetJamdetStatus(sim_id, &status); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(status); + } + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_jamdet_get_stauts_obj, 0, 1, qpy_jamdet_get_stauts); + +#endif + +#if defined(MICROPY_QPY_MODULE_IMS_STATUS) +static mp_obj_t qpy_net_get_ims_register(size_t n_args, const mp_obj_t *args) +{ + uint8_t sim_id = _get_current_simid();; + if (n_args == 1) { + sim_id = mp_obj_get_int(args[0]); + } + if ((sim_id != 0) && (sim_id != 1)) + { + mp_raise_ValueError("invalid value, simId should be 0 or 1."); + } + MP_THREAD_GIL_EXIT(); + int ret = Helios_Nw_ImsIsRegister(sim_id); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_net_get_ims_register_obj, 0, 1, qpy_net_get_ims_register); +#endif + +#if MICROPY_QPY_MODULE_NET_EX + +static mp_obj_t qpy_net_get_T3402(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+QCFG=\"emmtimer\"\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 1, para, sizeof(para)); + if(strlen(para) != 0) { + return mp_obj_new_str(para, strlen(para)); + } + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_T3402_obj, qpy_net_get_T3402); + + +static mp_obj_t qpy_net_get_T3412(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+QCFG=\"emmtimer\"\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 2, para, sizeof(para)); + if(strlen(para) != 0) { + return mp_obj_new_str(para, strlen(para)); + } + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_T3412_obj, qpy_net_get_T3412); + + +static mp_obj_t qpy_net_get_T3324(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + char cmd[64] = {0}; + int n = 0; + HELIOS_AT_RESP_STATUS_E ret = HELIOS_AT_RESP_OK; + MP_THREAD_GIL_EXIT(); + ret = Helios_Atcmd_Send_Sync(0, "AT+CEREG?\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret != HELIOS_AT_RESP_OK) { + return mp_obj_new_int(-1); + } + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 0, para, sizeof(para)); + if(strlen(para) != 0) { + n = atoi(para); + } + memset(resp, 0 ,sizeof(resp)); + memset(para, 0 ,sizeof(para)); + if (n !=4) { + MP_THREAD_GIL_EXIT(); + ret = Helios_Atcmd_Send_Sync(0, "AT+CEREG=4\r\n", NULL, 0, NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret != HELIOS_AT_RESP_OK) { + return mp_obj_new_int(-1); + } + }else { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 7, para, sizeof(para)); + if(strlen(para) != 0) { + return mp_obj_new_str(para, strlen(para)); + } + } + MP_THREAD_GIL_EXIT(); + Helios_Atcmd_Send_Sync(0, "AT+CEREG?\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 7, para, sizeof(para)); + snprintf(cmd, 64, "AT+CEREG=%d\r\n", n); + MP_THREAD_GIL_EXIT(); + ret = Helios_Atcmd_Send_Sync(0, cmd, NULL, 0, NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret != HELIOS_AT_RESP_OK) { + return mp_obj_new_int(-1); + } + return mp_obj_new_str(para, strlen(para)); + +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_T3324_obj, qpy_net_get_T3324); + + +static mp_obj_t qpy_net_get_TeDRX(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+CEDRXRDP\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 1, para, sizeof(para)); + return mp_obj_new_str(para, strlen(para)); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_TeDRX_obj, qpy_net_get_TeDRX); + +static mp_obj_t qpy_net_get_TPTW(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+CEDRXRDP\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 3, para, sizeof(para)); + return mp_obj_new_str(para, strlen(para)); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_TPTW_obj, qpy_net_get_TPTW); + +static mp_obj_t qpy_net_get_qRxlevMin(void) +{ + char resp[256] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+QCFG=\"sibinfo\"\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 3, para, sizeof(para)); + if(strlen(para) != 0) { + return mp_obj_new_str(para, strlen(para)); + } + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_qRxlevMin_obj, qpy_net_get_qRxlevMin); + +static mp_obj_t qpy_net_get_reject_cause(void) +{ + char resp[128] = {0}; + char para[64] = {0}; + MP_THREAD_GIL_EXIT(); + HELIOS_AT_RESP_STATUS_E ret = Helios_Atcmd_Send_Sync(0, "AT+QCFG=\"emmcause\"\r\n", resp, sizeof(resp), NULL, 10); + MP_THREAD_GIL_ENTER(); + if(ret == HELIOS_AT_RESP_OK) { + Helios_Atcmd_Get_Para_Value_Str(resp, 1, 1, para, sizeof(para)); + return mp_obj_new_str(para, strlen(para)); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_net_get_reject_cause_obj, qpy_net_get_reject_cause); +#endif + + +static const mp_rom_map_elem_t net_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_net) }, + { MP_ROM_QSTR(MP_QSTR___qpy_module_deinit__), MP_ROM_PTR(&qpy_module_net_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_csqQueryPoll), MP_ROM_PTR(&qpy_net_get_csq_obj) }, + { MP_ROM_QSTR(MP_QSTR_getState), MP_ROM_PTR(&qpy_net_get_reg_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_getConfig), MP_ROM_PTR(&qpy_net_get_configuration_obj) }, + { MP_ROM_QSTR(MP_QSTR_setConfig), MP_ROM_PTR(&qpy_net_set_configuration_obj) }, + { MP_ROM_QSTR(MP_QSTR_nitzTime), MP_ROM_PTR(&qpy_net_get_nitz_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_operatorName), MP_ROM_PTR(&qpy_net_get_operator_name_obj) }, + //{ MP_ROM_QSTR(MP_QSTR_setNetMode), MP_ROM_PTR(&qpy_net_set_selection_obj) }, + { MP_ROM_QSTR(MP_QSTR_getNetMode), MP_ROM_PTR(&qpy_net_get_selection_obj) }, + { MP_ROM_QSTR(MP_QSTR_getSignal), MP_ROM_PTR(&qpy_net_get_signal_strength_obj)}, + { MP_ROM_QSTR(MP_QSTR_getCellInfo), MP_ROM_PTR(&qpy_net_get_cell_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_getCellInfos), MP_ROM_PTR(&qpy_net_get_cell_info_v2_obj) }, + + { MP_ROM_QSTR(MP_QSTR_getCi), MP_ROM_PTR(&qpy_net_get_cid_obj) }, + { MP_ROM_QSTR(MP_QSTR_getLac), MP_ROM_PTR(&qpy_net_get_lac_obj) }, + { MP_ROM_QSTR(MP_QSTR_getMnc), MP_ROM_PTR(&qpy_net_get_mnc_obj) }, + { MP_ROM_QSTR(MP_QSTR_getMcc), MP_ROM_PTR(&qpy_net_get_mcc_obj) }, + { MP_ROM_QSTR(MP_QSTR_setModemFun), MP_ROM_PTR(&qpy_net_set_modem_fun_obj) }, + { MP_ROM_QSTR(MP_QSTR_getModemFun), MP_ROM_PTR(&qpy_net_get_modem_fun_obj) }, + { MP_ROM_QSTR(MP_QSTR_setCallback), MP_ROM_PTR(&qpy_net_add_event_handler_obj) }, + + { MP_ROM_QSTR(MP_QSTR_setApn), MP_ROM_PTR(&qpy_net_set_apn_obj) }, + { MP_ROM_QSTR(MP_QSTR_getApn), MP_ROM_PTR(&qpy_net_get_apn_obj) }, + #if MICROPY_QPY_MODULE_NET_LED + { MP_ROM_QSTR(MP_QSTR_set_networkled_config), MP_ROM_PTR(&qpy_set_networkled_config_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_getServingCi), MP_ROM_PTR(&qpy_net_get_cid_servingcell_obj) }, + { MP_ROM_QSTR(MP_QSTR_getServingLac), MP_ROM_PTR(&qpy_net_get_lac_servingcell_obj) }, + { MP_ROM_QSTR(MP_QSTR_getServingMnc), MP_ROM_PTR(&qpy_net_get_mnc_servingcell_obj) }, + { MP_ROM_QSTR(MP_QSTR_getServingMcc), MP_ROM_PTR(&qpy_net_get_mcc_servingcell_obj) }, +#if defined(PLAT_Qualcomm) || defined(PLAT_ASR)|| MICROPY_QPY_MODULE_NET_BAND + { MP_ROM_QSTR(MP_QSTR_setBand), MP_ROM_PTR(&qpy_net_set_band_obj) }, + { MP_ROM_QSTR(MP_QSTR_getBand), MP_ROM_PTR(&qpy_net_get_band_obj) }, +#endif +#if defined(PLAT_ASR) || MICROPY_QPY_MODULE_NET_BAND + { MP_ROM_QSTR(MP_QSTR_bandRst), MP_ROM_PTR(&qpy_net_band_restore_obj) }, +#endif + +#if MICROPY_QPY_MODULE_FTM_MODE + { MP_ROM_QSTR(MP_QSTR_ftmModeSwitch), MP_ROM_PTR(&qpy_ftm_mode_switch_obj) }, + { MP_ROM_QSTR(MP_QSTR_ftmTestStart), MP_ROM_PTR(&qpy_ftm_mode_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_ftmTestStop), MP_ROM_PTR(&qpy_ftm_mode_stop_obj) }, +#endif + +#if MICROPY_QPY_MODULE_BACKCELL + { MP_ROM_QSTR(MP_QSTR_addBlackCell), MP_ROM_PTR(&qpy_add_black_cell_obj) }, + { MP_ROM_QSTR(MP_QSTR_getBlackCell), MP_ROM_PTR(&qpy_get_black_cell_obj) }, + { MP_ROM_QSTR(MP_QSTR_getBlackCellCfg), MP_ROM_PTR(&qpy_get_black_cell_cfg_obj) }, + { MP_ROM_QSTR(MP_QSTR_setBlackCellCfg), MP_ROM_PTR(&qpy_set_black_cell_cfg_obj) }, + { MP_ROM_QSTR(MP_QSTR_deleteBlackCell), MP_ROM_PTR(&qpy_delete_black_cell_obj) }, +#endif + +#if MICROPY_QPY_MODULE_DRX_TIMER + { MP_ROM_QSTR(MP_QSTR_setDrxTm), MP_ROM_PTR(&qpy_net_set_drxtm_obj) }, + { MP_ROM_QSTR(MP_QSTR_getDrxTm), MP_ROM_PTR(&qpy_net_get_drxtm_obj) }, +#endif + +#if MICROPY_QPY_MODULE_JAMDET + { MP_ROM_QSTR(MP_QSTR_setJamdetSwitch), MP_ROM_PTR(&qpy_jamdet_set_switch_obj) }, + { MP_ROM_QSTR(MP_QSTR_getJamdetSwitch), MP_ROM_PTR(&qpy_jamdet_get_switch_obj) }, + { MP_ROM_QSTR(MP_QSTR_setJamdetParam), MP_ROM_PTR(&qpy_jamdet_set_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_getJamdetParam), MP_ROM_PTR(&qpy_jamdet_get_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_getJamdetStatus), MP_ROM_PTR(&qpy_jamdet_get_stauts_obj) }, +#endif + +#if defined(MICROPY_QPY_MODULE_IMS_STATUS) + { MP_ROM_QSTR(MP_QSTR_getImsRegister), MP_ROM_PTR(&qpy_net_get_ims_register_obj) }, +#endif + +#if MICROPY_QPY_MODULE_NET_EX + { MP_ROM_QSTR(MP_QSTR_getT3402), MP_ROM_PTR(&qpy_net_get_T3402_obj) }, + { MP_ROM_QSTR(MP_QSTR_getT3412), MP_ROM_PTR(&qpy_net_get_T3412_obj) }, + { MP_ROM_QSTR(MP_QSTR_getT3324), MP_ROM_PTR(&qpy_net_get_T3324_obj) }, + { MP_ROM_QSTR(MP_QSTR_getTeDRX), MP_ROM_PTR(&qpy_net_get_TeDRX_obj) }, + { MP_ROM_QSTR(MP_QSTR_getTPTW), MP_ROM_PTR(&qpy_net_get_TPTW_obj) }, + { MP_ROM_QSTR(MP_QSTR_getqRxlevMin), MP_ROM_PTR(&qpy_net_get_qRxlevMin_obj) }, + { MP_ROM_QSTR(MP_QSTR_getRejectCause), MP_ROM_PTR(&qpy_net_get_reject_cause_obj) }, +#endif +}; + +static MP_DEFINE_CONST_DICT(net_module_globals, net_module_globals_table); +const mp_obj_module_t mp_module_net = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&net_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_net, mp_module_net); +#endif /* MICROPY_QPY_MODULE_NET */ diff --git a/ports/quectel/modostimer.c b/ports/quectel/modostimer.c new file mode 100644 index 0000000000000..8fcce2a3f4c86 --- /dev/null +++ b/ports/quectel/modostimer.c @@ -0,0 +1,167 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include +#include "py/compile.h" +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "py/obj.h" + +#if MICROPY_QPY_MODULE_OSTIMER + +#include "helios_os.h" +#include "helios_debug.h" + +#define HELIOS_OSTIMER_LOG(msg, ...) custom_log("osTimer", msg, ##__VA_ARGS__) + + +typedef struct _mod_ostimer_obj_t +{ + mp_obj_base_t base; + Helios_OSTimer_t handle; /* OS supplied timer reference */ + unsigned int initialTime; /* initial expiration time in ms */ + bool cyclicalEn; /* wether to enable the cyclical mode or not */ + c_callback_t callback; /* timer call-back routine */ + bool deleteFlagh; +} mod_ostimer_obj_t; + +const mp_obj_type_t mp_ostimer_type; + +STATIC void mod_ostimer_isr(void *cb) { + c_callback_t *callback = (c_callback_t *)cb; + if(NULL != callback){ + mp_sched_schedule_ex(callback, mp_const_none); + } +} + + +STATIC mp_obj_t mod_ostimer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) +{ + mod_ostimer_obj_t *timer = mp_obj_malloc_with_finaliser(mod_ostimer_obj_t); + + timer->base.type = &mp_ostimer_type; + timer->handle = Helios_OSTimer_Create(); + timer->deleteFlagh = 0; + + return MP_OBJ_FROM_PTR(timer); +} + + +STATIC mp_obj_t mod_ostimer_start(uint n_args, const mp_obj_t *args) +{ + int ret = 0; + + mod_ostimer_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (!(self->deleteFlagh)) + { + self->initialTime = mp_obj_get_int(args[1]); + + self->cyclicalEn = !!mp_obj_get_int(args[2]); + + //self->callback = args[3]; + mp_sched_schedule_callback_register(&self->callback, args[3]); + + Helios_OSTimerAttr OSTimerAttr = { + .ms = (uint32_t)self->initialTime, + .cycle_enable = self->cyclicalEn, + .cb = mod_ostimer_isr, + .argv = (void *)&self->callback + }; + ret = Helios_OSTimer_Start(self->handle, &OSTimerAttr); + } + else + { + ret = -1; + } + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_ostimer_start_obj, 3, 5, mod_ostimer_start); + +STATIC mp_obj_t mod_ostimer_stop(mp_obj_t arg0) +{ + int ret = 0; + + mod_ostimer_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (!(self->deleteFlagh)) + { + ret = Helios_OSTimer_Stop(self->handle); + } + else + { + ret = -1; + } + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ostimer_stop_obj, mod_ostimer_stop); + + + +STATIC mp_obj_t mod_ostimer_delete(mp_obj_t arg0) +{ + int ret = 0; + + mod_ostimer_obj_t *self = MP_OBJ_TO_PTR(arg0); + + if (!(self->deleteFlagh)) + { + self->deleteFlagh = 1; + Helios_OSTimer_Delete(self->handle); + HELIOS_OSTIMER_LOG("[osTimer] ostimer delete\r\n"); + } + + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ostimer_delete_obj, mod_ostimer_delete); + + +STATIC const mp_rom_map_elem_t mod_ostimer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_osTimer) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_ostimer_delete_obj) }, + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&mod_ostimer_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mod_ostimer_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_delete_timer), MP_ROM_PTR(&mod_ostimer_delete_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mod_ostimer_locals_dict, mod_ostimer_locals_dict_table); + + +const mp_obj_type_t mp_ostimer_type = { + { &mp_type_type }, + .name = MP_QSTR_osTimer, + .make_new = mod_ostimer_make_new, + .locals_dict = (mp_obj_dict_t *)&mod_ostimer_locals_dict, +}; + + +#endif /* MICROPY_QPY_MODULE_OSTIMER */ diff --git a/ports/quectel/modsim.c b/ports/quectel/modsim.c new file mode 100644 index 0000000000000..691d7ab02b5d3 --- /dev/null +++ b/ports/quectel/modsim.c @@ -0,0 +1,1128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 "stdio.h" +#include "stdlib.h" +#include "obj.h" +#include "runtime.h" +#include "mphalport.h" + +#if MICROPY_QPY_MODULE_SIM + +#include "helios_debug.h" +#include "helios_sim.h" + +#if MICROPY_QPY_MODULE_ESIM_SIM_SWITCH +#include "rt_lpa.h" +#include "rt_port_at.h" +#include "rt_utils.h" + +#define MAX_EID_HEX_LEN 16 +#define MAX_EID_LEN 33 +#define THE_MAX_CARD_NUM 20 +#endif + +#define QPY_MODSIM_LOG(msg, ...) custom_log("SIM", msg, ##__VA_ARGS__) + + +static void toUpperCase(char *str) +{ + while (*str != '\0') { + if(*str >='a' && *str <= 'z') { + *str -= 32; + } + str++; + } +} + +int _get_current_simid(void) +{ + int cur_simid = 0; + +#if defined(PLAT_ASR) ||defined(PLAT_ASR_1606) || defined(PLAT_ASR_1609) || defined(PLAT_Unisoc_8850_R02) || defined(PLAT_EIGEN) || defined(PLAT_ASR_1602) || defined(PLAT_EIGEN_718) + + uint8_t sim_id = 0; + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetCurrentSimid(&sim_id); + MP_THREAD_GIL_ENTER(); + cur_simid = sim_id; +#elif defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + cur_simid = 0; +#endif + + if(cur_simid < 0) + { + cur_simid = 0; + } + + return cur_simid; +} +/*=============================================================================*/ +/* FUNCTION: qpy_sim_get_imsi */ +/*=============================================================================*/ +/*!@brief: Get the IMSI of the SIM card + * + * @return: + * if get successfully, return the IMSI + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_get_imsi(size_t n_args, const mp_obj_t *args) +{ + int cur_simid = 0; + char imsi_str[HELIOS_SIM_IMSI_LEN+1] = {0}; + uint8_t status = 0; + int ret = 0; + if ( n_args > 0 ) + { + cur_simid = mp_obj_get_int(args[0]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetCardStatus(cur_simid, (Helios_SIM_Status_e *)&status); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + if (status != 1) + { + return mp_obj_new_int(-1); + } + } + else + { + return mp_obj_new_int(-1); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetIMSI(cur_simid, (void *)imsi_str, sizeof(imsi_str)); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_str(imsi_str, strlen(imsi_str)); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_get_imsi_obj, 0, 1, qpy_sim_get_imsi); + + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_get_iccid */ +/*=============================================================================*/ +/*!@brief: Get the ICCID of the SIM card + * + * @return: + * if get successfully, return the ICCID + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_get_iccid(size_t n_args, const mp_obj_t *args) +{ + char iccid_str[HELIOS_SIM_ICCID_LEN+1] = {0}; + uint8_t status = 0; + int ret = 0; + int cur_simid = 0; + if ( n_args > 0 ) + { + cur_simid = mp_obj_get_int(args[0]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetCardStatus(cur_simid, (Helios_SIM_Status_e *)&status); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + if (status != 1) + { + return mp_obj_new_int(-1); + } + } + else + { + return mp_obj_new_int(-1); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetICCID(cur_simid, (void *)iccid_str, sizeof(iccid_str)); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + toUpperCase(iccid_str); + return mp_obj_new_str(iccid_str, strlen(iccid_str)); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_get_iccid_obj, 0, 1, qpy_sim_get_iccid); + + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_get_phonenumber */ +/*=============================================================================*/ +/*!@brief: Get the phone number of the SIM card + * + * @return: + * if get successfully, return the phone number + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_get_phonenumber(size_t n_args, const mp_obj_t *args) +{ + char phone_number[HELIOS_SIM_PHONENUM_LEN+1] = {0}; + int ret = 0; + int cur_simid = 0; + if ( n_args > 0 ) + { + cur_simid = mp_obj_get_int(args[0]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetPhoneNumber(cur_simid, (void *)phone_number, sizeof(phone_number)); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_str(phone_number, strlen(phone_number)); + } + + return mp_obj_new_int(-1); +} + +#if !defined (PLAT_RDA) +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_get_phonenumber_obj, 0, 1, qpy_sim_get_phonenumber); +#endif +/*=============================================================================*/ +/* FUNCTION: qpy_sim_enable_pin */ +/*=============================================================================*/ +/*!@brief: enable SIM card PIN code verification, and restart will take effect + * + * @pin [in] PIN code + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +#ifndef MICROPY_QPY_MODULE_NO_SIMPIN +static mp_obj_t qpy_sim_enable_pin(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMPinInfoStruct info = {0}; + mp_buffer_info_t bufinfo = {0}; + int cur_simid = 0; + if ( n_args > 1 ) + { + cur_simid = mp_obj_get_int(args[1]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + if (bufinfo.len > HELIOS_SIM_PIN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of pin should be no more than [%d] bytes."), HELIOS_SIM_PIN_LEN_MAX); + } + + strncpy((char *)info.pin, (const char *)bufinfo.buf, bufinfo.len); + + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINEnable(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_enable_pin_obj, 1, 2, qpy_sim_enable_pin); + + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_disable_pin */ +/*=============================================================================*/ +/*!@brief: disable SIM card PIN code verification + * + * @pin [in] PIN code + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_disable_pin(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMPinInfoStruct info = {0}; + mp_buffer_info_t bufinfo = {0}; + int cur_simid = 0; + if ( n_args > 1 ) + { + cur_simid = mp_obj_get_int(args[1]); + }else{ + cur_simid = _get_current_simid(); + } + + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + if (bufinfo.len > HELIOS_SIM_PIN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of pin should be no more than [%d] bytes."), HELIOS_SIM_PIN_LEN_MAX); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + strncpy((char *)info.pin, (const char *)bufinfo.buf, bufinfo.len); + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINDisable(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_disable_pin_obj, 1, 2, qpy_sim_disable_pin); + + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_verify_pin */ +/*=============================================================================*/ +/*!@brief: when the SIM state is requested PIN/PIN2, enter the PIN/PIN2 code to verify + * + * @pin [in] PIN code + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_verify_pin(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMPinInfoStruct info = {0}; + mp_buffer_info_t bufinfo = {0}; + int cur_simid = 0; + if ( n_args > 1 ) + { + cur_simid = mp_obj_get_int(args[1]); + }else{ + cur_simid = _get_current_simid(); + } + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + if (bufinfo.len > HELIOS_SIM_PIN_LEN_MAX) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of pin should be no more than [%d] bytes."), HELIOS_SIM_PIN_LEN_MAX); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + strncpy((char *)info.pin, (const char *)bufinfo.buf, bufinfo.len); + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINVerify(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_verify_pin_obj, 1, 2,qpy_sim_verify_pin); + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_change_pin */ +/*=============================================================================*/ +/*!@brief: After enabling SIM card PIN verification, change the SIM card PIN + * + * @old_pin [in] old PIN + * @new_pin [in] new PIN + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ + +static mp_obj_t qpy_sim_change_pin(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMChangePinInfoStruct info = {0}; + mp_buffer_info_t bufinfo[2] = {0}; + int cur_simid = 0; + if ( n_args > 2 ) + { + cur_simid = mp_obj_get_int(args[2]); + }else{ + cur_simid = _get_current_simid(); + } + mp_get_buffer_raise(args[0], &bufinfo[0], MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &bufinfo[1], MP_BUFFER_READ); + + if ((bufinfo[0].len > HELIOS_SIM_PIN_LEN_MAX) || (bufinfo[1].len > HELIOS_SIM_PIN_LEN_MAX)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of pin should be no more than [%d] bytes."), HELIOS_SIM_PIN_LEN_MAX); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + strncpy((char *)info.old_pin, (const char *)bufinfo[0].buf, bufinfo[0].len); + strncpy((char *)info.new_pin, (const char *)bufinfo[1].buf, bufinfo[1].len); + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINChange(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_change_pin_obj, 2,3,qpy_sim_change_pin); + + +/*=============================================================================*/ +/* FUNCTION: qpy_sim_unblock_pin */ +/*=============================================================================*/ +/*!@brief: When the SIM card status is requested PUK/PUK2 after multiple incorrect input + * of PIN/PIN2 code, input PUK/PUK2 code and a new PIN/PIN2 code to unlock + * + * @puk [in] PUK/PUK2 + * @new_pin [in] new PIN/PIN2 + * @return: + * 0 - success + * -1 - error + */ +/*=============================================================================*/ +static mp_obj_t qpy_sim_unblock_pin(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMUnlockPinInfoStruct info = {0}; + mp_buffer_info_t bufinfo[2] = {0}; + + int cur_simid = 0; + if ( n_args > 2 ) + { + cur_simid = mp_obj_get_int(args[2]); + }else{ + cur_simid = _get_current_simid(); + + } + mp_get_buffer_raise(args[0], &bufinfo[0], MP_BUFFER_READ); + + + mp_get_buffer_raise(args[1], &bufinfo[1], MP_BUFFER_READ); + + + if ((bufinfo[0].len > HELIOS_SIM_PIN_LEN_MAX) || (bufinfo[1].len > HELIOS_SIM_PIN_LEN_MAX)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, the length of pin or puk should be no more than [%d] bytes."), HELIOS_SIM_PIN_LEN_MAX); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + strncpy((char *)info.puk, (const char *)bufinfo[0].buf, bufinfo[0].len); + strncpy((char *)info.new_pin, (const char *)bufinfo[1].buf, bufinfo[1].len); + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINUnlock(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_unblock_pin_obj, 2, 3, qpy_sim_unblock_pin); +#endif + +#if MICROPY_QPY_MODULE_PIN_REMATTEMPTS + +static mp_obj_t qpy_sim_pin_remain_attempts(size_t n_args, const mp_obj_t *args) +{ + Helios_SIMRemainPinAttempts info = {0}; + + uint8_t cur_simid = _get_current_simid(); + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_PINRemainAttempts(cur_simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + if (n_args > 0) + { + int flag = mp_obj_get_int(args[0]); + if (flag == 0) + { + mp_obj_t tuple[2] = { + mp_obj_new_int(info.pin_remain_attempts), + mp_obj_new_int(info.puk_remain_attempts) + }; + return mp_obj_new_tuple(2, tuple); + } + else if (flag == 1) + { + mp_obj_t tuple[4] = { + mp_obj_new_int(info.pin_remain_attempts), + mp_obj_new_int(info.puk_remain_attempts), + mp_obj_new_int(info.pin2_remain_attempts), + mp_obj_new_int(info.puk2_remain_attempts) + }; + return mp_obj_new_tuple(4, tuple); + } + else + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, flag should be in [0,1].")); + } + } + else + { + mp_obj_t tuple[2] = { + mp_obj_new_int(info.pin_remain_attempts), + mp_obj_new_int(info.puk_remain_attempts) + }; + return mp_obj_new_tuple(2, tuple); + } + } + return mp_obj_new_int(-1); + +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_pin_remain_attempts_obj, 0, 1, qpy_sim_pin_remain_attempts); + +#endif +/*=============================================================================*/ +/* FUNCTION: sim_get_card_status */ +/*=============================================================================*/ +/*!@brief: Get the status of the SIM card. + * + * @return : + * - 0 SIM was removed. + * - 1 SIM is ready. + * - 2 Expecting the universal PIN./SIM is locked, waiting for a CHV1 password. + * - 3 Expecting code to unblock the universal PIN./SIM is blocked, CHV1 unblocking password is required. + * - 4 SIM is locked due to a SIM/USIM personalization check failure. + * - 5 SIM is blocked due to an incorrect PCK; an MEP unblocking password is required. + * - 6 Expecting key for hidden phone book entries. + * - 7 Expecting code to unblock the hidden key. + * - 8 SIM is locked; waiting for a CHV2 password. + * - 9 SIM is blocked; CHV2 unblocking password is required. + * - 10 SIM is locked due to a network personalization check failure. + * - 11 SIM is blocked due to an incorrect NCK; an MEP unblocking password is required. + * - 12 +SIM is locked due to a network subset personalization check failure. + * - 13 SIM is blocked due to an incorrect NSCK; an MEP unblocking password is required. + * - 14 SIM is locked due to a service provider personalization check failure. + * - 15 SIM is blocked due to an incorrect SPCK; an MEP unblocking password is required. + * - 16 SIM is locked due to a corporate personalization check failure. + * - 17 SIM is blocked due to an incorrect CCK; an MEP unblocking password is required. + * - 18 SIM is being initialized; waiting for completion. + * - 19 Use of CHV1/CHV2/universal PIN/code to unblock the CHV1/code to unblock the CHV2/code to unblock the universal PIN/ is blocked. + * - 20 Unuseful status. + * - 21 Unknow status. + */ + /*=============================================================================*/ + +static mp_obj_t qpy_sim_get_card_status(size_t n_args, const mp_obj_t *args) +{ + Helios_SIM_Status_e status = 0; + int ret = 0; + int cur_simid = 0 ; + if ( n_args > 0 ) + { + cur_simid = (uint8_t)mp_obj_get_int(args[0]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetCardStatus(cur_simid, &status); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(status); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_get_card_status_obj, 0, 1, qpy_sim_get_card_status); + +#if MICROPY_QPY_MODULE_PHB +static mp_obj_t qpy_sim_get_phonebookstatus(void) +{ + uint8_t pb_ready_flag = 0; + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetPbReady(&pb_ready_flag); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(pb_ready_flag); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_sim_get_phonebookstatus_obj, qpy_sim_get_phonebookstatus); +/*=============================================================================*/ +/* FUNCTION: qpy_sim_read_phonebook_record */ +/*=============================================================================*/ +/*!@brief : Read the phone book. + * + * @args[1] [in] the storage position of the phone book. + * @args[2] [in] start_index + * @args[3] [in] end_index + * @args[4] [in] username + * @return : + * -1 - error + * If it reads successfully, the results are returned in the following format. + * (record_number, [(index, username, phone_number), ... , (index, username, phone_number)]) + * For example: + * (2, [(1, 'zhangsan', '18122483511'), (2, 'lisi', '18122483542')]) + */ +/*=============================================================================*/ + +static mp_obj_t qpy_sim_read_phonebook_record(size_t n_args, const mp_obj_t *args) +{ + uint8_t i = 0; + int32_t storage = 0; + int cur_simid = 0; + Helios_SIMReadPhoneBookInfoStruct records = {0}; + mp_buffer_info_t nameinfo = {0}; + mp_obj_t list_records = mp_obj_new_list(0, NULL); + + if ( n_args > 4 ){ + cur_simid = mp_obj_get_int(args[4]); + }else{ + cur_simid = _get_current_simid(); + } + storage = mp_obj_get_int(args[0]); + records.start_index = mp_obj_get_int(args[1]); + records.end_index = mp_obj_get_int(args[2]); + mp_get_buffer_raise(args[3], &nameinfo, MP_BUFFER_READ); + + if ((storage < 0) || (storage > 15)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, storage should be in (0,15).")); + } + if (records.end_index < records.start_index) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, end >= start.")); + } + if ((records.end_index - records.start_index) > 20) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, end - start <= 20.")); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + if (nameinfo.len > 30) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, the length of username should be no more than 30 bytes.")); + } + records.user_name = (char *)nameinfo.buf; + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_ReadPhonebookRecord(cur_simid, storage, &records); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + for (i=0; i 4 ){ + cur_simid = mp_obj_get_int(args[4]); + } else{ + cur_simid = _get_current_simid(); + } + storage = mp_obj_get_int(args[0]); + record.index = mp_obj_get_int(args[1]); + mp_get_buffer_raise(args[2], &bufinfo[0], MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &bufinfo[1], MP_BUFFER_READ); + + if ((storage < 0) || (storage > 15)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, storage should be in (0,15).")); + } + if ((record.index < 1) || (record.index > 500)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, index should be in (1,500).")); + } + if (bufinfo[0].len > 30) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, the length of username should be no more than 30 bytes.")); + } + if (bufinfo[1].len > 20) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, the length of phonenumber should be no more than 20 bytes.")); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + strncpy(record.user_name, bufinfo[0].buf, bufinfo[0].len); + strncpy(record.phone_num, bufinfo[1].buf, bufinfo[1].len); + + + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_WritePhonebookRecord(cur_simid, storage, &record); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(0); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_write_phonebook_record_obj, 3,5, qpy_sim_write_phonebook_record); + +#endif + + +#if MICROPY_QPY_MODULE_SIMDET +static mp_obj_t qpy_sim_set_simdet(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int cur_simid = 0; + + int detenable = 0; + int insertlevel = 0; + if ( n_args > 2 ){ + cur_simid = mp_obj_get_int(args[2]); + }else{ + cur_simid = _get_current_simid(); + } + detenable = mp_obj_get_int(args[0]); + insertlevel = mp_obj_get_int(args[1]); + + if (detenable != 0 && detenable != 1) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, detenable should be in (0,1).")); + } + if (insertlevel != 0 && insertlevel != 1) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, insertlevel should be in (0,1).")); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_SetSimDet(cur_simid, detenable, insertlevel); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + return mp_obj_new_int(ret); + } + + return mp_obj_new_int(-1); +} + +static mp_obj_t qpy_sim_get_simdet(size_t n_args, const mp_obj_t *args) +{ + int ret = 0; + int detenable = 99; + int insertlevel = 99; + int cur_simid = 0; + if ( n_args > 0 ){ + cur_simid = mp_obj_get_int(args[0]); + }else{ + cur_simid = _get_current_simid(); + } + if ((cur_simid != 0) && (cur_simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simId should be in [0~1].")); + } + MP_THREAD_GIL_EXIT(); + ret = Helios_SIM_GetSimDet(cur_simid, &detenable, &insertlevel); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + mp_obj_t tuple[2] = + { + mp_obj_new_int(detenable), + mp_obj_new_int(insertlevel), + }; + return mp_obj_new_tuple(2, tuple); + } + + return mp_obj_new_int(-1); +} + +static c_callback_t *g_sim_user_callback; + +static void qpy_sim_event_handler(uint8_t sim_id, unsigned int event_id, void *ctx) +{ + if(g_sim_user_callback) + { + QPY_MODSIM_LOG("[SIM] callback start.\r\n"); + GC_STACKTOP_SET(); + mp_sched_schedule_ex(g_sim_user_callback, mp_obj_new_int(event_id)); + GC_STACKTOP_CLEAR(); + QPY_MODSIM_LOG("[SIM] callback end.\r\n"); + } +} + +static mp_obj_t qpy_sim_add_event_handler(mp_obj_t handler) +{ + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + g_sim_user_callback = &cb; + mp_sched_schedule_callback_register(g_sim_user_callback, handler); + MP_THREAD_GIL_EXIT(); + Helios_SIM_Add_Event_Handler(qpy_sim_event_handler); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_set_simdet_obj, 2, 3, qpy_sim_set_simdet); + +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qpy_sim_get_simdet_obj, 0, 1, qpy_sim_get_simdet); + +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_sim_add_event_handler_obj, qpy_sim_add_event_handler); +#endif + +#if defined(PLAT_ASR) +static mp_obj_t qpy_sim_genericaccess(const mp_obj_t sim_id, const mp_obj_t cmd) +{ + Helios_SIMGenericAccesStruct info = {0}; + int simid = mp_obj_get_int(sim_id); + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(cmd, &bufinfo, MP_BUFFER_READ); + + if (bufinfo.len <= 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, the length of cmd should be more than 0 bytes.")); + } + + if (simid != 0) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid simid.")); + } + + strncpy((char *)info.cmd, (const char *)bufinfo.buf, bufinfo.len); + info.len = bufinfo.len; + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_GenericAccess(simid, &info); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + mp_obj_t tuple[2] = + { + mp_obj_new_int(strlen(info.resp)), + mp_obj_new_str(info.resp, strlen(info.resp)), + }; + return mp_obj_new_tuple(2, tuple); + //return mp_obj_new_str(info.resp, strlen(info.resp)); + } + return mp_obj_new_int(-1); +} +static MP_DEFINE_CONST_FUN_OBJ_2(qpy_sim_genericaccess_obj, qpy_sim_genericaccess); +#endif + +#if MICROPY_QPY_MODULE_DUALSIM || MICROPY_QPY_MODULE_DSDS +static c_callback_t * g_switchsim_done_callback; + +static void switch_sim_done_callback(uint8_t state) +{ + if (g_switchsim_done_callback) + { + GC_STACKTOP_SET(); + mp_sched_schedule_ex(g_switchsim_done_callback, mp_obj_new_int(state)); + GC_STACKTOP_CLEAR(); + } +} + +static mp_obj_t qpy_sim_set_switchcard_cb(mp_obj_t switchsim_callback) +{ + static c_callback_t cb = {0}; + memset(&cb, 0, sizeof(c_callback_t)); + g_switchsim_done_callback = &cb; + mp_sched_schedule_callback_register(g_switchsim_done_callback, switchsim_callback); + + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_sim_set_switchcard_cb_obj, qpy_sim_set_switchcard_cb); + +static mp_obj_t qpy_sim_switch_card(mp_obj_t sim_id) +{ + uint8_t simid = mp_obj_get_int(sim_id); + if ((simid != 0) && (simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simid should be in [0,1].")); + } +#if MICROPY_QPY_MODULE_DUALSIM + uint8_t old_simid = HELIOS_SIM_0; + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetCurrentSimid(&old_simid); + MP_THREAD_GIL_ENTER(); + if (simid == old_simid) + { + return mp_obj_new_int(-1); + } +#endif + MP_THREAD_GIL_EXIT(); + int ret = Helios_SIM_SwitchCard(simid, switch_sim_done_callback); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_sim_switch_card_obj, qpy_sim_switch_card); + +static mp_obj_t qpy_sim_get_current_simid() +{ + uint8_t simid = HELIOS_SIM_0; + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetCurrentSimid(&simid); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(simid); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_sim_get_current_simid_obj, qpy_sim_get_current_simid); + +#if defined(BOARD_EC800MCN_LE_VOLVGL) || defined(BOARD_EC800MCN_LE_CPE) || defined(BOARD_EG810MEU_LA_VOLVGL) +static mp_obj_t qpy_sim_is_insert(mp_obj_t sim_id) +{ + int simid = mp_obj_get_int(sim_id); + if ((simid != 0) && (simid != 1)) + { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid value, simid should be in [0,1].")); + } + uint8_t insert_sta = 0; + MP_THREAD_GIL_EXIT(); + Helios_SIM_GetInsertStatus(simid, &insert_sta); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_bool(insert_sta); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_sim_is_insert_obj, qpy_sim_is_insert); +#endif + +#endif + +static mp_obj_t qpy_module_sim_deinit(void) +{ + #if MICROPY_QPY_MODULE_DUALSIM || MICROPY_QPY_MODULE_DSDS + g_switchsim_done_callback = NULL; + #endif + + #if MICROPY_QPY_MODULE_SIMDET + g_sim_user_callback = NULL; + MP_THREAD_GIL_EXIT(); + Helios_SIM_Add_Event_Handler(NULL); + MP_THREAD_GIL_ENTER(); + #endif + + return mp_obj_new_int(0); +} + +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_module_sim_deinit_obj, qpy_module_sim_deinit); + +#if MICROPY_QPY_MODULE_ESIM_SIM_SWITCH +static mp_obj_t qpy_sim_get_esim_list(void) +{ + int ret = 0; + u08 profile_num =0; + profile_info_t card_info[THE_MAX_CARD_NUM] = {0}; + mp_obj_t list_profile = mp_obj_new_list(0, NULL); + MP_THREAD_GIL_EXIT(); + ret = rt_lpa_get_profile_info(card_info, &profile_num, THE_MAX_CARD_NUM); + MP_THREAD_GIL_ENTER(); + if (ret == 0) + { + int i = 0; + for (i = 0; i < profile_num; i++) + { + mp_obj_t profile_info[3] = { + mp_obj_new_str(card_info[i].iccid, strlen(card_info[i].iccid)), + mp_obj_new_int(card_info[i].state), + mp_obj_new_int(card_info[i].class)}; + mp_obj_list_append(list_profile, mp_obj_new_tuple(3, profile_info)); + } + } + mp_obj_t tuple[2] = + { + mp_obj_new_int(profile_num), + list_profile, + }; + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_sim_get_esim_list_obj, qpy_sim_get_esim_list); + + +static mp_obj_t qpy_sim_get_esim_eid(void) +{ + char eid_buf[MAX_EID_HEX_LEN] = {0}; + char eid[MAX_EID_LEN] = {0}; + int ret = -1; + MP_THREAD_GIL_EXIT(); + ret = rt_lpa_get_eid((u08 *)eid_buf); + rt_utils_bytes_to_hex((cpu08)eid_buf, sizeof(eid_buf), eid); + MP_THREAD_GIL_ENTER(); + if (ret != 0) + { + memset(eid, 0, sizeof(eid)); + } + return mp_obj_new_str(eid, strlen(eid)); +} +static MP_DEFINE_CONST_FUN_OBJ_0(qpy_sim_get_esim_eid_obj, qpy_sim_get_esim_eid); + +static mp_obj_t qpy_sim_enable_esim(const mp_obj_t iccid) +{ + int ret = -1; + mp_buffer_info_t qpy_iccid = {0}; + mp_get_buffer_raise(iccid, &qpy_iccid, MP_BUFFER_READ); + + if (qpy_iccid.len != 20) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, the length of iccid should be 20 bytes.")); + } + MP_THREAD_GIL_EXIT(); + ret = rt_lpa_enable_profile(qpy_iccid.buf); + MP_THREAD_GIL_ENTER(); + return mp_obj_new_int(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_1(qpy_sim_enable_esim_obj, qpy_sim_enable_esim); +#endif + +#if MICROPY_QPY_MODULE_ESIM +extern const struct _mp_obj_module_t mp_module_esim; +#endif + +static const mp_rom_map_elem_t mp_module_sim_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sim) }, + +#if MICROPY_QPY_MODULE_ESIM + { MP_ROM_QSTR(MP_QSTR_esim), MP_ROM_PTR(&mp_module_esim) }, +#endif + + { MP_ROM_QSTR(MP_QSTR_getImsi), MP_ROM_PTR(&qpy_sim_get_imsi_obj) }, + { MP_ROM_QSTR(MP_QSTR_getIccid), MP_ROM_PTR(&qpy_sim_get_iccid_obj) }, + +#ifndef MICROPY_QPY_MODULE_NO_SIMPIN + { MP_ROM_QSTR(MP_QSTR_verifyPin), MP_ROM_PTR(&qpy_sim_verify_pin_obj) }, + { MP_ROM_QSTR(MP_QSTR_changePin), MP_ROM_PTR(&qpy_sim_change_pin_obj) }, + { MP_ROM_QSTR(MP_QSTR_unblockPin), MP_ROM_PTR(&qpy_sim_unblock_pin_obj) }, + + { MP_ROM_QSTR(MP_QSTR_enablePin), MP_ROM_PTR(&qpy_sim_enable_pin_obj) }, + { MP_ROM_QSTR(MP_QSTR_disablePin), MP_ROM_PTR(&qpy_sim_disable_pin_obj) }, +#endif + + { MP_ROM_QSTR(MP_QSTR_getStatus), MP_ROM_PTR(&qpy_sim_get_card_status_obj)}, +#if MICROPY_QPY_MODULE_PHB + { MP_ROM_QSTR(MP_QSTR_getPhonebookStatus), MP_ROM_PTR(&qpy_sim_get_phonebookstatus_obj) }, + { MP_ROM_QSTR(MP_QSTR_readPhonebook), MP_ROM_PTR(&qpy_sim_read_phonebook_record_obj) }, + { MP_ROM_QSTR(MP_QSTR_writePhonebook), MP_ROM_PTR(&qpy_sim_write_phonebook_record_obj) }, +#endif +#if defined (PLAT_ASR) + { MP_ROM_QSTR(MP_QSTR_genericAccess), MP_ROM_PTR(&qpy_sim_genericaccess_obj) }, +#endif +#if MICROPY_QPY_MODULE_DUALSIM || MICROPY_QPY_MODULE_DSDS + { MP_ROM_QSTR(MP_QSTR_switchCard), MP_ROM_PTR(&qpy_sim_switch_card_obj) }, + { MP_ROM_QSTR(MP_QSTR_setSwitchcardCallback), MP_ROM_PTR(&qpy_sim_set_switchcard_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_getCurSimid), MP_ROM_PTR(&qpy_sim_get_current_simid_obj) }, +#if defined(BOARD_EC800MCN_LE_VOLVGL) || defined(BOARD_EC800MCN_LE_CPE) || defined(BOARD_EG810MEU_LA_VOLVGL) + { MP_ROM_QSTR(MP_QSTR_isInsert), MP_ROM_PTR(&qpy_sim_is_insert_obj) }, +#endif +#endif +#if MICROPY_QPY_MODULE_SIMDET + { MP_ROM_QSTR(MP_QSTR_setSimDet), MP_ROM_PTR(&qpy_sim_set_simdet_obj) }, + { MP_ROM_QSTR(MP_QSTR_getSimDet), MP_ROM_PTR(&qpy_sim_get_simdet_obj) }, + { MP_ROM_QSTR(MP_QSTR_setCallback), MP_ROM_PTR(&qpy_sim_add_event_handler_obj) }, +#endif + + { MP_ROM_QSTR(MP_QSTR___qpy_module_deinit__), MP_ROM_PTR(&qpy_module_sim_deinit_obj) }, + +#if !defined (PLAT_RDA) + { MP_ROM_QSTR(MP_QSTR_getPhoneNumber), MP_ROM_PTR(&qpy_sim_get_phonenumber_obj)}, +#endif +#if MICROPY_QPY_MODULE_PIN_REMATTEMPTS + { MP_ROM_QSTR(MP_QSTR_getPinRemAttempts), MP_ROM_PTR(&qpy_sim_pin_remain_attempts_obj) }, +#endif +#if MICROPY_QPY_MODULE_ESIM_SIM_SWITCH + { MP_ROM_QSTR(MP_QSTR_eSimGetList), MP_ROM_PTR(&qpy_sim_get_esim_list_obj) }, + { MP_ROM_QSTR(MP_QSTR_eSimGetEid), MP_ROM_PTR(&qpy_sim_get_esim_eid_obj) }, + { MP_ROM_QSTR(MP_QSTR_eSimSwitch), MP_ROM_PTR(&qpy_sim_enable_esim_obj) }, +#endif +}; +static MP_DEFINE_CONST_DICT(mp_module_sim_globals, mp_module_sim_globals_table); + + +const mp_obj_module_t mp_module_sim = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_sim_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_sim, mp_module_sim); +#endif + + diff --git a/ports/quectel/modsim.h b/ports/quectel/modsim.h new file mode 100644 index 0000000000000..ecd199080932e --- /dev/null +++ b/ports/quectel/modsim.h @@ -0,0 +1,222 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MODSIM_H__ +#define __MODSIM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define QL_SIM_MCC_LEN 4 /** Length of the MCC. */ +#define QL_SIM_MNC_MAX 4 /** Maximum length of the MNC. */ +#define QL_SIM_PLMN_NUM_MAX 24 /** Maximum number of PLMN data sets. */ + +typedef enum +{ + QL_SIM_SUCCESS, + QL_SIM_GENERIC_FAILURE, +}QL_SIM_ERROR_CODE; + + + +typedef struct +{ + uint8_t mcc[QL_SIM_MCC_LEN]; /**< MCC value in ASCII characters.*/ + uint8_t mnc[QL_SIM_MNC_MAX]; /**< MNC value in ASCII characters.*/ +}QL_SIM_PLMN_INFO; + +typedef struct +{ + unsigned int preferred_operator_list_num; /**< Must be set to the number of elements in preferred_operator_list. */ + QL_SIM_PLMN_INFO preferred_operator_list[QL_SIM_PLMN_NUM_MAX]; /**< Preferred operator list. */ +}QL_SIM_PREFERRED_OPERATOR_LIST; /* Message */ + +#define QL_SIM_PIN_LEN_MAX 16 /** Maximum length of PIN data. */ +typedef struct +{ + uint8_t pin_value[QL_SIM_PIN_LEN_MAX]; /* Value of the PIN */ +}QL_SIM_VERIFY_PIN_INFO; + +typedef struct +{ + uint8_t old_pin_value[QL_SIM_PIN_LEN_MAX]; /**< Value of the old PIN as a sequence of ASCII characters. */ + uint8_t new_pin_value[QL_SIM_PIN_LEN_MAX]; /**< Value of the new PIN as a sequence of ASCII characters. */ +}QL_SIM_CHANGE_PIN_INFO; + +typedef struct +{ + uint8_t puk_value[QL_SIM_PIN_LEN_MAX]; /**< Value of the PUK as a sequence of ASCII characters. */ + uint8_t new_pin_value[QL_SIM_PIN_LEN_MAX]; /**< Value of the new PIN as a sequence of ASCII characters. */ +}QL_SIM_UNBLOCK_PIN_INFO; + +typedef enum +{ + QL_SIM_CARD_TYPE_UNKNOWN = 0, /**< Unidentified card type. */ + QL_SIM_CARD_TYPE_ICC = 1, /**< Card of SIM or RUIM type. */ + QL_SIM_CARD_TYPE_UICC = 2, /**< Card of USIM or CSIM type. */ +}QL_SIM_CARD_TYPE; + +typedef enum +{ + QL_SIM_STATUS_NOT_INSERTED, + QL_SIM_STATUS_READY, + QL_SIM_STATUS_SIM_PIN, + QL_SIM_STATUS_SIM_PUK, + QL_SIM_STATUS_PH_SIM_LOCK_PIN, + QL_SIM_STATUS_PH_SIM_LOCK_PUK, + QL_SIM_STATUS_PH_FSIM_PIN, + QL_SIM_STATUS_PH_FSIM_PUK, + QL_SIM_STATUS_SIM_PIN2, + QL_SIM_STATUS_SIM_PUK2, + QL_SIM_STATUS_PH_NET_PIN, + QL_SIM_STATUS_PH_NET_PUK, + QL_SIM_STATUS_PH_NET_SUB_PIN, + QL_SIM_STATUS_PH_NET_SUB_PUK, + QL_SIM_STATUS_PH_SP_PIN, + QL_SIM_STATUS_PH_SP_PUK, + QL_SIM_STATUS_PH_CORP_PIN, + QL_SIM_STATUS_PH_CORP_PUK, + QL_SIM_STATUS_BUSY, + QL_SIM_STATUS_BLOCKED, + QL_SIM_STATUS_UNKNOWN +}QL_SIM_STATUS; /**< Card state. */ + +typedef struct +{ + uint8_t pin1_num_retries; /**< Number of PIN 1 retries. */ + uint8_t puk1_num_retries; /**< Number of PUK 1 retries. */ + uint8_t pin2_num_retries; /**< Number of PIN 2 retries. */ + uint8_t puk2_num_retries; /**< Number of PUK 2 retries. */ +}QL_SIM_CARD_PIN_INFO; + + + +typedef struct +{ + QL_SIM_CARD_TYPE card_type; // SIM card type + QL_SIM_STATUS card_state; //SIM card state + QL_SIM_CARD_PIN_INFO card_pin_info; // PIN info +}QL_SIM_CARD_INFO; + +typedef enum +{ + QL_SIM_FILE_TYPE_UNKNOWN = 0, /**< Unknown file type */ + QL_SIM_FILE_TYPE_TRANSPARENT = 1, /**< File structure consisting of a sequence of bytes. */ + QL_SIM_FILE_TYPE_CYCLIC = 2, /**< File structure consisting of a sequence of records, each containing the same fixed size in + chronological order. Once all the records have been used, the oldest data is overwritten. */ + QL_SIM_FILE_TYPE_LINEAR_FIXED = 3, /**< File structure consisting of a sequence of records, each containing the same fixed size. */ +}QL_SIM_FILE_TYPE; + +typedef enum +{ + QL_SIM_FILE_ACCESS_TYPE_ALWAYS =0, + QL_SIM_FILE_ACCESS_TYPE_CHV1 =1, + QL_SIM_FILE_ACCESS_TYPE_CHV2 =2, + QL_SIM_FILE_ACCESS_TYPE_ADM =3, +}QL_SIM_FILE_ACCESS_TYPE; + +typedef struct +{ + QL_SIM_FILE_ACCESS_TYPE read_access; + QL_SIM_FILE_ACCESS_TYPE update_access; +}QL_SIM_FILE_ACCESS_INFO; + +typedef enum +{ + QL_SIM_FILE_STATUS_INVALID =0, + QL_SIM_FILE_STATUS_EFFECTIVE =1, +}QL_SIM_FILE_STATUS; + + +typedef struct +{ + unsigned int id; + QL_SIM_FILE_TYPE type; /**< File type: */ + QL_SIM_FILE_ACCESS_INFO access; /**< File access conditions: */ + QL_SIM_FILE_STATUS status; /**< File status: */ + unsigned int size; /**< Size of transparent files.*/ + unsigned int record_len; /**< Size of each cyclic or linear fixed file record.*/ + unsigned int record_count; /**< Number of cyclic or linear fixed file records.*/ +}QL_SIM_FILE_INFO; + +typedef struct +{ + int sw1; + int sw2; +}QL_SIM_FILE_OPERATION_RET; + +#define QL_SIM_DATA_LEN_MAX 255 +typedef struct +{ + unsigned int data_len; /**< Must be set to the number of elements in data. */ + uint8_t data[QL_SIM_DATA_LEN_MAX]; /**< Data retrieved from the card. */ +}QL_SIM_CARD_FILE_DATA; + +typedef enum +{ + QL_SIM_PHONE_BOOK_STORAGE_DC, /**< 0 - ME dialed calls list */ + QL_SIM_PHONE_BOOK_STORAGE_EN, /**< 1 - SIM (or ME) emergency number */ + QL_SIM_PHONE_BOOK_STORAGE_FD, /**< 2 - SIM fix dialing-phone book */ + QL_SIM_PHONE_BOOK_STORAGE_LD, /**< 3 - SIM last-dialing-phone book */ + QL_SIM_PHONE_BOOK_STORAGE_MC, /**< 4 - ME missed (unanswered) calls list */ + QL_SIM_PHONE_BOOK_STORAGE_ME, /**< 5 - Mobile equipment phonebook */ + QL_SIM_PHONE_BOOK_STORAGE_MT, /**< 6 - */ + QL_SIM_PHONE_BOOK_STORAGE_ON, /**< 7 - SIM own numbers (MSISDNs) list */ + QL_SIM_PHONE_BOOK_STORAGE_RC, /**< 8 - ME received calls list */ + QL_SIM_PHONE_BOOK_STORAGE_SM, /**< 9 - SIM phonebook */ + QL_SIM_PHONE_BOOK_STORAGE_AP, /**< 10 - */ + QL_SIM_PHONE_BOOK_STORAGE_MBDN, /**< 11 - */ + QL_SIM_PHONE_BOOK_STORAGE_MN, /**< 12 - */ + QL_SIM_PHONE_BOOK_STORAGE_SDN, /**< 13 - */ + QL_SIM_PHONE_BOOK_STORAGE_ICI, /**< 14 - */ + QL_SIM_PHONE_BOOK_STORAGE_OCI, /**< 15 - */ +}QL_SIM_PHONE_BOOK_STORAGE; + +#define QL_SIM_PHONE_BOOK_RECORDS_MAX_COUNT 20 + +typedef struct +{ // when write, if phonenum is empty, it means to delete this item specified by index + int index; // the record index in phone book + uint8_t username[32]; // username + uint8_t phonenum[24]; // Phone number, it can include '+'*/ +}QL_SIM_PHONE_BOOK_RECORD_INFO; + + +typedef struct +{ + int record_count; //the count of record + QL_SIM_PHONE_BOOK_RECORD_INFO record[QL_SIM_PHONE_BOOK_RECORDS_MAX_COUNT]; // the list of record +}QL_SIM_PHONE_BOOK_RECORDS_INFO; + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ports/quectel/modsocket.c b/ports/quectel/modsocket.c new file mode 100644 index 0000000000000..96921d480425e --- /dev/null +++ b/ports/quectel/modsocket.c @@ -0,0 +1,1280 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include +#include "mpconfigport.h" + +#if MICROPY_QPY_MODULE_USOCKET +#include "mpconfigboard.h" + +#include "py/runtime0.h" +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "shared/netutils/netutils.h" + +#if defined(PLAT_SONY_ALT1350) +#include "sockets.h" +#include "netdb.h" +#include "ip4.h" +#include "igmp.h" +#include "ip_addr.h" +#include "errno.h" + +#else +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/ip4.h" +#include "lwip/igmp.h" +#endif + +#include "helios_datacall.h" + +#define SOCKET_POLL_US (100000) +#define MDNS_QUERY_TIMEOUT_MS (5000) +#define MDNS_LOCAL_SUFFIX ".local" +#define TIMEOUT_MAX (0xFFFFFFFF) + + +enum { + SOCKET_STATE_NEW, + SOCKET_STATE_CONNECTED, + SOCKET_STATE_PEER_CLOSED, +}; + +typedef struct _socket_obj_t { + mp_obj_base_t base; + int fd; + int timeout; + uint8_t domain; + uint8_t type; + uint8_t proto; + uint8_t state; + unsigned int retries; + #if MICROPY_PY_USOCKET_EVENTS + mp_obj_t events_callback; + struct _socket_obj_t *events_next; + #endif +} socket_obj_t; + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms); + +#if MICROPY_PY_USOCKET_EVENTS +// Support for callbacks on asynchronous socket events (when socket becomes readable) + +// This divisor is used to reduce the load on the system, so it doesn't poll sockets too often +#define USOCKET_EVENTS_DIVISOR (8) + +static uint8_t usocket_events_divisor; +static socket_obj_t *usocket_events_head; + +void usocket_events_deinit(void) { + usocket_events_head = NULL; +} + +// Assumes the socket is not already in the linked list, and adds it +static void usocket_events_add(socket_obj_t *sock) { + sock->events_next = usocket_events_head; + usocket_events_head = sock; +} + +// Assumes the socket is already in the linked list, and removes it +static void usocket_events_remove(socket_obj_t *sock) { + for (socket_obj_t **s = &usocket_events_head;; s = &(*s)->events_next) { + if (*s == sock) { + *s = (*s)->events_next; + return; + } + } +} + +// Polls all registered sockets for readability and calls their callback if they are readable +void usocket_events_handler(void) { + if (usocket_events_head == NULL) { + return; + } + if (--usocket_events_divisor) { + return; + } + usocket_events_divisor = USOCKET_EVENTS_DIVISOR; + + fd_set rfds; + FD_ZERO(&rfds); + int max_fd = 0; + + for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) { + FD_SET(s->fd, &rfds); + max_fd = MAX(max_fd, s->fd); + } + + // Poll the sockets + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + int r = select(max_fd + 1, &rfds, NULL, NULL, &timeout); + if (r <= 0) { + return; + } + + // Call the callbacks + for (socket_obj_t *s = usocket_events_head; s != NULL; s = s->events_next) { + if (FD_ISSET(s->fd, &rfds)) { + mp_call_function_1_protected(s->events_callback, s); + } + } +} + +#endif // MICROPY_PY_USOCKET_EVENTS + +static inline void check_for_exceptions(void) { + mp_handle_pending(true); +} + +// This function mimics lwip_getaddrinfo, with added support for mDNS queries +static int _socket_getaddrinfo3(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) { + +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int pdp = 1;//Helios_DataCall_GetCurrentPDP(); + return getaddrinfowithcid(nodename, servname, hints, res, pdp); + +#elif defined(PLAT_SONY_ALT1350) + return lwip_getaddrinfo(nodename, servname, hints, res, NULL); +#else + int pdp = Helios_DataCall_GetCurrentPDP(); + + // Normal query + return getaddrinfo_with_pcid(nodename, servname, hints, res, pdp); +#endif +} +extern void mp_hal_stdout_tx_str(const char *str); +static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { +#if defined(PLAT_SONY_ALT1350) + const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, + }; +#else + const struct addrinfo hints = { + .ai_family = AF_UNSPEC,//AF_INET, + .ai_socktype = SOCK_STREAM, + }; +#endif + + mp_obj_t port = portx; + if (mp_obj_is_small_int(port)) { + // This is perverse, because lwip_getaddrinfo promptly converts it back to an int, but + // that's the API we have to work with ... + port = mp_obj_str_binary_op(MP_BINARY_OP_MODULO, mp_obj_new_str_via_qstr("%s", 2), port); + } + const char *host_str = mp_obj_str_get_str(host); + const char *port_str = mp_obj_str_get_str(port); + + if (host_str[0] == '\0') { + // a host of "" is equivalent to the default/all-local IP address + host_str = "0.0.0.0"; + } + + MP_THREAD_GIL_EXIT(); + int res = _socket_getaddrinfo3(host_str, port_str, &hints, resp); + MP_THREAD_GIL_ENTER(); + + // Per docs: instead of raising gaierror getaddrinfo raises negative error number + if (res != 0) { + mp_raise_OSError(res > 0 ? -res : res); + } + // Somehow LwIP returns a resolution of 0.0.0.0 for failed lookups, traced it as far back + // as netconn_gethostbyname_addrtype returning OK instead of error. + if (*resp == NULL || + (strcmp(resp[0]->ai_canonname, "0.0.0.0") == 0 && strcmp(host_str, "0.0.0.0") != 0)) { + mp_raise_OSError(-2); // name or service not known + } + + return res; +} + +static void _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { + mp_obj_t *elem; + mp_obj_get_array_fixed_n(addrtuple, 2, &elem); + _socket_getaddrinfo2(elem[0], elem[1], resp); +} + +static mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 3, false); + + //socket_obj_t *sock = mp_obj_malloc_with_finaliser(socket_obj_t, usocket_events_head); + socket_obj_t *sock = (socket_obj_t *) m_malloc_with_finaliser(sizeof(socket_obj_t)); + sock->base.type = type_in; + sock->domain = AF_INET; + sock->type = SOCK_STREAM; + sock->proto = 0; + if (n_args > 0) { + sock->domain = mp_obj_get_int(args[0]); + if (n_args > 1) { + sock->type = mp_obj_get_int(args[1]); + if (n_args > 2) { + sock->proto = mp_obj_get_int(args[2]); + } + } + } + + sock->state = sock->type == SOCK_STREAM ? SOCKET_STATE_NEW : SOCKET_STATE_CONNECTED; + + sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); + if (sock->fd < 0) { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + int socket_errno = lwip_get_soc_errno(); +#elif defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = EINVAL; +#else + int socket_errno = errno; +#endif + mp_raise_OSError(socket_errno); + } + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + if ((sock->proto != 7) && (sock->proto != 8)) + { + Helios_DataCallInfoStruct info = {0}; + int ret = 0, pid = 0; + for (pid=1; pid<5; pid++) + { + ret = Helios_DataCall_GetInfo(pid, 0, &info); + if (ret == 0) + { + if ((info.v4.state == 1) || (info.v6.state == 1)) + { + break; + } + } + } + + if (ret != 0) + { + mp_raise_OSError(-1); + } + + if (sock->domain == AF_INET) + { + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + + char ip4_ip_addr[16] = {0}; + inet_ntop(AF_INET, &info.v4.addr.ip, ip4_ip_addr, sizeof(ip4_ip_addr)); + inet_pton(AF_INET, ip4_ip_addr, &server_addr.sin_addr.s_addr); + + server_addr.sin_family = AF_INET; + server_addr.sin_port = 0; + lwip_bind(sock->fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in)); + } + else if (sock->domain == AF_INET6) + { + struct sockaddr_in6 local_v6; + memset(&local_v6, 0, sizeof(local_v6)); + + char ip6_ip_addr[64] = {0}; + inet_ntop(AF_INET6, &info.v6.addr.ip, ip6_ip_addr, sizeof(ip6_ip_addr)); + inet_pton(AF_INET6, ip6_ip_addr, &local_v6.sin6_addr); + + local_v6.sin6_family = AF_INET6; + local_v6.sin6_port = 0; + lwip_bind(sock->fd, (struct sockaddr *)&local_v6, sizeof(struct sockaddr_in6)); + } + } +#endif + + _socket_settimeout(sock, TIMEOUT_MAX); + + return MP_OBJ_FROM_PTR(sock); +} + +static mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + + mp_obj_t *elem; + mp_obj_get_array_fixed_n(arg1, 2, &elem); + const char *addr_info = mp_obj_str_get_str(elem[0]); + int server_port = mp_obj_get_int(elem[1]); + + Helios_DataCallInfoStruct info = {0}; + if (addr_info[0] == '\0') + { + int ret = 0, pid = 0; + for (pid=1; pid<5; pid++) + { + ret = Helios_DataCall_GetInfo(pid, 0, &info); + if (ret == 0) + { + if ((info.v4.state == 1) || (info.v6.state == 1)) + { + break; + } + } + } + + if (ret != 0) + { + mp_raise_OSError(-1); + } + } + + if (self->domain == AF_INET) + { + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + server_addr.sin_port = lwip_htons(server_port); +#elif defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + server_addr.sin_port = htons(server_port); +#endif + + if (addr_info[0] == '\0') + { + char ip4_ip_addr[16] = {0}; + inet_ntop(AF_INET, &info.v4.addr.ip, ip4_ip_addr, sizeof(ip4_ip_addr)); + inet_pton(AF_INET, ip4_ip_addr, &server_addr.sin_addr.s_addr); + } + else + { + inet_pton(AF_INET, addr_info, &server_addr.sin_addr.s_addr); + } + + lwip_bind(self->fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in)); + } + else if (self->domain == AF_INET6) + { + struct sockaddr_in6 local_v6; + memset(&local_v6, 0, sizeof(local_v6)); + + local_v6.sin6_family = AF_INET6; +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + local_v6.sin6_port = lwip_htons(server_port); +#elif defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + local_v6.sin6_port = htons(server_port); +#endif + + if (addr_info[0] == '\0') + { + char ip6_ip_addr[64] = {0}; + inet_ntop(AF_INET6, &info.v6.addr.ip, ip6_ip_addr, sizeof(ip6_ip_addr)); + //QPY_SOCKET_LOG("[socket][%s][%d]bind IPV6 datacall info .\r\n", ip6_ip_addr, server_port); + inet_pton(AF_INET6, ip6_ip_addr, &local_v6.sin6_addr); + } + else + { + inet_pton(AF_INET6, addr_info, &local_v6.sin6_addr); + //QPY_SOCKET_LOG("[socket][%s][%d]bind IPV6 addr info .\r\n", addr_info, server_port); + } + + lwip_bind(self->fd, (struct sockaddr *)&local_v6, sizeof(struct sockaddr_in6)); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); + +// method socket.listen([backlog]) +static mp_obj_t socket_listen(size_t n_args, const mp_obj_t *args) { + socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + int backlog = 0; + if (n_args > 1) { + backlog = mp_obj_get_int(args[1]); + backlog = (backlog < 0) ? 0 : backlog; + } + + self->state = SOCKET_STATE_CONNECTED; + int r = lwip_listen(self->fd, backlog); + if (r < 0) { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + mp_raise_OSError(socket_errno); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_listen_obj, 1, 2, socket_listen); + +static mp_obj_t socket_accept(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + mp_obj_t tuple[3]; + char ip4_addr_buf[16] = {0}; + char ip6_addr_buf[128] = {0}; + int ip4_port = 0; + int ip6_port = 0; + + int new_fd = -1; + for (unsigned int i = 0; i <= self->retries; i++) + { + if (self->domain == AF_INET) + { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + struct sockaddr_in addr_v4; + socklen_t addr_v4_len = sizeof(struct sockaddr_in); + + MP_THREAD_GIL_EXIT(); + new_fd = lwip_accept(self->fd, (struct sockaddr *)&addr_v4, &addr_v4_len); + MP_THREAD_GIL_ENTER(); + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + if (socket_errno != EAGAIN) { + mp_raise_OSError(socket_errno); + } + + inet_ntop(AF_INET, &addr_v4.sin_addr, ip4_addr_buf, sizeof(ip4_addr_buf)); + ip4_port = ntohs(addr_v4.sin_port); +#endif + } + else if (self->domain == AF_INET6) + { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) || defined(PLAT_SONY_ALT1350) + struct sockaddr_in6 addr_v6; + socklen_t addr_v6_len = sizeof(struct sockaddr_in6); + + MP_THREAD_GIL_EXIT(); + new_fd = lwip_accept(self->fd, (struct sockaddr *)&addr_v6, &addr_v6_len); + MP_THREAD_GIL_ENTER(); + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + if (socket_errno != EAGAIN) { + mp_raise_OSError(socket_errno); + } + + inet_ntop(AF_INET6, &addr_v6.sin6_addr, ip6_addr_buf, sizeof(ip6_addr_buf)); + ip6_port = ntohs(addr_v6.sin6_port); +#endif + } + + if (new_fd >= 0) { + break; + } + + check_for_exceptions(); + } + + if (new_fd < 0) + { + if (self->retries == 0) { + mp_raise_OSError(MP_EAGAIN); + } else { + mp_raise_OSError(MP_ETIMEDOUT); + } + } + + // create new socket object + socket_obj_t *sock = (socket_obj_t *) m_malloc_with_finaliser(sizeof(socket_obj_t)); + sock->base.type = self->base.type; + sock->fd = new_fd; + sock->domain = self->domain; + sock->type = self->type; + sock->proto = self->proto; + sock->state = SOCKET_STATE_CONNECTED; + tuple[0] = MP_OBJ_FROM_PTR(sock); + + if (self->domain == AF_INET) + { + tuple[1] = mp_obj_new_str(ip4_addr_buf, strlen(ip4_addr_buf)); + tuple[2] = mp_obj_new_int(ip4_port); + } + else if (self->domain == AF_INET6) + { + tuple[1] = mp_obj_new_str(ip6_addr_buf, strlen(ip6_addr_buf)); + tuple[2] = mp_obj_new_int(ip6_port); + } + + _socket_settimeout(sock, TIMEOUT_MAX); + return mp_obj_new_tuple(3, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); + +static mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + int r = 0; + + if ((self->type == SOCK_STREAM) && (self->state == SOCKET_STATE_CONNECTED)){ + mp_raise_OSError(114); + } + + self->state = SOCKET_STATE_CONNECTED; + + struct addrinfo *res; + _socket_getaddrinfo(arg1, &res); + + if (self->timeout > 0 && self->timeout < 25) + { + int sock_nbio = 1; + lwip_ioctl(self->fd, FIONBIO, &sock_nbio); + + MP_THREAD_GIL_EXIT(); + r = lwip_connect(self->fd, res->ai_addr, res->ai_addrlen); + MP_THREAD_GIL_ENTER(); +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + +#if defined(PLAT_SONY_ALT1350) + if((r == -1) && (socket_errno != 119)) +#else + if((r == -1) && (socket_errno != 115)) +#endif + { + lwip_freeaddrinfo(res); + mp_raise_OSError(socket_errno); + } + + lwip_freeaddrinfo(res); + + struct timeval t; + t.tv_sec = self->timeout; + t.tv_usec = 0; + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + fd_set read_fds, write_fds; + FD_ZERO(&read_fds); + FD_SET(self->fd, &read_fds); + FD_ZERO(&write_fds); + FD_SET(self->fd, &write_fds); + + MP_THREAD_GIL_EXIT(); + r = lwip_select(self->fd + 1, &read_fds, &write_fds, NULL, &t); + MP_THREAD_GIL_ENTER(); + + if(r <= 0) + { + mp_raise_OSError(MP_ETIMEDOUT); + } + + int value; + int len = sizeof(value); + //get so_error to check connect be RST or not + getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &value, (socklen_t *)&len); + if(value == ECONNRESET) + { + mp_raise_OSError(ECONNRESET); + } +#elif defined(PLAT_SONY_ALT1350) + fd_set read_fds, write_fds; + FD_ZERO(&read_fds);FD_ZERO(&write_fds); + FD_SET(self->fd, &read_fds);FD_SET(self->fd, &write_fds); + + MP_THREAD_GIL_EXIT(); + r = lwip_select(self->fd + 1, &read_fds, &write_fds, NULL, &t); + MP_THREAD_GIL_ENTER(); + + if(r <= 0) + { + mp_raise_OSError(MP_ETIMEDOUT); + } + else + { + if (FD_ISSET(self->fd, &read_fds)) + { + char buf[10]; + int bytes = lwip_recvfrom(self->fd, buf, 0, 0, NULL, NULL); + + socket_errno = errno; + if(bytes < 0) + { + mp_raise_OSError(socket_errno); + } + } + } +#else + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(self->fd, &read_fds); + + MP_THREAD_GIL_EXIT(); + r = lwip_select(self->fd + 1, &read_fds, NULL, NULL, &t); + MP_THREAD_GIL_ENTER(); + + if(r <= 0) + { + mp_raise_OSError(MP_ETIMEDOUT); + } +#endif + + sock_nbio = 0; + lwip_ioctl(self->fd, FIONBIO, &sock_nbio); + } + else + { + MP_THREAD_GIL_EXIT(); + r = lwip_connect(self->fd, res->ai_addr, res->ai_addrlen); + MP_THREAD_GIL_ENTER(); +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + if (r != 0) + { + lwip_freeaddrinfo(res); + mp_raise_OSError(socket_errno); + } + lwip_freeaddrinfo(res); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); + +static mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { + (void)n_args; // always 4 + socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + int opt = mp_obj_get_int(args[2]); + + switch (opt) { + // level: SOL_SOCKET + case SO_REUSEADDR: { + int val = mp_obj_get_int(args[3]); + int ret = lwip_setsockopt(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); + if (ret != 0) { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + mp_raise_OSError(socket_errno); + } + break; + } + + case TCP_KEEPALIVE: { + if (self->type == SOCK_STREAM) + { + int val = mp_obj_get_int(args[3]); + int optval = 1; + int ret = lwip_setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(int)); + + optval = val*60; + ret = lwip_setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(int)); + + optval = 25; + ret = lwip_setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(int));//TCP_KEEPINTVL + + optval = 3; + ret = lwip_setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(int)); + if (ret != 0) { +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + mp_raise_OSError(socket_errno); + } + } + break; + } + + #if MICROPY_PY_USOCKET_EVENTS + // level: SOL_SOCKET + // special "register callback" option + case 20: { + if (args[3] == mp_const_none) { + if (self->events_callback != MP_OBJ_NULL) { + usocket_events_remove(self); + self->events_callback = MP_OBJ_NULL; + } + } else { + if (self->events_callback == MP_OBJ_NULL) { + usocket_events_add(self); + } + self->events_callback = args[3]; + } + break; + } + #endif + default: + mp_printf(&mp_plat_print, "Warning: lwip.setsockopt() option not implemented\n"); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms) { + // Rather than waiting for the entire timeout specified, we wait sock->retries times + // for SOCKET_POLL_US each, checking for a MicroPython interrupt between timeouts. + // with SOCKET_POLL_MS == 100ms, sock->retries allows for timeouts up to 13 years. + // if timeout_ms == UINT64_MAX, wait forever. + sock->retries = (timeout_ms == TIMEOUT_MAX) ? UINT_MAX : timeout_ms * 1000 / SOCKET_POLL_US; + + sock->timeout = timeout_ms/1000; + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = timeout_ms ? SOCKET_POLL_US : 0 + }; + lwip_setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_fcntl(sock->fd, F_SETFL, timeout_ms ? 0 : O_NONBLOCK); +} + +static mp_obj_t socket_settimeout(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (arg1 == mp_const_none) { + _socket_settimeout(self, TIMEOUT_MAX); + } else { + #if MICROPY_PY_BUILTINS_FLOAT + _socket_settimeout(self, (uint64_t)(mp_obj_get_float(arg1) * MICROPY_FLOAT_CONST(1000.0))); + #else + _socket_settimeout(self, mp_obj_get_int(arg1) * 1000); + #endif + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); + +static mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (mp_obj_is_true(arg1)) { + _socket_settimeout(self, TIMEOUT_MAX); + } else { + _socket_settimeout(self, 0); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +// XXX this can end up waiting a very long time if the content is dribbled in one character +// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not +// good behaviour. +static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, + struct sockaddr *from, socklen_t *from_len, int *errcode) { + socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); + + // A new socket cannot be read from. + if (sock->state == SOCKET_STATE_NEW) { + *errcode = MP_ENOTCONN; + return MP_STREAM_ERROR; + } + + // If the peer closed the connection then the lwIP socket API will only return "0" once + // from lwip_recvfrom and then block on subsequent calls. To emulate POSIX behaviour, + // which continues to return "0" for each call on a closed socket, we set a flag when + // the peer closed the socket. + if (sock->state == SOCKET_STATE_PEER_CLOSED) { + return 0; + } + + unsigned int retries = 0; + if (sock->timeout > 5) + { + retries = ((sock->timeout/5) - 1); + if (!retries) + retries = 1; + } + else + { + retries = sock->retries; + } + + // XXX Would be nicer to use RTC to handle timeouts + for (unsigned int i = 0; i <= retries; ++i) { + // Poll the socket to see if it has waiting data and only release the GIL if it doesn't. + // This ensures higher performance in the case of many small reads, eg for readline. + + if (sock->timeout > 5) + { + unsigned int retries_i = 0; + retries_i = sock->timeout/5; + if (!((retries_i - 1) == retries)) + { + retries = retries_i; + } + } + else + { + retries = sock->retries; + } + + if (sock->timeout > 5) + { + struct timeval timeout = { .tv_sec = 5, .tv_usec = 0 }; + lwip_setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)); + } + + MP_THREAD_GIL_EXIT(); + int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len); + MP_THREAD_GIL_ENTER(); + +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#elif defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + int socket_errno = lwip_get_error(sock->fd); +#else + int socket_errno = errno; +#endif + if (r == 0) + { + sock->state = SOCKET_STATE_PEER_CLOSED; + if (socket_errno == ENOTCONN)//recv FIN + { + *errcode = ENOTCONN; + return MP_STREAM_ERROR; + } + } + + if (r >= 0) { + return r; + } + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) + int32_t tcpstate = (int32_t)lwip_getTcpState(sock->fd); + if ((tcpstate < 4) && (sock->type == SOCK_STREAM)) + { + if (socket_errno == EWOULDBLOCK) + { + mp_raise_OSError(115); + } + } +#endif + +#if defined(PLAT_EIGEN) + if ((socket_errno != EWOULDBLOCK) && (socket_errno != 62)) +#else + if (socket_errno != EWOULDBLOCK) +#endif + { + *errcode = socket_errno; + return MP_STREAM_ERROR; + } + check_for_exceptions(); + } + + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, + struct sockaddr *from, socklen_t *from_len) { + size_t len = mp_obj_get_int(len_in); + vstr_t vstr; + vstr_init_len(&vstr, len); + + int errcode; + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + if (ret == MP_STREAM_ERROR) { + mp_raise_OSError(errcode); + } + + vstr.len = ret; + return mp_obj_new_str_from_vstr(&vstr); +} + +static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { + return _socket_recvfrom(self_in, len_in, NULL, NULL); +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); + +static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + struct sockaddr from; + socklen_t fromlen = sizeof(from); + + mp_obj_t tuple[2]; + tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + + uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr; + mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port); + tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + +int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { + int sentlen = 0; + for (unsigned int i = 0; i <= sock->retries && sentlen < (int) datalen; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write(sock->fd, data + sentlen, datalen - sentlen); + MP_THREAD_GIL_ENTER(); +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + // lwip returns EINPROGRESS when trying to send right after a non-blocking connect + if (r < 0 && socket_errno != EWOULDBLOCK && socket_errno != EINPROGRESS) { + mp_raise_OSError(socket_errno); + //mp_raise_OSError(errno); + } + if (r > 0) { + sentlen += r; + } + check_for_exceptions(); + } + if (sentlen == 0) { + mp_raise_OSError(sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT); + } + return sentlen; +} + +static mp_obj_t socket_send(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg1, &bufinfo, MP_BUFFER_READ); + int r = _socket_send(sock, bufinfo.buf, bufinfo.len); + return mp_obj_new_int(r); +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); + +static mp_obj_t socket_sendall(const mp_obj_t arg0, const mp_obj_t arg1) { + // XXX behaviour when nonblocking (see extmod/modlwip.c) + // XXX also timeout behaviour. + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg1, &bufinfo, MP_BUFFER_READ); + int r = _socket_send(sock, bufinfo.buf, bufinfo.len); + if (r < (int) bufinfo.len) { + mp_raise_OSError(MP_ETIMEDOUT); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_sendall_obj, socket_sendall); + +static mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // get the buffer to send + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + // create the destination address + struct sockaddr_in to; + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + to.sin_port = lwip_htons(netutils_parse_inet_addr(addr_in, (uint8_t *)&to.sin_addr, NETUTILS_BIG)); + + // send the data + for (unsigned int i = 0; i <= self->retries; i++) { + MP_THREAD_GIL_EXIT(); + int ret = lwip_sendto(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr *)&to, sizeof(to)); + MP_THREAD_GIL_ENTER(); + if (ret > 0) { + return mp_obj_new_int_from_uint(ret); + } +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + if (ret == -1 && socket_errno != EWOULDBLOCK) { + mp_raise_OSError(socket_errno); + //mp_raise_OSError(errno); + } + check_for_exceptions(); + } + mp_raise_OSError(MP_ETIMEDOUT); +} +static MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); + +static mp_obj_t socket_fileno(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + return mp_obj_new_int(self->fd); +} +static MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno); + +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_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { + return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); +} + +static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + socket_obj_t *sock = self_in; + for (unsigned int i = 0; i <= sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write(sock->fd, buf, size); + MP_THREAD_GIL_ENTER(); + if (r > 0) { + return r; + } +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + // lwip returns MP_EINPROGRESS when trying to write right after a non-blocking connect + if (r < 0 && socket_errno != EWOULDBLOCK && socket_errno != EINPROGRESS) { + *errcode = socket_errno; + return MP_STREAM_ERROR; + } + check_for_exceptions(); + } + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +static mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + socket_obj_t *socket = self_in; + if (request == MP_STREAM_POLL) { + if (socket->fd == -1) { + return MP_STREAM_POLL_NVAL; + } + + fd_set rfds; + FD_ZERO(&rfds); + fd_set wfds; + FD_ZERO(&wfds); + fd_set efds; + FD_ZERO(&efds); + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + if (arg & MP_STREAM_POLL_RD) { + FD_SET(socket->fd, &rfds); + } + if (arg & MP_STREAM_POLL_WR) { + FD_SET(socket->fd, &wfds); + } + if (arg & MP_STREAM_POLL_HUP) { + FD_SET(socket->fd, &efds); + } + + int r = select((socket->fd) + 1, &rfds, &wfds, &efds, &timeout); + if (r < 0) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + + mp_uint_t ret = 0; + if (FD_ISSET(socket->fd, &rfds)) { + ret |= MP_STREAM_POLL_RD; + } + if (FD_ISSET(socket->fd, &wfds)) { + ret |= MP_STREAM_POLL_WR; + } + if (FD_ISSET(socket->fd, &efds)) { + ret |= MP_STREAM_POLL_HUP; + } + + // New (unconnected) sockets are writable and have HUP set. + if (socket->state == SOCKET_STATE_NEW) { + ret |= (arg & MP_STREAM_POLL_WR) | MP_STREAM_POLL_HUP; + } + + return ret; + } else if (request == MP_STREAM_CLOSE) { + if (socket->fd >= 0) { + #if MICROPY_PY_USOCKET_EVENTS + if (socket->events_callback != MP_OBJ_NULL) { + usocket_events_remove(socket); + socket->events_callback = MP_OBJ_NULL; + } + #endif + int ret = lwip_close(socket->fd); +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + int socket_errno = lwip_get_soc_errno(); +#else + int socket_errno = errno; +#endif + if (ret != 0) { + *errcode = errno; + *errcode = socket_errno; + return MP_STREAM_ERROR; + } + socket->fd = -1; + } + return 0; + } + + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; +} + +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) +static mp_obj_t socket_getstate(const mp_obj_t self_in) +{ + socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + int32_t tcpstate = (int32_t)lwip_getTcpState(self->fd); + return mp_obj_new_int(tcpstate); +} +static MP_DEFINE_CONST_FUN_OBJ_1(socket_getstate_obj, socket_getstate); +#endif + + +static const mp_rom_map_elem_t socket_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&socket_bind_obj) }, + { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&socket_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&socket_accept_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&socket_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&socket_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_sendall), MP_ROM_PTR(&socket_sendall_obj) }, + { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&socket_sendto_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&socket_recv_obj) }, + { MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&socket_recvfrom_obj) }, + { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&socket_setsockopt_obj) }, + { MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&socket_settimeout_obj) }, + { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, + { MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&socket_makefile_obj) }, +#if defined(PLAT_Unisoc) || defined(PLAT_Unisoc_8850) || defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) + { MP_ROM_QSTR(MP_QSTR_getsocketsta), MP_ROM_PTR(&socket_getstate_obj) }, +#endif + { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&socket_fileno_obj) }, + + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +static MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); + +static const mp_stream_p_t socket_stream_p = { + .read = socket_stream_read, + .write = socket_stream_write, + .ioctl = socket_stream_ioctl +}; + +MP_DEFINE_CONST_OBJ_TYPE( + socket_type, + MP_QSTR_socket, + MP_TYPE_FLAG_NONE, + make_new, socket_make_new, + protocol, &socket_stream_p, + locals_dict, &socket_locals_dict + ); + +static mp_obj_t quec_socket_getaddrinfo(size_t n_args, const mp_obj_t *args) { + // TODO support additional args beyond the first two + + struct addrinfo *res = NULL; + _socket_getaddrinfo2(args[0], args[1], &res); + mp_obj_t ret_list = mp_obj_new_list(0, NULL); + + for (struct addrinfo *resi = res; resi; resi = resi->ai_next) + { + mp_obj_t addrinfo_objs[5] = { + mp_obj_new_int(resi->ai_family), + mp_obj_new_int(resi->ai_socktype), + mp_obj_new_int(resi->ai_protocol), + mp_obj_new_str(resi->ai_canonname, strlen(resi->ai_canonname)), + mp_const_none + }; + + if (resi->ai_family == AF_INET) + { + char buf[16]; + struct sockaddr_in *addr = (struct sockaddr_in *)resi->ai_addr; + // This looks odd, but it's really just a u32_t + ip_addr_t ip4_addr = { .u_addr.ip4.addr = addr->sin_addr.s_addr }; + ipaddr_ntoa_r(&ip4_addr, buf, sizeof(buf)); + mp_obj_t inaddr_objs[2] = { + mp_obj_new_str(buf, strlen(buf)), + mp_obj_new_int(ntohs(addr->sin_port)) + }; + addrinfo_objs[4] = mp_obj_new_tuple(2, inaddr_objs); + } + else if (resi->ai_family == AF_INET6) + { + char ip6_addr_buf[128] = {0}; + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)resi->ai_addr; + inet_ntop(AF_INET6, &addr->sin6_addr, ip6_addr_buf, sizeof(ip6_addr_buf)); + mp_obj_t inaddr_objs[2] = { + mp_obj_new_str(ip6_addr_buf, strlen(ip6_addr_buf)), + mp_obj_new_int(ntohs(addr->sin6_port)) + }; + addrinfo_objs[4] = mp_obj_new_tuple(2, inaddr_objs); + } + + mp_obj_list_append(ret_list, mp_obj_new_tuple(5, addrinfo_objs)); + } + + if (res) { + lwip_freeaddrinfo(res); + } + return ret_list; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(quec_socket_getaddrinfo_obj, 2, 6, quec_socket_getaddrinfo); + +static mp_obj_t quec_socket_initialize() { + static int initialized = 0; + if (!initialized) { + initialized = 1; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(quec_socket_initialize_obj, quec_socket_initialize); + +static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usocket) }, + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&quec_socket_initialize_obj) }, + { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&socket_type) }, + { MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&quec_socket_getaddrinfo_obj) }, + + { MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(AF_INET) }, + { MP_ROM_QSTR(MP_QSTR_AF_INET6), MP_ROM_INT(AF_INET6) }, + { MP_ROM_QSTR(MP_QSTR_SOCK_STREAM), MP_ROM_INT(SOCK_STREAM) }, + { MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), MP_ROM_INT(SOCK_DGRAM) }, + { MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(SOCK_RAW) }, + { MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP), MP_ROM_INT(IPPROTO_TCP) }, + { MP_ROM_QSTR(MP_QSTR_IPPROTO_UDP), MP_ROM_INT(IPPROTO_UDP) }, + { MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP_SER), MP_ROM_INT(7) }, + { MP_ROM_QSTR(MP_QSTR_TCP_CUSTOMIZE_PORT), MP_ROM_INT(8) }, + { MP_ROM_QSTR(MP_QSTR_IPPROTO_IP), MP_ROM_INT(IPPROTO_IP) }, + { MP_ROM_QSTR(MP_QSTR_SOL_SOCKET), MP_ROM_INT(SOL_SOCKET) }, + { MP_ROM_QSTR(MP_QSTR_TCP_KEEPALIVE), MP_ROM_INT(TCP_KEEPALIVE) }, + { MP_ROM_QSTR(MP_QSTR_SO_REUSEADDR), MP_ROM_INT(SO_REUSEADDR) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); + +const mp_obj_module_t mp_module_usocket = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_socket_globals, +}; + +// Note: This port doesn't define MICROPY_PY_USOCKET or MICROPY_PY_LWIP so +// this will not conflict with the common implementation provided by +// extmod/mod{lwip,usocket}.c. +MP_REGISTER_MODULE(MP_QSTR_usocket, mp_module_usocket); + +#endif //MICROPY_QPY_MODULE_USOCKET diff --git a/ports/quectel/modules/_boot.py b/ports/quectel/modules/_boot.py new file mode 100644 index 0000000000000..08b55c4f39ae5 --- /dev/null +++ b/ports/quectel/modules/_boot.py @@ -0,0 +1,144 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uos +import ujson +import dial + +''' +Mark.Zhu - 2022/12/02 +Added the littlefs2 mount function,it will be called after a failed attempt to mount littlefs1 +''' +# global datacall_flag +datacall_flag = 1 + +def _auto_activate_pdpcontext(): + if "datacall_config.json" not in uos.listdir('/usr'): + # raise ValueError("Not found datacall_config.json.") + dial.setAutoConnect(1, 1) + dial.setAsynMode(1) + dial.start(1, 0, '', '', '', 0) + dial.setAsynMode(0) + else: + retval = dial.getPdpRange() + max_profile = retval[1] + + with open("/usr/datacall_config.json", "r", encoding='utf-8') as fd: + try: + datacall_config = ujson.load(fd) + if not isinstance(datacall_config, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + + for profile_id in range(1, max_profile+1): + value_of_key_profileid = datacall_config.get(str(profile_id), None) + if value_of_key_profileid is not None: + if not isinstance(value_of_key_profileid, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + value_of_key_autoActivate = value_of_key_profileid.get("autoActivate", 0) + value_of_key_autoConnect = value_of_key_profileid.get("autoConnect", 0) + if value_of_key_autoConnect==1: + dial.setAutoConnect(profile_id, value_of_key_autoConnect) + if value_of_key_autoActivate == 1: + dial.setAsynMode(1) + ret = -1 + repeat_count = 2 + while (ret == -1) and (repeat_count >= 0): + repeat_count -= 1 + ret = dial.start(profile_id, 0, '', '', '', 0) + if ret == -1: + print('Activation of the {} PDP Context failed.'.format(profile_id)) + dial.setAsynMode(0) + else: + pass + except Exception: + raise ValueError("The format of the datacall_config.json is incorrect.") + + +def _check_data_call(): + retval = dial.getPdpRange() + min = retval[0] + max = retval[1] + + for pdp in range(min, max+1): + nw_sta = dial.getInfo(pdp, 2) + if (nw_sta != -1) and (nw_sta[2][0] == 0) and (nw_sta[3][0] == 0): + continue + elif (nw_sta != -1) and ((nw_sta[2][0] == 1) or (nw_sta[3][0] == 1)): + return 1 + return 0 + + +def _repl_enable(): + global datacall_flag + if "system_config.json" in uos.listdir("/usr/"): + with open("/usr/system_config.json", "r", encoding='utf-8') as fd: + try: + json_data = ujson.load(fd) + repl_flag = json_data.get("replFlag", 0) + datacall_flag = json_data.get("datacallFlag", 1) + repl_pswd = json_data.get("replPswd",None) + + import misc + if repl_pswd: + misc.replUpdatePassswd(repl_pswd,repl_pswd) + misc.replEnable(repl_flag, repl_pswd) + else: + misc.replEnable(repl_flag) + except ValueError: + with open("/usr/system_config.json", "w", encoding='utf-8') as fdw: + new_json_data = ujson.dumps({"replFlag": 0, "datacallFlag": datacall_flag}) + fdw.write(new_json_data) + else: + with open("/usr/system_config.json", "w+", encoding='utf-8') as fd: + repl_data = ujson.dumps({"replFlag": 0}) + fd.write(repl_data) + +try: + udev = None + bdev = None + try: + from uos import VfsLfs1 as VfsLfs + except Exception: + from uos import VfsLfs2 as VfsLfs + + udev = uos.FlashDevice('customer_fs', 4096) + try: + uos.mount(udev, '/usr') + except Exception as e: + if 'ENODEV' in str(e): + VfsLfs.mkfs(udev) + uos.mount(udev, '/usr') + else: + raise ValueError("LFS usr mount fail".format(e)) + + bdev = uos.FlashDevice('customer_backup_fs', 4096) + try: + uos.mount(bdev, '/bak') + except Exception as e: + if 'ENODEV' in str(e): + VfsLfs.mkfs(bdev) + uos.mount(bdev, '/bak') + else: + raise ValueError("LFS bak mount fail".format(e)) + + +except Exception: + print('error ocurs in boot step.') + +finally: + _repl_enable() + if datacall_flag == 1: + ret = _check_data_call() + if ret == 0: + _auto_activate_pdpcontext() diff --git a/ports/quectel/modules/app_fota.py b/ports/quectel/modules/app_fota.py new file mode 100644 index 0000000000000..fac458ac6990f --- /dev/null +++ b/ports/quectel/modules/app_fota.py @@ -0,0 +1,33 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!python3 +# -*- coding:utf-8 -*- + +import app_fota_download +import app_fota_updater +from app_fota_mount import AppFotaPkgMount +class new(object): + def __init__(self): + self.app_fota_pkg_mount = AppFotaPkgMount() + self.app_fota_pkg_mount.mount_disk() + def download(self, url, file_name, headers=None): + return app_fota_download.download(url, file_name, headers) + def bulk_download(self, info=[], headers=None): + return app_fota_download.bulk_download(info, headers) + def set_update_flag(self): + app_fota_download.set_update_flag() + def update(self): + return app_fota_updater.update() + \ No newline at end of file diff --git a/ports/quectel/modules/app_fota_download.py b/ports/quectel/modules/app_fota_download.py new file mode 100644 index 0000000000000..dc57e95462d73 --- /dev/null +++ b/ports/quectel/modules/app_fota_download.py @@ -0,0 +1,212 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!python3 +# -*- coding:utf-8 -*- + +import uos +import request +import ujson +import ql_fs +from app_fota_mount import AppFotaPkgMount +app_fota_pkg_mount = AppFotaPkgMount() + +def get_updater_dir(): + return app_fota_pkg_mount.fota_dir + '/usr/.updater' + +def get_download_stat_file(): + return app_fota_pkg_mount.fota_dir + '/usr/.updater/download.stat' + +def get_update_flag_file(): + return app_fota_pkg_mount.fota_dir + '/usr/.updater/update.flag' + +updater_dir = get_updater_dir() +download_stat_file = get_download_stat_file() +update_flag_file = get_update_flag_file() + + +def _get_download_stat_by_file(file_name): + try: + if not ql_fs.path_exists(get_download_stat_file()): + return None + + fp = open(get_download_stat_file(), 'rt') + if not fp: + return None + + fr = fp.read() + fp.close() + if not fr: + return None + + download_stat = ujson.loads(fr) + for item in download_stat: + if item['name'].lower() == file_name.lower(): + return item + return None + except Exception as e: + return None + + +def _get_download_stat(): + try: + if not ql_fs.path_exists(get_download_stat_file()): + return None + + fp = open(get_download_stat_file(), 'rt') + if not fp: + return None + + fr = fp.read() + fp.close() + if not fr: + return None + + download_stat = ujson.loads(fr) + return download_stat + except Exception as e: + print("get download stat error: "+str(e)) + return None + + +def get_download_stat(): + return _get_download_stat() + + +def _fetch(url, fetched_size, headers=None): + if headers is None or not isinstance(headers, dict): + request_headers = {} + else: + request_headers = headers + request_headers['Range'] = 'bytes={}-'.format(fetched_size) + return request.get(url, headers=request_headers) + + +def _update_download_stat(url, file_name, total_size): + need_append_stat = 1 + download_stat = _get_download_stat() + if download_stat: + for item in download_stat: + if item['name'].lower() == file_name.lower(): + item['url'] = url + item['total_size'] = total_size + need_append_stat = 0 + break + else: + download_stat = [] + + if need_append_stat: + single_download_stat = {} + single_download_stat['url'] = url + single_download_stat['name'] = file_name + single_download_stat['total_size'] = total_size + download_stat.append(single_download_stat) + + json_str = ujson.dumps(download_stat) + fp = open(get_download_stat_file(), 'wt') + fp.write(json_str) + fp.close() + + +def update_download_stat(url, file_name, total_size): + _update_download_stat(url, file_name, total_size) + + +def delete_update_file(file_name): + download_stat = _get_download_stat() + if download_stat: + for item in download_stat[:]: + if item['name'].lower() == file_name.lower(): + download_stat.remove(item) + json_str = ujson.dumps(download_stat) + fp = open(get_download_stat_file(), 'wt') + fp.write(json_str) + fp.close() + + +def download(url, file_name, headers=None): + download_file_name = get_updater_dir() + file_name + ql_fs.mkdirs(ql_fs.path_dirname(download_file_name)) + + single_download_stat = _get_download_stat_by_file(file_name) + if single_download_stat: + if not ql_fs.path_exists(download_file_name): + fetched_size = 0 + else: + fetched_size = ql_fs.path_getsize(download_file_name) + + if fetched_size == single_download_stat['total_size']: + return 0 + else: + fetched_size = 0 + + r = _fetch(url, fetched_size, headers) + if r.status_code == 200 or r.status_code == 206: + total_size = -1 + need_refresh_stat_again = 0 + file_open_mode = '' + if not single_download_stat: + total_size = 'unknown' + _update_download_stat(url, file_name, total_size) + file_open_mode = 'wb+' + else: + file_open_mode = 'ab+' + + fp = open(download_file_name, file_open_mode) + content = r.content + + try: + while True: + c = next(content) + length = len(c) + for i in range(0, length, 4096): + fp.write(c[i:i + 4096]) + except StopIteration: + fp.close() + + r.close() + else: + fp.close() + uos.remove(download_file_name) + r.close() + return -1 + + total_size = ql_fs.path_getsize(download_file_name) + _update_download_stat(url, file_name, total_size) + + return 0 + else: + r.close() + return -1 + + +def bulk_download(info=None, headers=None): + if info is None: + info = [] + fail_result = [] + for item in info: + url = item['url'] + file_name = item['file_name'] + if download(url, file_name, headers) == -1: + fail_result.append(item) + if len(fail_result): + return fail_result + return None + + +def set_update_flag(): + ql_fs.mkdirs(ql_fs.path_dirname(get_update_flag_file())) + fp = open(get_update_flag_file(), 'wb') + fp.close() + diff --git a/ports/quectel/modules/app_fota_mount.py b/ports/quectel/modules/app_fota_mount.py new file mode 100644 index 0000000000000..3b98052f75a56 --- /dev/null +++ b/ports/quectel/modules/app_fota_mount.py @@ -0,0 +1,49 @@ +import uos + + +class AppFotaPkgMount(object): + __instance = None + __mount_state = False + __can_mount = False + def __new__(cls, *args, **kwargs): + if cls.__instance is None: + cls.__instance = object.__new__(cls, *args, **kwargs) + return cls.__instance + + def __init__(self): + self.__fota_dir = "/fota" + + @property + def mount_state(self): + return self.__mount_state + + @property + def can_mount(self): + return self.__can_mount + + @property + def fota_dir(self): + if not self.can_mount: + return "" + else: + return self.__fota_dir + + def mount_disk(self): + try: + if not self.mount_state: + fota_pkg = uos.VfsTemp("customer_temp_fs") + uos.mount(fota_pkg, self.__fota_dir) + except Exception as e: + self.__mount_state = False + self.__can_mount = False + else: + self.__mount_state = True + self.__can_mount = True + + def umount_disk(self): + if self.mount_state: + uos.umount(self.fota_dir) + self.__mount_state = False + + def get_fota_file_name(self, path): + return self.fota_dir + path \ No newline at end of file diff --git a/ports/quectel/modules/app_fota_updater.py b/ports/quectel/modules/app_fota_updater.py new file mode 100644 index 0000000000000..9214090a1b73c --- /dev/null +++ b/ports/quectel/modules/app_fota_updater.py @@ -0,0 +1,91 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!python3 +# -*- coding:utf-8 -*- + +import uos +import ujson +import ql_fs +import checksum +from app_fota_mount import AppFotaPkgMount +app_fota_pkg_mount = AppFotaPkgMount() + +def get_updater_dir(): + return app_fota_pkg_mount.fota_dir+ '/usr/.updater' + +def get_download_stat_file(): + return app_fota_pkg_mount.fota_dir+'/usr/.updater/download.stat' + +def get_update_flag_file(): + return app_fota_pkg_mount.fota_dir+'/usr/.updater/update.flag' + +updater_dir = get_updater_dir() +download_stat_file = get_download_stat_file() +download_stat_file_max_size = 16384 +update_flag_file = get_update_flag_file() + +def _check_update_flag(): + try: + if ql_fs.path_exists(get_update_flag_file()): + return 1 + else: + return 0 + except Exception as e: + return 0 + +def update(): + if _check_update_flag(): + try: + if not ql_fs.path_exists(get_download_stat_file()): + return -1 + + fp = open(get_download_stat_file(), 'rt') + if not fp: + return -1 + + fr = fp.read(download_stat_file_max_size) + fp.close() + if not fr: + return -1 + download_stat = ujson.loads(fr) + print(download_stat) + download_stat_tmp = download_stat[:] + print(download_stat_tmp) + for item in download_stat: + file_name = item['name'] + download_file_name = get_updater_dir() + file_name + if ql_fs.path_exists(download_file_name): + ql_fs.mkdirs(ql_fs.path_dirname(file_name)) + ql_fs.file_copy(file_name, download_file_name) + checksum.update(file_name) + download_stat_tmp.remove(item) + json_str = ujson.dumps(download_stat_tmp) + fp = open(get_download_stat_file(), 'wt') + if not fp: + return -1 + fp.write(json_str) + fp.close() + uos.remove(download_file_name) + uos.remove(get_update_flag_file()) + uos.remove(get_download_stat_file()) + ql_fs.rmdirs(get_updater_dir()) + app_fota_pkg_mount.umount_disk() + return 0 + + except Exception as e: + return -1 + else: + app_fota_pkg_mount.umount_disk() + return -1 \ No newline at end of file diff --git a/ports/quectel/modules/checkNet.py b/ports/quectel/modules/checkNet.py new file mode 100644 index 0000000000000..b19a69eb8d880 --- /dev/null +++ b/ports/quectel/modules/checkNet.py @@ -0,0 +1,231 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +''' +jayceon 2020/12/31 +''' + +import sim +import net +import utime +import modem +import dial +import dataCall +# from misc import Power + +class CheckNetwork(): + def __init__(self, proj_name, proj_version): + self.PROJECT_NAME = proj_name + self.PROJECT_VERSION = proj_version + self.FIRMWARE_VERSION = modem.getDevFwVersion() + # self.POWERON_REASON = Power.powerOnReason() + + def poweron_print_once(self): + sim_sta = sim.getStatus() + print('==================================================') + print('PROJECT_NAME : {}'.format(self.PROJECT_NAME)) + print('PROJECT_VERSION : {}'.format(self.PROJECT_VERSION)) + print('FIRMWARE_VERSION : {}'.format(self.FIRMWARE_VERSION)) + # print('POWERON_REASON : {}'.format(self.POWERON_REASON)) + print('SIM_CARD_STATUS : {}'.format(sim_sta)) + print('==================================================') + + @staticmethod + def check_datacall_status(): + retval = dial.getPdpRange() + min = retval[0] + max = retval[1] + for pdp in range(min, max+1): + datacall_sta = dataCall.getInfo(pdp, 2) + if (datacall_sta != -1) and ((datacall_sta[2][0] == 1) or (datacall_sta[3][0] == 1)): + return 1 + elif (datacall_sta != -1) and (datacall_sta[2][0] == 0) and (datacall_sta[3][0] == 0): + continue + return 0 + + ''' + return format: stagecode, subcode, time + stagecode - + 1:Currently in the stage of getting SIM card state + 2:Currently in the stage of getting network state + 3:Currently in the stage of getting datacall state + subcode - + when stagecode = 1, subcode represents the state of the SIM card + when stagecode = 2, subcode represents the state of the network + when stagecode = 3, subcode represents the state of the datacall + ''' + def wait_network_connected(self, timeout_s=60): + if (timeout_s < 1) or (timeout_s > 3600): + raise OSError("timeout_s should be in [1, 3600]s!") + timeout_ms = timeout_s * 1000 + + ''' + stage_code = 1 + while True: + sim_sta = sim.getStatus() + if sim_sta == 1: + break + elif sim_sta == 0: + return stage_code, sim_sta + elif (sim_sta == 18) or (sim_sta == 20) or (sim_sta == 21): + utime.sleep_ms(100) + timeout_ms -= 100 + if timeout_ms <= 0: + return stage_code, sim_sta + else: + return stage_code, sim_sta + + stage_code = 2 + while True: + net_sta = net.getState() + if net_sta == -1: + utime.sleep_ms(100) + timeout_ms -= 100 + if timeout_ms <= 0: + return stage_code, -1 + else: + if (net_sta[1][0] == 1) or (net_sta[1][0] == 5): + break + else: + utime.sleep_ms(100) + timeout_ms -= 100 + if timeout_ms <= 0: + return stage_code, net_sta[1][0] + + stage_code = 3 + while True: + datacall_sta = self.check_datacall_status() + if datacall_sta == 1: + return stage_code, datacall_sta + else: + utime.sleep_ms(100) + timeout_ms -= 100 + if timeout_ms <= 0: + return stage_code, datacall_sta + ''' + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 1 + while True: + sim_sta = sim.getStatus() + if sim_sta == 1: + break + else: + utime.sleep_ms(100) + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, sim_sta + + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 2 + while True: + net_sta = net.getState() + if net_sta == -1: + utime.sleep_ms(100) + #timeout_ms -= 100 + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, -1 + else: + if (net_sta[1][0] == 1) or (net_sta[1][0] == 5): + break + else: + utime.sleep_ms(100) + #timeout_ms -= 100 + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, net_sta[1][0] + + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 3 + while True: + datacall_sta = self.check_datacall_status() + if datacall_sta == 1: + return stage_code, datacall_sta + else: + utime.sleep_ms(100) + #timeout_ms -= 100 + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, datacall_sta + + +def wait_network_connected(timeout_s=60): + if (timeout_s < 1) or (timeout_s > 3600): + raise OSError("timeout_s should be in [1, 3600]s!") + timeout_ms = timeout_s * 1000 + + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 1 + while True: + sim_sta = sim.getStatus() + if sim_sta == 1: + break + else: + utime.sleep_ms(100) + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, sim_sta + + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 2 + while True: + net_sta = net.getState() + if net_sta == -1: + utime.sleep_ms(100) + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, -1 + else: + if (net_sta[1][0] == 1) or (net_sta[1][0] == 5): + break + else: + utime.sleep_ms(100) + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, net_sta[1][0] + + current_time = utime.ticks_ms() + last_time = current_time + stage_code = 3 + while True: + datacall_sta = CheckNetwork.check_datacall_status() + if datacall_sta == 1: + return stage_code, datacall_sta + else: + utime.sleep_ms(100) + current_time = utime.ticks_ms() + timeout_ms -= utime.ticks_diff(current_time, last_time) + last_time = current_time + if timeout_ms <= 0: + return stage_code, datacall_sta diff --git a/ports/quectel/modules/checksum.py b/ports/quectel/modules/checksum.py new file mode 100644 index 0000000000000..9478cecb26235 --- /dev/null +++ b/ports/quectel/modules/checksum.py @@ -0,0 +1,157 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# !python3 +# -*- coding:utf-8 -*- + +import ujson +import uos +import file_crc32 +import ql_fs + +checksum_file = '/usr/checksum.json' +checksum_file_max_size = 16384 +backup_checksum_file = '/bak/checksum.json' + + +class _checkError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + + +def check(): + if not ql_fs.path_exists(checksum_file): + if not ql_fs.path_exists(backup_checksum_file): + raise _checkError('%s not exist' % checksum_file) + else: + ql_fs.mkdirs(ql_fs.path_dirname(checksum_file)) + ql_fs.file_copy(checksum_file, backup_checksum_file) + + fp = open(checksum_file, 'rt+') + fp.seek(0) + fr = fp.read(checksum_file_max_size) + fp.close() + if not fr: + raise _checkError('%s is empty' % checksum_file) + return fr + + +def retrieve(file_name): + # Pawn 2021-01-14 for JIRA STASR3601-2428 begin + if not file_name.startswith("/"): + file_name = "/" + file_name + # Pawn 2021-01-14 for JIRA STASR3601-2428 end + + json_str = check() + checksum = ujson.loads(json_str) + for item in checksum: + if item['name'].lower() == file_name.lower(): + return item['crc32'] + return None + + +def _flush_checksum(checksum=[], file_name=checksum_file): + json_str = ujson.dumps(checksum) + fp = open(file_name, 'wt+') + fp.seek(0) + wl = fp.write(json_str) + fp.close() + if wl > 0: + return checksum + return None + + +def bak_update(file_name): + # add by Jaxsen xu + checksum = ql_fs.read_json(backup_checksum_file) + + if not file_name.startswith("/"): + file_name = "/" + file_name + + src_file = file_name[file_name.find("/", 1):] + exist_file = False + for item in checksum: + if item['name'].lower() == src_file.lower(): + exist_file = True + file_crc32_value = file_crc32.calc(file_name) + item['crc32'] = file_crc32_value + if not exist_file: + checksum.append(dict(name=src_file.lower(), crc32=file_crc32.calc(file_name))) + return _flush_checksum(checksum, file_name=file_name[:file_name.find("/", 1)] + "/checksum.json") + + +def usr_update(file_name): + try: + data = ql_fs.read_json(checksum_file) + if data is not None: + exist_file = False + for item in data: + if item['name'].lower() == file_name.lower(): + exist_file = True + item['crc32'] = file_crc32.calc(file_name) + if not exist_file: + data.append(dict(name=file_name.lower(), crc32=file_crc32.calc(file_name))) + return ql_fs.touch(checksum_file, data) + return None + except Exception as e: + return None + + +def update(file_name): + try: + json_str = check() + checksum = ujson.loads(json_str) + + # Pawn 2021-01-14 for JIRA STASR3601-2428 begin + if not file_name.startswith("/"): + file_name = "/" + file_name + # Pawn 2021-01-14 for JIRA STASR3601-2428 end + + for item in checksum: + if item['name'].lower() == file_name.lower(): + file_crc32_value = file_crc32.calc(file_name) + if file_crc32_value: + item['crc32'] = file_crc32_value + return _flush_checksum(checksum) + return None + except Exception: + return None + + +def bulk_update(file_name_list=[]): + try: + json_str = check() + checksum = ujson.loads(json_str) + need_update = 0 + + for file_name in file_name_list: + # Pawn 2021-01-14 for JIRA STASR3601-2428 begin + if not file_name.startswith("/"): + file_name = "/" + file_name + # Pawn 2021-01-14 for JIRA STASR3601-2428 end + for item in checksum: + if item['name'].lower() == file_name.lower(): + file_crc32_value = file_crc32.calc(file_name) + if file_crc32_value: + item['crc32'] = file_crc32_value + need_update = 1 + break + if need_update: + return _flush_checksum(checksum) + return None + except Exception: + return None diff --git a/ports/quectel/modules/dataCall.py b/ports/quectel/modules/dataCall.py new file mode 100644 index 0000000000000..16ea6c6fa88e8 --- /dev/null +++ b/ports/quectel/modules/dataCall.py @@ -0,0 +1,284 @@ +# -*- coding:utf-8 -*- +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +jayceon 2021/03/23 +""" + +import dial + +def setAutoActivate(profileID, enable,cur_simid=None): + + """ + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + if enable != 0 and enable != 1: + raise ValueError("invalid value, enable should be 0 or 1.") + if cur_simid != None and cur_simid != 0 and cur_simid != 1: + raise ValueError("invalid value, enable should be 0 or 1.") + + if "datacall_config.json" in uos.listdir('/usr'): + with open("/usr/datacall_config.json", "r", encoding='utf-8') as fd: + try: + datacall_config = ujson.load(fd) + if not isinstance(datacall_config, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + + value_of_key_profileid = datacall_config.get(str(profileID), None) + if value_of_key_profileid is not None: + if not isinstance(value_of_key_profileid, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + if cur_simid ==1: + datacall_config[str(profileID)]["autoActivate_1"] = enable + else: + datacall_config[str(profileID)]["autoActivate"] = enable + # value_of_key_autoConnect = value_of_key_profileid.get("autoConnect", 0) + # datacall_config[str(profileID)] = {"autoActivate": enable, "autoConnect": value_of_key_autoConnect} + else: + # datacall_config[str(profileID)] = {"autoActivate": enable, "autoConnect": 0} + if cur_simid ==1: + datacall_config[str(profileID)] = {"autoActivate": 0, "autoConnect": 0,"autoActivate_1": enable} + else: + datacall_config[str(profileID)] = {"autoActivate": enable,"autoConnect": 0} + except Exception: + raise ValueError("The format of the datacall_config.json is incorrect.") + with open("/usr/datacall_config.json", "w", encoding='utf-8') as fd: + datacall_config_json = ujson.dumps(datacall_config) + fd.write(datacall_config_json) + else: + # print('[Warning]The datacall_config.json file does not exist, create it now.') + default_config = { + "1": {"autoActivate": 0, "autoConnect": 0}, + "2": {"autoActivate": 0, "autoConnect": 0}, + "3": {"autoActivate": 0, "autoConnect": 0} + } + if cur_simid ==1: + default_config[str(profileID)]["autoActivate_1"] = enable + else: + default_config[str(profileID)]["autoActivate"] = enable + with open("/usr/datacall_config.json", "w", encoding='utf-8') as fd: + default_config_json = ujson.dumps(default_config) + fd.write(default_config_json) + """ + return -1 + + +def setAutoConnect(profileID, enable, cur_simid=None): + """ + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + if enable != 0 and enable != 1: + raise ValueError("invalid value, enable should be 0 or 1.") + if cur_simid != None and cur_simid != 0 and cur_simid != 1: + raise ValueError("invalid value, enable should be 0 or 1.") + + if "datacall_config.json" in uos.listdir('/usr'): + with open("/usr/datacall_config.json", "r", encoding='utf-8') as fd: + try: + datacall_config = ujson.load(fd) + if not isinstance(datacall_config, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + value_of_key_profileid = datacall_config.get(str(profileID), None) + if value_of_key_profileid is not None: + if not isinstance(value_of_key_profileid, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + if cur_simid ==1: + datacall_config[str(profileID)]["autoConnect_1"] = enable + else: + datacall_config[str(profileID)]["autoConnect"] = enable + else: + if cur_simid ==1: + datacall_config[str(profileID)] = {"autoActivate": 0, "autoConnect": 0,"autoConnect_1": enable} + else: + datacall_config[str(profileID)] = {"autoActivate": 0,"autoConnect": enable} + except Exception: + raise ValueError("The format of the datacall_config.json is incorrect.") + with open("/usr/datacall_config.json", "w", encoding='utf-8') as fd: + datacall_config_json = ujson.dumps(datacall_config) + fd.write(datacall_config_json) + else: + # print('[Warning]The datacall_config.json file does not exist, create it now.') + default_config = { + "1": {"autoActivate": 0, "autoConnect": 0}, + "2": {"autoActivate": 0, "autoConnect": 0}, + "3": {"autoActivate": 0, "autoConnect": 0} + } + if cur_simid ==1: + default_config[str(profileID)]["autoConnect_1"] = enable + else: + default_config[str(profileID)]["autoConnect"] = enable + with open("/usr/datacall_config.json", "w", encoding='utf-8') as fd: + default_config_json = ujson.dumps(default_config) + fd.write(default_config_json) + """ + if cur_simid != None: + dial.setAutoConnect(profileID, enable,cur_simid) + else: + dial.setAutoConnect(profileID, enable) + + + +def setPDPContext(profileID, ipType, apn, username, password, authType, cur_simid=None): + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + if authType < 0 or authType >3: + raise ValueError("invalid value.") + # Users can set apn, username, and password to None. + # When it is set to None, it means that the original values will not be changed and the original values will be used instead. + if apn is None or username is None or password is None: + pdp_context = dial.getPDPContext(profileID) + if not isinstance(pdp_context,tuple) or len(pdp_context) < 3: + raise ValueError("getPDPContext error.") + if apn is None: + apn = pdp_context[1] + if username is None: + username = pdp_context[2] + if password is None: + password = pdp_context[3] + # if len(username) == 0 and len(password) == 0 and authType != 0: + # authType = 0 + if (len(username) != 0 or len(password) != 0) and authType == 0: + authType = 2 + if cur_simid == None: + return dial.setPDPContext(profileID, ipType, apn, username, password, authType) + else: + return dial.setPDPContext(profileID, ipType, apn, username, password, authType, cur_simid) + + +def getPDPContext(profileID, cur_simid=None): + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + if cur_simid == None: + return dial.getPDPContext(profileID) + else: + return dial.getPDPContext(profileID, cur_simid) + + + +def setDNSServer(profileID, simID, priDNS, secDNS): + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + return dial.setDnsserver(profileID, simID, priDNS, secDNS) + + +def activate(profileID, cur_simid=None): + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + """ + if "datacall_config.json" in uos.listdir('/usr'): + with open("/usr/datacall_config.json", "r", encoding='utf-8') as fd: + try: + datacall_config = ujson.load(fd) + if not isinstance(datacall_config, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + value_of_key_profileid = datacall_config.get(str(profileID), None) + if value_of_key_profileid is not None: + if not isinstance(value_of_key_profileid, dict): + raise ValueError("The format of the datacall_config.json is incorrect.") + auto_connect = value_of_key_profileid.get("autoConnect", 0) + dial.setAutoConnect(profileID, auto_connect) + else: + raise ValueError("No configuration information for profileID {}.".format(profileID)) + except Exception: + raise ValueError("The format of the datacall_config.json is incorrect.") + """ + if cur_simid == None: + return dial.start(profileID, 0, "", "", "", 0) + else: + return dial.start(profileID, 0, "", "", "", 0,cur_simid) + + +def deactivate(profileID, cur_simid=None): + if profileID < 1 or profileID > 3: + raise ValueError("invalid value, profileID should be in [1,3].") + + pdpctx = dial.getPDPContext(profileID) + if pdpctx != -1: + iptype = pdpctx[0] + if cur_simid == None: + return dial.stop(profileID, iptype) + else: + return dial.stop(profileID, iptype,cur_simid) + else: + return -1 + +###################################################################################### + +def start(profileidx, iptype=0, apn="", username="", password="", authtype=0, cur_simid=None): + ret = dial.setPDPContext(profileidx, iptype, apn, username, password, authtype) + if ret == 0: + if cur_simid == None: + return dial.start(profileidx, iptype, apn, username, password, authtype) + else: + return dial.start(profileidx, iptype, apn, username, password, authtype, cur_simid) + else: + return -1 + + +def stop(profileidx, iptype,cur_simid=None): + if cur_simid == None: + return dial.stop(profileidx, iptype) + else: + return dial.stop(profileidx, iptype, cur_simid) + + +def getInfo(profileidx, iptype, cur_simid=None): + if cur_simid == None: + ret = dial.getInfo(profileidx, iptype) + else: + ret = dial.getInfo(profileidx, iptype, cur_simid) + if ret == -1: + ipv4 = [0, 0, '0.0.0.0', '0.0.0.0', '0.0.0.0'] + ipv6 = [0, 0, '::', '::', '::'] + if iptype ==2: + return (profileidx, iptype, ipv4, ipv6) + elif iptype == 1: + return (profileidx, iptype, ipv6) + elif iptype == 0: + return (profileidx, iptype, ipv4) + return ret + +def getAddressinfo(profileidx, iptype): + ret = dial.getAddressinfo(profileidx, iptype) + if ret == -1: + ipv4 = ['00-00-00-00-00-00', '0.0.0.0', '0.0.0.0'] + ipv6 = ['00-00-00-00-00-00', '::', '::'] + if iptype ==2: + return (ipv4, ipv6) + elif iptype == 1: + return (ipv6) + elif iptype == 0: + return (ipv4) + return ret + +def getSiminfo(simID): + ret = dial.getSiminfo(simID) + if ret == -1: + return (0, 0, 0, 0) + return ret + +def setAsynMode(mode): + return dial.setAsynMode(mode) + +def setCallback(usrfun): + return dial.setCallback(usrfun) + +def setDnsserver(profileidx, simid, new_pri, new_sec): + return dial.setDnsserver(profileidx, simid, new_pri, new_sec) + +def getSpeed(): + return dial.getSpeed() + diff --git a/ports/quectel/modules/file_crc32.py b/ports/quectel/modules/file_crc32.py new file mode 100644 index 0000000000000..7e64adc90e152 --- /dev/null +++ b/ports/quectel/modules/file_crc32.py @@ -0,0 +1,45 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from utils import crc32 + +READ_BLOCK_SIZE = 4096 + +def _check(fp): + fp.seek(0) + fr = fp.read(READ_BLOCK_SIZE) + while fr: + yield fr + fr = fp.read(READ_BLOCK_SIZE) + else: + fp.seek(0) + +def calc(file_name): + csumFunc = crc32() + csum = 0xffffffff + flag = 0 + try: + with open(file_name, 'rb') as fp: + for fr in _check(fp): + csum = csumFunc.update(csum, fr) + flag = 1 + fp.close() + except Exception as e: + pass + finally: + if not flag: + return None + return hex(csum) + diff --git a/ports/quectel/modules/log.py b/ports/quectel/modules/log.py new file mode 100644 index 0000000000000..6a64d1fe3998e --- /dev/null +++ b/ports/quectel/modules/log.py @@ -0,0 +1,119 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import usys + +CRITICAL = 50 +ERROR = 40 +WARNING = 30 +INFO = 20 +DEBUG = 10 +NOTSET = 0 + +_level_dict = { + CRITICAL: "CRIT", + ERROR: "ERROR", + WARNING: "WARN", + INFO: "INFO", + DEBUG: "DEBUG", +} + +_stream = usys.stderr + +class LogRecord: + def __init__(self): + self.__dict__ = {} + + def __getattr__(self, key): + return self.__dict__[key] + +class Logger: + + level = NOTSET + handlers = [] + record = LogRecord() + + def __init__(self, name): + self.name = name + + def _level_str(self, level): + l = _level_dict.get(level) + if l is not None: + return l + return "LVL%s" % level + + def setLevel(self, level): + self.level = level + + def isEnabledFor(self, level): + return level >= (self.level or _level) + + def log(self, level, msg, *args): + if self.isEnabledFor(level): + level = self._level_str(level) + if args: + msg = msg % args + if self.handlers: + d = self.record.__dict__ + d["levelname"] = level + d["message"] = msg + d["name"] = self.name + for h in self.handlers: + h.emit(self.record) + else: + print(level, ":", self.name, ":", msg, sep="", file=_stream) + + def debug(self, msg, *args): + self.log(DEBUG, msg, *args) + + def info(self, msg, *args): + self.log(INFO, msg, *args) + + def warning(self, msg, *args): + self.log(WARNING, msg, *args) + + def error(self, msg, *args): + self.log(ERROR, msg, *args) + + def critical(self, msg, *args): + self.log(CRITICAL, msg, *args) + + +_level = INFO +_loggers = {} + +def getLogger(name="root"): + if name in _loggers: + return _loggers[name] + l = Logger(name) + _loggers[name] = l + return l + +def set_output(out): + global _stream + from machine import UART + if isinstance(out, UART) or out == usys.stderr or out == usys.stdout: + _stream = out + else: + raise Exception("{} must extend UART".format(out)) + +def basicConfig(level=INFO, filename=None, stream=None, format=None): + global _level, _stream + _level = level + if stream: + _stream = stream + if filename is not None: + print("logging.basicConfig: filename arg is not supported") + if format is not None: + print("logging.basicConfig: format arg is not supported") \ No newline at end of file diff --git a/ports/quectel/modules/ql_fs.py b/ports/quectel/modules/ql_fs.py new file mode 100644 index 0000000000000..924cc242fd710 --- /dev/null +++ b/ports/quectel/modules/ql_fs.py @@ -0,0 +1,137 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!python3 +# -*- coding:utf-8 -*- + +import uos +import ujson + + +class FileNotFoundError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + + +def path_exists(path): + if not path: + return False + else: + try: + if uos.stat(path): + return True + else: + return False + except Exception as e: + return False + + +def file_copy(dstFile, srcFile): + if not path_exists(srcFile): + return False + + dstFp = open(dstFile, 'wb+') + srcFp = open(srcFile, 'rb') + srcFr = srcFp.read(4096) + while srcFr: + dstFp.write(srcFr) + srcFr = srcFp.read(4096) + dstFp.close() + srcFp.close() + return True + + +def path_dirname(path): + if not path: + return '' + + pos = path.rfind('/') + if pos < 0: + return '' + if pos == 0: + return '/' + + dirname = '' + for i in range(0, len(path)): + if i == pos: + break + dirname = dirname + path[i] + return dirname + + +def path_getsize(path): + if path_exists(path): + return uos.stat(path)[-4] + else: + raise FileNotFoundError("can not find: '%s'" % path) + + +def mkdirs(dir): + dir_level_list = dir.split('/') + dir_step_up = dir_level_list[0] + for index in enumerate(dir_level_list): + if dir_step_up and (not path_exists(dir_step_up)): + uos.mkdir(dir_step_up) + if index[0] == (len(dir_level_list) - 1): + break + dir_step_up = dir_step_up + '/' + dir_level_list[index[0] + 1] + + +def rmdirs(dir): + ls = uos.listdir(dir) + if not ls: + uos.remove(dir) + else: + for item in ls: + item = dir + '/' + item + if int(uos.stat(item)[0]) & 0x4000: + rmdirs(item) + else: + uos.remove(item) + rmdirs(dir) + + +def touch(file, data, i=1, file_type="json"): + i = file.find("/", i) + if i != -1: + if not path_exists(file[:i]): + uos.mkdir(file[:i]) + return touch(file, data, i=i + 1, file_type=file_type) + else: + try: + with open(file, "w") as f: + if file_type == "json": + f.write(ujson.dumps(data)) + else: + f.write(data) + except Exception as e: + return -1 + else: + return 0 + + +def write_json(file, data): + return touch(file, data) + + +def read_json(file): + if path_exists(file): + with open(file, "r") as f: + return ujson.load(f) + else: + return None + diff --git a/ports/quectel/modules/queue.py b/ports/quectel/modules/queue.py new file mode 100644 index 0000000000000..223bd2e522064 --- /dev/null +++ b/ports/quectel/modules/queue.py @@ -0,0 +1,51 @@ +import _thread + + +class Queue(object): + def __init__(self, maxsize=100): + self.maxsize = maxsize + self.__deque = [] + self.__lock_queue = _thread.allocate_lock() + self.__lock_signal = _thread.allocate_lock() + self.__lock_signal.acquire() + + def put(self, item=None): + self.__lock_queue.acquire() + status = self.__put(item) + self.__lock_queue.release() + if self.__lock_signal.locked(): + self.__lock_signal.release() + return status + + def __pop(self): + self.__lock_queue.acquire() + try: + status = self.__deque.pop(0) + except Exception: + return 0, None + else: + return 1, status + finally: + self.__lock_queue.release() + + def get(self): + if not self.size(): + self.__lock_signal.acquire() + flag, status = self.__pop() + if not flag: + return self.get() + return status + + def __put(self, item): + if self.size() > self.maxsize: + return False + else: + self.__deque.append(item) + return True + + def empty(self): + return not self.size() + + def size(self): + return len(self.__deque) + diff --git a/ports/quectel/modules/request.py b/ports/quectel/modules/request.py new file mode 100644 index 0000000000000..208e3c9fbd83a --- /dev/null +++ b/ports/quectel/modules/request.py @@ -0,0 +1,470 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import usocket +import ujson +import utime +import uio + +import uos +name, platform = uos.uname()[1].split("=",1) + +if platform != "FCM360W" and platform != "FCM362K": + import net + import dataCall + + +def parse_chunked_data(sock): + data = b'' + while True: + chunk_size_str = b'' + while True: + char = sock.read(1) + if char == b'\r': + continue + if char == b'\n': + break + chunk_size_str += char + chunk_size = int(chunk_size_str, 16) + if chunk_size == 0: + break + chunk_data = sock.read(chunk_size) + data += chunk_data + sock.read(2) # discard \r\n + return data + +class Response: + def __init__(self, f, decode=True, sizeof=4096, flag=False): + self.raw = f + self.encoding = "utf-8" + self.decode = decode + self.sizeof = sizeof + self.read_len = 0 + self.read_data = sizeof + self.flag = flag + + def close(self): + if self.raw: + if s_isopen: + self.raw.close() + self.raw = None + if s_isopen: + if self.raw: + self.raw.close() + + @property + def content(self): + global s_isopen + try: + while True: + if self.read_data > 2048: + block = self.raw.read(2048) + self.read_data -= 2048 + else: + block = self.raw.read(self.read_data) + self.read_len += len(block) + if block: + yield block.decode() if self.decode else block + else: + self.raw.close() + s_isopen = False + break + if (self.flag == True) and (self.sizeof == self.read_len): + self.raw.close() + s_isopen = False + break + except Exception as e: + self.raw.close() # 2021-05-27 + s_isopen = False + if "ETIMEDOUT" in str(e): + raise ValueError(str(e)) + else: + pass + return "" + + @property + def text(self): + for i in self.content: + yield str(i) + return "" + + def json(self): + try: + json_str = "" + for i in self.content: + json_str += i + if json_str: + return ujson.loads(json_str) + else: + return None + except Exception as e: + raise ValueError( + "The data for the response cannot be converted to JSON-type data,please try use response.content method") + + +def request(method, url, data=None, json=None, files=None, stream=None, decode=True, sizeof=2048, timeout=20, headers=None, + ssl_params=None, version=0, files_data=None,files_data_len=None,files_name=None, ipvtype=0,username=None,password=None): + global port + global s_isopen + s_isopen = True + port_exist = False + addr = None + URL = url + if ipvtype == 1 and (url.split("[")[0] == "http://" or url.split("[")[0] == "https://"): + try: + proto, dummy, hosts, path = url.split("/", 3) + except ValueError: + proto, dummy, hosts = url.split("/", 2) + path = "" + + left = hosts.find('[') + if left < 0: + raise ValueError("http missing left delimiter:" + url) + + right = hosts.find(']', left + 1) + if right < 0: + raise ValueError("http missing right delimiter:" + url) + + host = hosts[left + 1:right] + + try: + if hosts[right + 1] == ':': + port = int(hosts[right + 2:]) + port_exist = True + except Exception as e: + pass + + if proto == "http:": + if not port_exist: + port = 80 + elif proto == "https:": + if not port_exist: + port = 443 + else: + raise ValueError("Unsupported protocol: " + proto) + + try: + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) + except Exception as e: + raise IndexError("Domain name resolution error, please check network connection:" + host) + + import uos + name, platform = uos.uname()[1].split("=",1) + if platform == "FCM360W" or platform == "FCM362K": + raise ValueError("platform Not Support IPV6!") + for i in ai: + if i[0] == 10: + addr = i[-1] + break + host = hosts + + if addr is None: + raise ValueError("Request DNS Parsing IPV6 Fail") + elif not url.split(".")[0].isdigit(): + if not url.startswith("http"): + url = "http://" + url + try: + proto, dummy, host, path = url.split("/", 3) + except ValueError: + proto, dummy, host = url.split("/", 2) + path = "" + if ":" in host: + url_info = host.split(":") + host = url_info[0] + port = int(url_info[1]) + port_exist = True + # jian.yao 2020-12-09 + if proto == "http:": + if not port_exist: + port = 80 + # jian.yao 2020-12-09 + elif proto == "https:": + if not port_exist: + port = 443 + else: + raise ValueError("Unsupported protocol: " + proto) + + try: + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) + except Exception as e: + raise IndexError("Domain name resolution error, please check network connection") + + if ipvtype == 1: + import uos + name, platform = uos.uname()[1].split("=",1) + if platform == "FCM360W" or platform == "FCM362K": + raise ValueError("platform Not Support IPV6!") + for i in ai: + if i[0] == 10: + addr = i[-1] + break + if addr is None: + raise ValueError("Request DNS Parsing IPV_6 Fail") + else: + for i in ai: + if i[0] == 2: + addr = i[-1] + break + if addr is None: + raise ValueError("Request DNS Parsing IPV_4 Fail") + elif url.split(".")[0].isdigit() and ":" not in url: + raise ValueError( + "MissingSchema: Invalid URL '{}': No schema supplied. Perhaps you meant http://{}? ".format(url, url)) + else: + path = "" + proto = "" + if ":" not in url: + raise ValueError("URL address error: !" + url) + try: + if "/" in url: + ip_info = url.split('/', 1) + path = ip_info[1] + host, port = ip_info[0].split(":") + else: + host, port = url.split(":") + except: + raise ValueError("URL address error: " + url) + try: + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) + except Exception as e: + raise IndexError("Domain name resolution error, please check network connection") + + if ipvtype == 1: + import uos + name, platform = uos.uname()[1].split("=",1) + if platform == "FCM360W" or platform == "FCM362K": + raise ValueError("platform Not Support IPV6!") + for i in ai: + if i[0] == 10: + addr = i[-1] + break + if addr is None: + raise ValueError("Request DNS Parsing IPV6 Fail") + else: + for i in ai: + if i[0] == 2: + addr = i[-1] + break + if addr is None: + raise ValueError("Request DNS Parsing IPV4 Fail") + #global s + + if ipvtype == 1: + import uos + name, platform = uos.uname()[1].split("=",1) + if platform == "FCM360W" or platform == "FCM362K": + raise ValueError("platform Not Support IPV6!") + try: + s = usocket.socket(usocket.AF_INET6, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + except Exception as e: + raise RuntimeError("socket resource malloc FAIL='{}'".format(str(e))) + call_state = dataCall.getInfo(1,1) + if call_state == -1: + raise ValueError("request Connect dataCall get Info FAIL") + elif call_state[2][0] != 1: + raise ValueError("request Connect dataCall get IPV6 Info FAIL") + try: + s.bind((call_state[2][2],0)) + except Exception as e: + raise ValueError("request sock bind IPV6 IP FAIL") + else: + try: + s = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + except Exception as e: + raise RuntimeError("socket resource malloc FAIL='{}'".format(str(e))) + + s.settimeout(timeout) + try: + try: + s.connect(addr) + except Exception as e: + s.close() + raise RuntimeError("HTTP Connection '{}' FAIL='{}'".format(str(e), URL)) + if proto == "https:": + import ussl + try: + if ssl_params: + s = ussl.wrap_socket(s, **ssl_params, server_hostname=host) + else: + s = ussl.wrap_socket(s, server_hostname=host) + except Exception as e: + s.close() + raise RuntimeError("HTTPS USSL '{}' FAIL='{}'".format(str(e), URL)) + if version == 1: + s.write(b"%s /%s HTTP/1.1\r\n" % (method, path)) + else: + s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) + s.write(b"Host: %s\r\n" % host) + if version == 1: + s.write(b"Connection: close\r\n") + if headers: + if files: + boundary = str(utime.time()) + if not (files.get("filepath") and files.get("filename")): + raise ValueError("Missing key parameters 'filepath' and 'filename'") + if headers.get('Content-Type') == "multipart/form-data": + headers['Content-Type'] = headers['Content-Type'] + '; boundary={}'.format(boundary) + headers['charset'] = 'UTF-8' + if files_data: + boundary = str(utime.time()) + if headers.get('Content-Type') == "multipart/form-data": + headers['Content-Type'] = headers['Content-Type'] + '; boundary={}'.format(boundary) + headers['charset'] = 'UTF-8' + else: + headers = dict() + if files: + boundary = str(utime.time()) + headers['Content-Type'] = "multipart/form-data; boundary={}".format(boundary) + headers['charset'] = 'UTF-8' + if json: + headers['Content-Type'] = "application/json" + for k in headers: + s.write(k) + s.write(b": ") + s.write(headers[k]) + s.write(b"\r\n") + if (username is not None) and (password is not None): + try: + import ubinascii + key = "{}:{}".format(username, password) + key_64 = ubinascii.b2a_base64(key)[:-1].decode('utf-8') + s.write(b"Authorization: Basic {}\r\n".format(key_64)) + except Exception as e: + s.close() + raise RuntimeError("HTTP base64 FAIL='{}'".format(str(e))) + if json is not None: + assert data is None + data = ujson.dumps(json) + if data: + s.write(b"Content-Length: %d\r\n" % len(data)) + s.write(b"\r\n") + s.write(data) + if files_data: + if files_name == None: + raise ValueError("files_name must have") + if files_data_len == None: + raise ValueError("files_data_len must have") + import uos + name,platform = uos .uname()[1].split("=",1) + datas = '--{0}{1}Content-Disposition: form-data; name="file"; filename="{2}"{1}Content-Type: application/octet-stream{1}{1}'.format( + boundary, '\r\n', files_name) + suffix = '{1}--{0}--{1}'.format(boundary, '\r\n') + len_d = files_data_len + len(datas) + len(suffix) + s.write(b"Content-Length: %d\r\n" % len_d) + s.write(b"\r\n") + s.write(datas) + s.write(files_data[0:files_data_len]) + s.write(suffix) + if files: + import uos + counter = 0 + name,platform = uos .uname()[1].split("=",1) + file_path = files.get("filepath") + with open(file_path, 'rb') as f: + content = f.read(4096) + if files.get("name") is not None: + datas = '--{0}{1}Content-Disposition: form-data; {2}{1}{1}{3}{1}--{0}'. \ + format(boundary, '\r\n', files.get("name"), content) + else: + datas = '--{0}{1}Content-Disposition: form-data; name="file"; filename="{2}"{1}Content-Type: application/octet-stream{1}{1}'.format( + boundary, '\r\n', files.get("filename")) + if files.get("filepath1") is not None: + with open('{}'.format(files.get("filepath1")), 'r') as f1: + content1 = f1.read() + if files.get("name1") is not None: + datas += '{1}Content-Disposition: form-data; {2}{1}{1}{3}{1}--{0}'. \ + format(boundary, '\r\n', files.get("name1"), content1) + else: + if files.get("filename1") is None: + raise ValueError("Missing key parameters 'filename1' ") + datas += '{1}Content-Disposition: form-data; name="file"; filename="{2}"{1}Content-Type: application/octet-stream{1}{1}{3}{1}--{0}'. \ + format(boundary, '\r\n', files.get("filename1"), content1) + suffix = '{1}--{0}--{1}'.format(boundary, '\r\n') + len_d = uos.stat(file_path)[-4] + len(datas) + len(suffix) + s.write(b"Content-Length: %d\r\n" % len_d) + s.write(b"\r\n") + s.write(datas) + while content: + s.write(content) + content = f.read(4096) + s.write(suffix) + if not (files and data and files_data): + s.write(b"\r\n") + l = s.readline() + uheaders = {} + chunked_encoding = False + try: + # jian.yao 2020-12-09 Abnormal response handle + l = l.split(None, 2) + status = int(l[1]) + except: + raise ValueError("InvalidSchema: No connection adapters were found for '{}'".format(URL)) + reason = "" + if len(l) > 2: + reason = l[2].rstrip() + con_len = sizeof + con_flag = False + while True: + l = s.readline() + j = l.decode().split(":") + try: + uheaders[j[0]] = j[1].replace("\n", "").replace("\r", "") + except Exception as e: + pass + if not l or l == b"\r\n": + break + + if l.lower().startswith(b"transfer-encoding:"): + if b"chunked" in l.lower(): + chunked_encoding = True + if l.startswith(b'Content-Length:'): + con_len = int(l.split(b' ')[1]) + con_flag = True + #if l.startswith(b"Location:") and not 200 <= status <= 299: + #raise NotImplementedError("Redirects not yet supported") + except OSError: + s.close() + raise + if chunked_encoding: + resp = Response(uio.BytesIO(parse_chunked_data(s)), sizeof=con_len) + else: + resp = Response(s,sizeof=con_len, flag=con_flag) + resp.status_code = status + resp.reason = reason + resp.headers = uheaders + return resp + + +def head(url, **kw): + return request("HEAD", url, **kw) + + +def get(url, **kw): + return request("GET", url, **kw) + + +def post(url, **kw): + return request("POST", url, **kw) + + +def put(url, **kw): + return request("PUT", url, **kw) + + +def patch(url, **kw): + return request("PATCH", url, **kw) + + +def delete(url, **kw): + return request("DELETE", url, **kw) diff --git a/ports/quectel/modules/umqtt.py b/ports/quectel/modules/umqtt.py new file mode 100644 index 0000000000000..6daa88dbc133e --- /dev/null +++ b/ports/quectel/modules/umqtt.py @@ -0,0 +1,776 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import uos +name, platform = uos.uname()[1].split("=",1) + +import utime +import log +import _thread +import usocket as socket +import ustruct as struct +import osTimer + +if platform != "FCM360W" and platform != "FCM362K": + import net + import dataCall + +log.basicConfig(level=log.INFO) +mqtt_log = log.getLogger("MQTT") +# bob.xian 2023/9/20 [FCM360W may be a bit unstable when using the log module, so print is temporarily used] begin +if platform == "FCM360W": + mqtt_log.info = print + mqtt_log.warning = print +# bob.xian 2023/9/20 [FCM360W may be a bit unstable when using the log module, so print is temporarily used] end + +#ALIYDEVREGISTER = False +#TOPICLIST = list() +#SERVERACK = set() # PUBACK and SUBACK pids awaiting ACK response +#PING = False + +class MQTTException(Exception): + pass + +def pid_gen(): + pid = 0 + while True: + pid = pid + 1 if pid < 65535 else 1 + yield pid + +class BaseMqtt(object): + CONNECTEXCE = -1 + CONNECTSUCCESS = 0 + ARECONNECT = 1 + SEVCLOSE = 2 + + def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0, + ssl=False, ssl_params={}, reconn=True, version=4, pingmaxnum=0, manage=False, ipvtype=0): + if port == 0: + port = 8883 if ssl else 1883 + self.client_id = client_id + self.sock = None + self.server = server + self.port = port + self.ssl = ssl + self.ssl_params = ssl_params + self.pid = 0 + self.__newpid = pid_gen() + self.__rcv_pids = set() # PUBACK and SUBACK pids awaiting ACK response + self.cb = None + self.user = user + self.pswd = password + self.keepalive = keepalive + self.lw_topic = None + self.lw_msg = None + self.lw_qos = 0 + self.lw_retain = False + self.last_time = None + self.timerFlag = True + self.pingFlag = False + self.connSta = False + self.reconn = reconn + self.topic_list = [] + self.addr = None + self.qos = 0 + self.mqttversion = version + self.clean_session = True + self.ping_outstanding = False + self.pingmaxnum = pingmaxnum + self.pingnum = 0 + self.mqttlock = None + self.mqttsendlock = None + self.mqttmsglock = None + self.threadId = None + self.__response_time = 5000 + self.wait_flag = 0 + self.ipvtype = ipvtype + self.manage = manage + self.looptask_id = None + self.PING = False + self.SERVERACK = set() # PUBACK and SUBACK pids awaiting ACK response + self.TOPICLIST = list() + self.ALIYDEVREGISTER = False + if keepalive != 0 and keepalive < 5: + raise ValueError("inport parameter error : keepalive >= 5S") + + def _send_str(self, s): + self.sock.write(struct.pack("!H", len(s))) + self.sock.write(s) + + def _recv_len(self): + n = 0 + sh = 0 + while 1: + b = self.sock.read(1)[0] + n |= (b & 0x7f) << sh + if not b & 0x80: + return n + sh += 7 + + def set_callback(self, f): + self.cb = f + + def set_last_will(self, topic, msg, retain=False, qos=0): + assert 0 <= qos <= 2 + assert topic + self.lw_topic = topic + self.lw_msg = msg + self.lw_qos = qos + self.lw_retain = retain + + def connect(self, clean_session=True): + if self.mqttlock is None: + self.mqttlock = _thread.allocate_lock() + if self.mqttsendlock is None: + self.mqttsendlock = _thread.allocate_lock() + if self.mqttmsglock is None: + self.mqttmsglock = _thread.allocate_lock() + j = 1 + while True: + if j > 5: + raise ValueError("DNS Parsing NULL") + try: + addrall = socket.getaddrinfo(self.server, self.port) + except Exception as e: + raise ValueError("DNS Parsing '{}'".format(self.server)) + if not addrall: + j += 1 + utime.sleep_ms(500) + continue + if self.ipvtype == 1: + for i in addrall: + if i[0] == 10: + self.addr = i[-1] + break + if self.addr is None: + raise ValueError("DNS Parsing IPV6 Fail") + else: + try: + self.addr = addrall[0][-1] + except Exception as e: + raise ValueError("DNS Parsing IPV4 Fail") + break + + if self.ipvtype == 1: + if platform == "FCM360W" or platform == "FCM362K": + print("Not Support!") + return + self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.TCP_CUSTOMIZE_PORT) + call_state = dataCall.getInfo(1,1) + if call_state == -1: + raise ValueError("MQTT Connect dataCall get Info FAIL") + elif call_state[2][0] != 1: + raise ValueError("MQTT Connect dataCall get IPV6 Info FAIL") + try: + self.sock.bind((call_state[2][2],0)) + except Exception as e: + raise ValueError("sock bind IPV6 IP FAIL") + else: + self.sock = socket.socket() + self.sock.settimeout(20) + self.sock.connect(self.addr) + if self.ssl: + import ussl + self.sock = ussl.wrap_socket(self.sock, **self.ssl_params, server_hostname = self.server) + + if self.mqttversion == 3: + protocol = b"MQIsdp" + else: + protocol = b"MQTT" + + connect_flags = 0 + connect_flags = clean_session << 1 + remaining_length = 2 + len(protocol) + 1 + 1 + 2 + + if self.client_id: + remaining_length = remaining_length + 2 + len(self.client_id) + if self.user: + remaining_length = remaining_length + 2 + len(self.user) + connect_flags = connect_flags | 0x80 + if self.pswd: + connect_flags = connect_flags | 0x40 + remaining_length = remaining_length + 2 + len(self.pswd) + if self.lw_topic: + connect_flags = connect_flags | 0x04 | ((self.lw_qos & 0x03) << 3) | ((self.lw_retain & 0x01) << 5) + remaining_length += 2 + len(self.lw_topic) + 2 + len(self.lw_msg) + + command = 0x10 + packet = bytearray() + packet.extend(struct.pack("!B", command)) + remaining_bytes = [] + while True: + byte = remaining_length % 128 + remaining_length = remaining_length // 128 + if remaining_length > 0: + byte = byte | 0x80 + + remaining_bytes.append(byte) + packet.extend(struct.pack("!B", byte)) + if remaining_length == 0: + break + + if self.mqttversion == 3: + packet.extend(struct.pack("!H" + str(len(protocol)) + "sBBH", len(protocol), protocol, 3, connect_flags, + self.keepalive)) + else: + packet.extend(struct.pack("!H" + str(len(protocol)) + "sBBH", len(protocol), protocol, 4, connect_flags, + self.keepalive)) + if self.client_id: + packet.extend(struct.pack("!H" + str(len(self.client_id)) + "s", len(self.client_id), self.client_id)) + if self.lw_topic: + packet.extend(struct.pack("!H" + str(len(self.lw_topic)) + "s", len(self.lw_topic), self.lw_topic)) + packet.extend(struct.pack("!H" + str(len(self.lw_msg)) + "s", len(self.lw_msg), self.lw_msg)) + if self.user: + packet.extend(struct.pack("!H" + str(len(self.user)) + "s", len(self.user), self.user)) + if self.pswd: + packet.extend(struct.pack("!H" + str(len(self.pswd)) + "s", len(self.pswd), self.pswd)) + + self.sock.write(packet) + + resp = self.sock.read(4) + self.sock.setblocking(True) + assert resp[0] == 0x20 and resp[1] == 0x02 + if resp[3] != 0: + raise MQTTException(resp[3]) + # self.last_time = utime.mktime(utime.localtime()) + self.last_time = utime.ticks_ms() // 1000 + self.connSta = True + self.clean_session = clean_session + self.PING = False + self.pingnum = self.pingmaxnum + self.ALIYDEVREGISTER = self.sock + return resp[2] & 1 + + def disconnect(self): + # Pawn.zhou 2021/1/12 for JIRA STASR3601-2523 begin + try: + self.timerFlag = False + self.pingFlag = False + self.connSta = False + self.PING = False + self.pingnum = self.pingmaxnum + self.topic = [] + self.sock.write(b"\xe0\0") + # Pawn.zhou 2021/1/12 for JIRA STASR3601-2523 end + except: + mqtt_log.warning("Error send mqtt_dis FAIL.") + try: + if self.mqttsendlock.locked(): + self.mqttsendlock.release() + if self.mqttlock.locked(): + self.mqttlock.release() + if self.mqttmsglock.locked(): + self.mqttmsglock.release() + if self.mqttlock is not None: + _thread.delete_lock(self.mqttlock) + self.mqttlock = None + if self.mqttsendlock is not None: + _thread.delete_lock(self.mqttsendlock) + self.mqttsendlock = None + if self.mqttmsglock is not None: + _thread.delete_lock(self.mqttmsglock) + self.mqttmsglock = None + except: + mqtt_log.warning("Error delete_lock FAIL.") + utime.sleep_ms(500) + try: + self.sock.close() + except: + mqtt_log.warning("Error socket close FAIL.") + + def close(self): + self.sock.close() + + def ping(self): + # self.last_time = utime.mktime(utime.localtime()) + self.last_time = utime.ticks_ms() // 1000 + self.sock.write(b"\xc0\0") + self.PING = True + + def publish(self, topic, msg, retain=False, qos=0): + pkt = bytearray(b"\x30\0\0\0") + pid = next(self.__newpid) + pkt[0] |= qos << 1 | retain + sz = 2 + len(topic) + len(msg) + if qos > 0: + sz += 2 + assert sz < 2097152 + i = 1 + while sz > 0x7f: + pkt[i] = (sz & 0x7f) | 0x80 + sz >>= 7 + i += 1 + pkt[i] = sz + self.sock.write(pkt, i + 1) + self._send_str(topic) + if qos > 0: + self.SERVERACK.add(pid) + struct.pack_into("!H", pkt, 0, pid) + self.sock.write(pkt, 2) + self.sock.write(msg) + # self.last_time = utime.mktime(utime.localtime()) + if qos == 0: + if self.keepalive >= 30: + # time = utime.mktime(utime.localtime()) + time = utime.ticks_ms() // 1000 + if (time - self.last_time) > (self.keepalive - 15): + self.last_time = time - self.keepalive + return True + + if qos == 1: + if self.wait_flag == 0: + self.wait_msg() + if not self._await_pid(pid): + mqtt_log.warning("publish Pid[%s] QOS1 message was not received correctly"%pid) + return False + else: + # self.last_time = utime.mktime(utime.localtime()) + self.last_time = utime.ticks_ms() // 1000 + return True + elif qos == 2: + mqtt_log.warning("publish QOS2 Not Support") + assert 0 + + def __subscribe(self, topic, qos, sock): + if sock: + self.sock = sock + pkt = bytearray(b"\x82\0\0\0") + pid = next(self.__newpid) + self.SERVERACK.add(pid) + struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, pid) + self.sock.write(pkt) + self._send_str(topic) + self.sock.write(qos.to_bytes(1, "little")) + if self.ssl == False: + self.sock.settimeout(5) + while True: + if self.wait_flag == 0: + if self.mqttmsglock.locked(): + return True + op = self.wait_msg() + if op == 0x90: + return True + else: + return False + else: + if not self._await_pid(pid): + return False + else: + return True + + def subscribe(self, topic, qos=0, sock=None): + assert self.cb is not None, "Subscribe callback is not set" + if topic not in self.topic_list: + self.topic_list.append(topic) + self.TOPICLIST = self.topic_list + self.qos = qos + self.__subscribe(topic, qos=qos, sock=sock) + + def publish_ack(self, pid): + pkt = bytearray(b"\x40\x02\0\0") + struct.pack_into("!H", pkt, 2, pid) + while True: + try: + if self.mqttsendlock.locked(): + utime.sleep_ms(10) + continue + self.mqttsendlock.acquire() + self.sock.write(pkt) + self.mqttsendlock.release() + return + except Exception as e: + mqtt_log.warning("Publish Pid[%s] ACK OSError[%s]." %(pid, str(e))) + if self.mqttsendlock.locked(): + self.mqttsendlock.release() + return + + # Wait for a single incoming MQTT message and process it. + # Subscribed messages are delivered to a callback previously + # set by .set_callback() method. Other (internal) MQTT + # messages processed internally. + def wait_msg(self): + global SUBACK + SUBACK = True + if self.ssl: + res = self.sock.read(1) + else: + res = self.sock.recv(1) + self.sock.setblocking(True) + if res is None: + return None + if res == b"": + # raise OSError(-1) + # Pawn 2020/11/14 - WFZT mqttBUg -1 + return None + + if res == b"\xd0": # PINGRESP + self.PING = False + sz = self.sock.read(1)[0] + assert sz == 0 + return res + + if res == b'\x40': # PUBACK: save pid + sz = self.sock.read(1) + if sz != b"\x02": + mqtt_log.warning("Publish message does not return ACK correctly") + return + rcv_pid = self.sock.read(2) + pid = rcv_pid[0] << 8 | rcv_pid[1] + if pid in self.SERVERACK: + self.SERVERACK.discard(pid) + return + else: + return + + sub_ack = res[0] + if (sub_ack & 0xf0) == 0x90: # SUBSCRIBE: + resp = self.sock.read(4) + pid = resp[1] << 8 | resp[2] + if pid in self.SERVERACK: + self.SERVERACK.discard(pid) + if resp[3] == 0x80: + mqtt_log.warning("subscribe topic ACK Fail") + return 0x80 + else: + return 0x90 + + op = res[0] + if op & 0xf0 != 0x30: + return op + sz = self._recv_len() + topic_len = self.sock.read(2) + topic_len = (topic_len[0] << 8) | topic_len[1] + topic = self.sock.read(topic_len) + sz -= topic_len + 2 + if op & 6: + pid = self.sock.read(2) + pid = pid[0] << 8 | pid[1] + sz -= 2 + msg = self.sock.read(sz) + # self.last_time = utime.mktime(utime.localtime()) + if op & 6 == 2: + task_stacksize = _thread.stack_size() + name,platform = uos.uname()[1].split("=",1) + if platform == "EC600E" or platform == "EC800E" or platform == "FCM360W" or platform == "FCM362K": + _thread.stack_size(2*1024) + _thread.start_new_thread(self.publish_ack, (pid,)) + _thread.stack_size(task_stacksize) + if op & 6 == 4: + assert 0 + try: + self.cb(topic, msg) + except Exception as e: + mqtt_log.warning("set_callback OSError[%s]." % str(e)) + + # Checks whether a pending message from server is available. + # If not, returns immediately with None. Otherwise, does + # the same processing as wait_msg. + def check_msg(self): + self.sock.setblocking(False) + return self.wait_msg() + + def _timeout(self, t): + return utime.ticks_diff(utime.ticks_ms(), t) > self.__response_time + + def _await_pid(self, pid): + t = utime.ticks_ms() + while pid in self.SERVERACK: # local copy + if self._timeout(t): + break # Must repub or bail out + utime.sleep_ms(500) + else: + return True # PID received. All done. + return False + + def get_mqttsta(self): + ''' + Get the MQTT connection status + CONNECTEXCE -1:Connect the interrupt + CONNECTSUCCESS 0:connection is successful + ARECONNECT 1:In the connection + SEVCLOSE 2:server closes the connection + ''' + socket_sta = self.sock.getsocketsta() + if socket_sta == 0: + return self.ARECONNECT + elif (socket_sta == 2) or (socket_sta == 3): + return self.ARECONNECT + elif (socket_sta == 4) and self.connSta: + return self.CONNECTSUCCESS + elif (socket_sta == 7) or (socket_sta == 8): + return self.SEVCLOSE + else: + return self.CONNECTEXCE + + +class MQTTClient(BaseMqtt): + DELAY = 2 + DEBUG = False + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.error_callback = None + self.subtimer = osTimer() + + def delay(self, i): + utime.sleep(self.DELAY) + + def disconnect(self): + try: + self.subtimer.stop() + self.subtimer.delete_timer() + except: + pass + super().disconnect() + + def sub_timer_task(self, arg): + if not self.topic_list: + self.topic_list = self.TOPICLIST + for topic_re in self.topic_list: + super().subscribe(topic_re, qos=self.qos, sock=self.ALIYDEVREGISTER) + + def error_register_cb(self, func): + self.error_callback = func + + def base_reconnect(self): + i = 0 + if self.mqttlock.locked(): + return + self.mqttlock.acquire() + if self.looptask_id is not None and self.PING != True: + if self.mqttsendlock.locked(): + self.mqttsendlock.release() + if self.wait_flag == 0: + _thread.stop_thread(self.looptask_id) + self.looptask_id = None + if self.reconn: + if self.error_callback is not None: + e = "reconnect_start" + self.error_callback(e)#start reconnect + while 1: + try: + if not self.timerFlag: + break + #felix.hou 2024.5.11 add ,close() may cause reconnecting to the mqtt server to fail + if platform == "FCM360W" or platform == "FCM362K": + self.sock.shutdown() + utime.sleep_ms(20) + self.sock.close() + else: + self.sock.close() + #felix.hou 2024.5.11 add ,close() may cause reconnecting to the mqtt server to fail + time_info = self.logTime() + mqtt_log.info( + "[%s] The network condition has been restored and an attempt will be made to reconnect" % time_info) + self.pingFlag = False + self.connSta = False + self.connect(self.clean_session) + mqtt_log.info("[%s] Reconnection successful!" % time_info) + if self.manage: + self.subtimer.start(3000, 0, self.sub_timer_task) + else: + if not self.topic_list: + self.topic_list = self.TOPICLIST + for topic_re in self.topic_list: + super().subscribe(topic_re, self.qos) + time_info = self.logTime() + if self.mqttlock.locked(): + self.mqttlock.release() + if self.reconn: + if self.error_callback is not None: + e = "reconnect_success" + try: + self.error_callback(e)#reconnect success + except Exception as e: + mqtt_log.warning("error_callback OSError[%s]." % str(e)) + return + except Exception as e: + if not self.timerFlag: + break + i += 1 + time_info = self.logTime() + mqtt_log.warning( + "[%s] The connection attempt failed [%s] and will be tried again after %d seconds." % (time_info, str(e), 5 + i)) + utime.sleep(5 + i) + if platform != "FCM360W" and platform != "FCM362K": + net_sta = net.getState() + if net_sta != -1 and ((net_sta[1][0] == 1) or (net_sta[1][0] == 5)): + call_state = dataCall.getInfo(1, 0) + if call_state == -1: + mqtt_log.info("LTE datacall Fail.") + elif call_state[2][0] != 1: + mqtt_log.info("LTE datacall IPV4 Fail.") + else: + mqtt_log.info("LTE Net unregistered.") + + def publish(self, topic, msg, retain=False, qos=0): + while True: + try: + if self.mqttsendlock.locked(): + utime.sleep_ms(10) + continue + self.mqttsendlock.acquire() + ret = super().publish(topic, msg, retain, qos) + self.mqttsendlock.release() + return ret + except Exception as e: + mqtt_log.warning("Publish Fail OSError[%s]." % str(e)) + if self.mqttsendlock.locked(): + self.mqttsendlock.release() + return False + + def wait_msg(self): + while True: + try: + # The state changes when disconnect is called + if not self.timerFlag: + break + self.wait_flag = 1 + if self.mqttmsglock.locked(): + utime.sleep_ms(10) + continue + self.mqttmsglock.acquire() + ret = super().wait_msg() + self.mqttmsglock.release() + self.wait_flag = 0 + return ret + except Exception as e: + if self.mqttmsglock is not None and self.mqttmsglock.locked(): + self.mqttmsglock.release() + if not self.timerFlag: + break + if not self.reconn: + raise e + # Whether to use the built-in reconnect mechanism + time_info = self.logTime() + self.wait_flag = 0 + if self.ping == True: + mqtt_log.warning("[%s] wait msg, send ping timeout. Trying to reconnect" % time_info) + else: + mqtt_log.warning("[%s] wait msg OSError[%s] . Trying to reconnect" % (time_info, str(e))) + utime.sleep(1) + self.base_reconnect() + + def connect(self, clean_session=True): + try: + super().connect(clean_session) + except Exception as e: + if str(e) == "104": + raise ValueError( + "MQTT Connect Error='{}' Server Actively RST Disconnected.Need Check addr&port".format(e)) + elif str(e) == "107" or str(e) == "4": + raise ValueError( + "MQTT Connect Error='{}' Server Actively FIN Disconnected.Need Check Connection parameters".format( + e)) + elif str(e) == "110": + raise ValueError("MQTT Connect Error='{}' Timeout.Need Check Network".format(e)) + raise ValueError("MQTT Connect error='{}' FAIL".format(e)) + if self.looptask_id is None: + if self.keepalive > 0 and not self.pingFlag: + # Pawn.zhou 2021/1/12 for JIRA STASR3601-2523 begin + task_stacksize = _thread.stack_size() + name,platform = uos.uname()[1].split("=",1) + if platform == "EC600E" or platform == "EC800E": + _thread.stack_size(8 * 1024) + elif platform == "FCM360W" or platform == "FCM362K": + _thread.stack_size(2 * 1024) + else: + _thread.stack_size(16 * 1024) + self.looptask_id = _thread.start_new_thread(self.__loop_forever, ()) + _thread.stack_size(task_stacksize) + self.pingFlag = True + # Pawn.zhou 2021/1/12 for JIRA STASR3601-2523 end + return 0 + + def __loop_forever(self): + while True: + if self.keepalive >= 5: + keepalive = self.keepalive - 3 + try: + if not self.timerFlag: + if self.mqttsendlock is not None and self.mqttsendlock.locked(): + self.mqttsendlock.release() + self.looptask_id = None + break + # time = utime.mktime(utime.localtime()) + time = utime.ticks_ms() // 1000 + + if time - self.last_time > keepalive: + if self.mqttlock.locked(): + utime.sleep(5) + continue + if self.PING == True: + mqtt_log.warning("[%s] Send ping timeout 2. Trying to reconnect" % time) + if not self.reconn: + if self.mqttsendlock is not None and self.mqttsendlock.locked(): + self.mqttsendlock.release() + self.looptask_id = None + raise Exception("Send ping timeout.") + else: + if self.pingnum <= 0: + mqtt_log.warning("[%s] Send ping timeout 1. Trying to reconnect" % time) + self.sock.settimeout(3) + self.pingFlag = False + if self.mqttsendlock is not None and self.mqttsendlock.locked(): + self.mqttsendlock.release() + self.looptask_id = None + break + else: + mqtt_log.warning("[%s] Trying to resend ping(%d)" % (time, self.pingnum)) + self.pingnum = self.pingnum - 1 + while True: + if self.mqttsendlock is not None and self.mqttsendlock.locked(): + utime.sleep_ms(10) + continue + self.mqttsendlock.acquire() + super().ping() + self.mqttsendlock.release() + utime.sleep(5) + break + else: + utime.sleep(5) + continue + except Exception as e: + if self.mqttsendlock is not None and self.mqttsendlock.locked(): + self.mqttsendlock.release() + if not self.timerFlag: + self.looptask_id = None + break + # Network normal, take the initiative to throw exception + if not self.reconn: + if self.error_callback is not None: + self.error_callback(str(e)) + self.pingFlag = False + self.looptask_id = None + break + else: + self.pingFlag = False + self.looptask_id = None + break + # Whether to use the built-in reconnect mechanism + time_info = self.logTime() + if self.PING == True: + mqtt_log.warning("[%s] Send ping timeout 3. Trying to reconnect" % time_info) + else: + mqtt_log.warning("[%s] Send ping, Network exception. Trying to reconnect" % time_info) + utime.sleep(2) + self.base_reconnect() + break + + def logTime(self): + log_time = utime.localtime() + time_info = "%d-%d-%d %d:%d:%d" % ( + log_time[0], log_time[1], log_time[2], log_time[3], log_time[4], log_time[5],) + return time_info diff --git a/ports/quectel/moduos.c b/ports/quectel/moduos.c new file mode 100644 index 0000000000000..26d44011c421f --- /dev/null +++ b/ports/quectel/moduos.c @@ -0,0 +1,240 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include + +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "extmod/misc.h" +#include "extmod/vfs.h" +#include "extmod/vfs_lfs.h" +#include "mpversion.h" +#include "helios_dev.h" +#if MICROPY_VFS_QUECFS +#include "vfs_quecfs.h" +#endif + +#if MICROPY_QPY_MODULE_UOS + +static char sysname[30] = {0}; +static char nodename[20] = {0}; +static char machine[30] = {0}; + +extern const mp_obj_type_t mp_fat_vfs_type; + + +static const qstr os_uname_info_fields[] = { + MP_QSTR_sysname, MP_QSTR_nodename,MP_QSTR_release, + MP_QSTR_version, MP_QSTR_machine, MP_QSTR_qpyver, +}; + +static MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, sysname); +static MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, nodename); +static MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); +static MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); +static MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, machine); +static MP_DEFINE_STR_OBJ(os_uname_info_qpyver_obj, QUECPYTHON_VERSION_STRING); + +static MP_DEFINE_ATTRTUPLE( + os_uname_info_obj, + os_uname_info_fields, + 6, + (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)&os_uname_info_qpyver_obj + ); + +static mp_obj_t os_uname2(void) { + Helios_Dev_GetProductName((void *)sysname, sizeof(sysname)); + Helios_Dev_GetModel((void *)nodename, sizeof(nodename)); + snprintf(machine, sizeof(machine), "%s with QUECTEL", nodename); + + os_uname_info_sysname_obj.len = strlen(sysname); + os_uname_info_nodename_obj.len = strlen(nodename); + os_uname_info_machine_obj.len = strlen(machine); + os_uname_info_qpyver_obj.len = strlen(QUECPYTHON_VERSION_STRING); + return (mp_obj_t)&os_uname_info_obj; +} +static MP_DEFINE_CONST_FUN_OBJ_0(os_uname2_obj, os_uname2); + + +static mp_obj_t os_uname(void) { + char sysname[40] = {0}; + char nodname[20] = {0}; + char release[20] = {0}; + char machine[30] = {0}; + char version[128] = {0}; + //char qpy_ver[20] = {0}; + + char mob_usb_product[64] = {0}; + char mob_model_id[64] = {0}; + //char _qpy_ver[20] = {0}; + + Helios_Dev_GetProductName((void *)mob_usb_product, sizeof(mob_usb_product)); + Helios_Dev_GetModel((void *)mob_model_id, sizeof(mob_model_id)); + //Helios_Dev_GetQpyVersion((void *)_qpy_ver, sizeof(_qpy_ver)); + + snprintf(sysname, sizeof(sysname), "sysname=%s", mob_usb_product); + snprintf(nodname, sizeof(nodname), "nodename=%s", mob_model_id); + snprintf(release, sizeof(release), "release=%s", MICROPY_VERSION_STRING); + snprintf(version, sizeof(version), "version=%s on %s", MICROPY_GIT_TAG, MICROPY_BUILD_DATE); + snprintf(machine, sizeof(machine), "machine=%s with QUECTEL", mob_model_id); + //snprintf(qpy_ver, sizeof(qpy_ver), "qpyver=%s", _qpy_ver); + + mp_obj_t tuple[6] = { + mp_obj_new_str(sysname, strlen(sysname)), + mp_obj_new_str(nodname, strlen(nodname)), + mp_obj_new_str(release, strlen(release)), + mp_obj_new_str(version, strlen(version)), + mp_obj_new_str(machine, strlen(machine)), + mp_obj_new_str("qpyver="QUECPYTHON_VERSION_STRING, strlen("qpyver="QUECPYTHON_VERSION_STRING)), + }; + + return mp_obj_new_tuple(6, tuple); +} + + +static MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); + +#include "quectel_version.h" +static mp_obj_t os_sdkver(void) { +#if defined(PLAT_Qualcomm) + return mp_obj_new_str(mob_sw_rev, strlen(mob_sw_rev)); +#else + extern mp_obj_t queclib_dev_fw_version(); + return queclib_dev_fw_version(); +#endif +} + +static MP_DEFINE_CONST_FUN_OBJ_0(os_sdkver_obj, os_sdkver); + +// Work out if the seed will be set on import or not. +#if MICROPY_MODULE_BUILTIN_INIT && defined(MICROPY_PY_URANDOM_SEED_INIT_FUNC) +#define OS_URANDOM_SEED_ON_IMPORT (1) +#else +#define OS_URANDOM_SEED_ON_IMPORT (0) +#endif + +#if OS_URANDOM_SEED_ON_IMPORT +static mp_obj_t os_urandom___init__(void) { + // This module may be imported by more than one name so need to ensure + // that it's only ever seeded once. + static bool seeded = false; + if (!seeded) { + seeded = true; + srand(MICROPY_PY_URANDOM_SEED_INIT_FUNC); + } + return mp_const_none; + +} +static MP_DEFINE_CONST_FUN_OBJ_0(os_urandom___init___obj, os_urandom___init__); +#endif + +#if MICROPY_PY_OS_DUPTERM +static mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { + (void)obj_in; + for (;;) { + int c = mp_uos_dupterm_rx_chr(); + if (c < 0) { + break; + } + ringbuf_put(&stdin_ringbuf, c); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +#endif + +extern const mp_obj_type_t helios_flash_device_type; +static const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + #if OS_URANDOM_SEED_ON_IMPORT + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&os_urandom___init___obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, + { MP_ROM_QSTR(MP_QSTR_uname2), MP_ROM_PTR(&os_uname2_obj) }, + { MP_ROM_QSTR(MP_QSTR_sdkver), MP_ROM_PTR(&os_sdkver_obj) }, + #if MICROPY_PY_OS_DUPTERM + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_FlashDevice), MP_ROM_PTR(&helios_flash_device_type) }, + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_FAT + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, + #endif + #if MICROPY_VFS_LFS1 + { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, + #endif + #if MICROPY_VFS_LFS2 + { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, + #endif + #if defined(PLAT_Qualcomm) + { MP_ROM_QSTR(MP_QSTR_VfsEfs), MP_ROM_PTR(&mp_type_vfs_efs) }, + #endif + #if defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) + { MP_ROM_QSTR(MP_QSTR_VfsTemp), MP_ROM_PTR(&mp_type_vfs_temp) }, + #endif + #if defined(PLAT_Unisoc_8910_R05) || defined(PLAT_Unisoc_8910_R06) || (defined(PLAT_Unisoc_8850_R02) && !defined(BOARD_EC800GCN_LD_XBND)) + { MP_ROM_QSTR(MP_QSTR_VfsSd), MP_ROM_PTR(&mp_type_vfs_temp) }, + #endif +#if MICROPY_VFS_FAT_SDIO + { MP_ROM_QSTR(MP_QSTR_VfsEmmc), MP_ROM_PTR(&mp_type_vfs_sdio_emmc) }, + { MP_ROM_QSTR(MP_QSTR_VfsSd), MP_ROM_PTR(&mp_type_vfs_sdio_sd) }, +#endif +#if MICROPY_VFS_QUECFS + { MP_ROM_QSTR(MP_QSTR_VfsQuecfs), MP_ROM_PTR(&mp_type_vfs_quecfs) }, +#endif + #endif +}; + +static MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t uos_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&os_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_uos, uos_module); +#endif diff --git a/ports/quectel/modussl_mbedtls.c b/ports/quectel/modussl_mbedtls.c new file mode 100644 index 0000000000000..149279d95beff --- /dev/null +++ b/ports/quectel/modussl_mbedtls.c @@ -0,0 +1,600 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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" +#include "mpconfigport.h" + + +#if MICROPY_PY_USSL && MICROPY_SSL_MBEDTLS +#include "mpconfigboard.h" +#include +#include +#include // needed because mp_is_nonblocking_error uses system error codes + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/objstr.h" + +// mbedtls_time_t +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/pk.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/x509.h" +#include "helios_debug.h" + +#define QPY_MPUSSL_LOG(msg, ...) custom_log(Ussl, msg, ##__VA_ARGS__) + +typedef struct _mp_obj_ssl_socket_t { + mp_obj_base_t base; + mp_obj_t sock; + mbedtls_entropy_context *entropy; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_ssl_context *ssl; + mbedtls_ssl_config *conf; + mbedtls_x509_crt *cacert; + mbedtls_x509_crt *cert; + mbedtls_pk_context *pkey; +} mp_obj_ssl_socket_t; + +struct ssl_args { + mp_arg_val_t ca; + mp_arg_val_t key; + mp_arg_val_t cert; + mp_arg_val_t server_side; + mp_arg_val_t server_hostname; + mp_arg_val_t do_handshake; +}; + +extern const mp_obj_type_t ussl_socket_type; + + +#ifdef MBEDTLS_DEBUG_C +static void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) { + (void)ctx; + (void)level; + QPY_MPUSSL_LOG("DBG:%s:%04d: %s\n", file, line, str); +} +#endif + +static NORETURN void mbedtls_raise_error(int err) { + // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the + // underlying socket into negative codes to pass them through mbedtls. Here we turn them + // positive again so they get interpreted as the OSError they really are. The + // cut-off of -256 is a bit hacky, sigh. + if (err < 0 && err > -256) { + mp_raise_OSError(-err); + } + + #if defined(MBEDTLS_ERROR_C) + // Including mbedtls_strerror takes about 1.5KB due to the error strings. + // MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror. + // It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile. + + // Try to allocate memory for the message + #define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit + mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + byte *o_str_buf = m_new_maybe(byte, ERR_STR_MAX); + if (o_str == NULL || o_str_buf == NULL) { + mp_raise_OSError(err); + } + + // print the error message into the allocated buffer + mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); + size_t len = strlen((char *)o_str_buf); + + // Put the exception object together + o_str->base.type = &mp_type_str; + o_str->data = o_str_buf; + o_str->len = len; + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + // raise + mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; + nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); + #else + // mbedtls is compiled without error strings so we simply return the err number + mp_raise_OSError(err); // err is typically a large negative number + #endif +} + +static int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { + mp_obj_t sock = *(mp_obj_t *)ctx; + + const mp_stream_p_t *sock_stream = mp_get_stream(sock); + int err; + + mp_printf(&mp_plat_print, "[mbedtls](write buf = %s ) :\n", buf); + + mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err); + mp_printf(&mp_plat_print, "[mbedtls](write out_sz = %d ) :\n", out_sz); + if (out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(err)) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + return -err; // convert an MP_ERRNO to something mbedtls passes through as error + } else { + return out_sz; + } +} + +static int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { + mp_obj_t sock = *(mp_obj_t *)ctx; + + const mp_stream_p_t *sock_stream = mp_get_stream(sock); + int err; + + mp_printf(&mp_plat_print, "[mbedtls](read len = %d ) :\n", len); + + mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err); + + mp_printf(&mp_plat_print, "[mbedtls](read out_sz = %d ) :\n", out_sz); + + if (out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(err)) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + return -err; + } else { + return out_sz; + } +} + +#define SSL_IGNORE_BADCERT_EXPIRED 0x01 /**< The certificate validity has expired. */ +#define SSL_IGNORE_BADCERT_REVOKED 0x02 /**< The certificate has been revoked (is on a CRL). */ +#define SSL_IGNORE_BADCERT_CN_MISMATCH 0x04 /**< The certificate Common Name (CN) does not match with the expected CN. */ +#define SSL_IGNORE_BADCERT_NOT_TRUSTED 0x08 /**< The certificate is not correctly signed by the trusted CA. */ +#define SSL_IGNORE_BADCRL_NOT_TRUSTED 0x10 /**< The CRL is not correctly signed by the trusted CA. */ +#define SSL_IGNORE_BADCRL_EXPIRED 0x20 /**< The CRL is expired. */ +#define SSL_IGNORE_BADCERT_MISSING 0x40 /**< Certificate was missing. */ +#define SSL_IGNORE_BADCERT_SKIP_VERIFY 0x80 /**< Certificate verification was skipped. */ +#define SSL_IGNORE_BADCERT_FUTURE 0x0200 /**< The certificate validity starts in the future. */ + +static int my_verify( void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags ) +{ + char buf[1024]; + ((void) data); + + mbedtls_x509_crt_info( buf, sizeof( buf ) - 1, "", crt ); + + mp_printf(&mp_plat_print, "[mbedtls](*flags %d error) :\n", *flags); + if(*flags & (SSL_IGNORE_BADCERT_FUTURE)) + { + *flags &= ~(SSL_IGNORE_BADCERT_FUTURE); + return( 0 ); + } + + return *flags; +} + +static mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { + // Verify the socket object has the full stream protocol + mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); + #if MICROPY_PY_USSL_FINALISER + mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); + #else + //mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); + mp_obj_ssl_socket_t *o = (mp_obj_ssl_socket_t *) m_malloc_with_finaliser(sizeof(mp_obj_ssl_socket_t)); + #endif + + int ret = -1; + o->entropy = (mbedtls_entropy_context *)malloc(sizeof(mbedtls_entropy_context)); + if (o->entropy == NULL){ + goto cleanup; + } + o->ctr_drbg = (mbedtls_ctr_drbg_context *)malloc(sizeof(mbedtls_ctr_drbg_context)); + if (o->ctr_drbg == NULL){ + goto cleanup; + } + o->ssl = (mbedtls_ssl_context *)malloc(sizeof(mbedtls_ssl_context)); + if (o->ssl == NULL){ + goto cleanup; + } + o->conf = (mbedtls_ssl_config *)malloc(sizeof(mbedtls_ssl_config)); + if (o->conf == NULL){ + goto cleanup; + } + o->cacert = (mbedtls_x509_crt *)malloc(sizeof(mbedtls_x509_crt)); + if (o->cacert == NULL){ + goto cleanup; + } + o->cert = (mbedtls_x509_crt *)malloc(sizeof(mbedtls_x509_crt)); + if (o->cert == NULL){ + goto cleanup; + } + o->pkey = (mbedtls_pk_context *)malloc(sizeof(mbedtls_pk_context)); + if (o->pkey == NULL){ + goto cleanup; + } + + o->base.type = &ussl_socket_type; + o->sock = sock; + + mbedtls_platform_setup(NULL); + + mbedtls_ssl_init(o->ssl); + mbedtls_ssl_config_init(o->conf); + mbedtls_x509_crt_init(o->cacert); + mbedtls_x509_crt_init(o->cert); + mbedtls_pk_init(o->pkey); + mbedtls_ctr_drbg_init(o->ctr_drbg); + #ifdef MBEDTLS_DEBUG_C + // Debug level (0-4) + mbedtls_debug_set_threshold(100); + #endif + + mbedtls_entropy_init(o->entropy); + const byte seed[] = "upy"; + ret = mbedtls_ctr_drbg_seed(o->ctr_drbg, mbedtls_entropy_func, o->entropy, seed, sizeof(seed)); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_ssl_config_defaults(o->conf, + args->server_side.u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + + if (ret != 0) { + goto cleanup; + } + + mbedtls_ssl_conf_authmode(o->conf, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_rng(o->conf, mbedtls_ctr_drbg_random, o->ctr_drbg); + #ifdef MBEDTLS_DEBUG_C + mbedtls_ssl_conf_dbg(o->conf, mbedtls_debug, NULL); + #endif + + ret = mbedtls_ssl_setup(o->ssl, o->conf); + if (ret != 0) { + goto cleanup; + } + + if (args->server_hostname.u_obj != mp_const_none) { + const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj); + ret = mbedtls_ssl_set_hostname(o->ssl, sni); + if (ret != 0) { + goto cleanup; + } + } + + mbedtls_ssl_set_bio(o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); + + if (args->ca.u_obj != mp_const_none) { + size_t cert_len; + const byte *cert = (const byte *)mp_obj_str_get_data(args->ca.u_obj, &cert_len); + + ret = mbedtls_x509_crt_parse(o->cacert, cert, cert_len + 1); + if (ret != 0) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; // use general error for all cert errors + goto cleanup; + } + + uint32_t flags; + char xcbuf[256]; + memset( xcbuf, 0, 256); + + ret = mbedtls_x509_crt_verify( o->cacert, o->cacert, NULL, NULL, &flags, NULL, NULL ); + if( ret != 0 ) + { + QPY_MPUSSL_LOG("flags: %0x\n", flags); + ret = mbedtls_x509_crt_verify_info(xcbuf, sizeof(xcbuf), "\n", flags); + + if (flags == 0x200) + { + QPY_MPUSSL_LOG("mbedtls_x509_crt_verify IGNORE\n"); + } + else + { + mp_printf(&mp_plat_print, "[mbedtls](mbedtls_x509_crt_verify FAIL: %s) :\n", xcbuf); + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; // use general error for all cert errors + goto cleanup; + } + } + else + { + QPY_MPUSSL_LOG("mbedtls_x509_crt_verify succeeded\n"); + } + + mbedtls_ssl_conf_ca_chain(o->conf, o->cacert, NULL); + mbedtls_ssl_conf_authmode(o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_verify( o->conf, my_verify, NULL ); + } + + if (args->key.u_obj != mp_const_none) { + size_t key_len; + const byte *key = (const byte *)mp_obj_str_get_data(args->key.u_obj, &key_len); + // len should include terminating null + ret = mbedtls_pk_parse_key(o->pkey, key, key_len + 1, NULL, 0); + if (ret != 0) { + ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA; // use general error for all key errors + goto cleanup; + } + + size_t cert_len; + const byte *cert = (const byte *)mp_obj_str_get_data(args->cert.u_obj, &cert_len); + // len should include terminating null + ret = mbedtls_x509_crt_parse(o->cert, cert, cert_len + 1); + if (ret != 0) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; // use general error for all cert errors + goto cleanup; + } + + ret = mbedtls_ssl_conf_own_cert(o->conf, o->cert, o->pkey); + if (ret != 0) { + goto cleanup; + } + } + + if (args->do_handshake.u_bool) { + while ((ret = mbedtls_ssl_handshake(o->ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + goto cleanup; + } + } + } + + return MP_OBJ_FROM_PTR(o); + +cleanup: + if (o->pkey != NULL) + { + mbedtls_pk_free(o->pkey); free(o->pkey); o->pkey = NULL; + } + if (o->cert != NULL) + { + mbedtls_x509_crt_free(o->cert); free(o->cert); o->cert = NULL; + } + if (o->cacert != NULL) + { + mbedtls_x509_crt_free(o->cacert); free(o->cacert); o->cacert = NULL; + } + if (o->ssl != NULL) + { + mbedtls_ssl_free(o->ssl); free(o->ssl); o->ssl = NULL; + } + if (o->conf != NULL) + { + mbedtls_ssl_config_free(o->conf); free(o->conf); o->conf = NULL; + } + if (o->ctr_drbg != NULL) + { + mbedtls_ctr_drbg_free(o->ctr_drbg); free(o->ctr_drbg); o->ctr_drbg = NULL; + } + if (o->entropy != NULL) + { + mbedtls_entropy_free(o->entropy); free(o->entropy); o->entropy = NULL; + } + + if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { + mp_raise_OSError(MP_ENOMEM); + } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); + } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); + } else if (ret == MBEDTLS_ERR_X509_FILE_IO_ERROR) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid ca")); + } else { + mbedtls_raise_error(ret); + } +} + +static mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) { + mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); + if (!mp_obj_is_true(binary_form)) { + mp_raise_NotImplementedError(NULL); + } + const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(o->ssl); + if (peer_cert == NULL) { + return mp_const_none; + } + return mp_obj_new_bytes(peer_cert->raw.p, peer_cert->raw.len); +} +static MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_getpeercert_obj, mod_ssl_getpeercert); + +static void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "<_SSLSocket %p>", self); +} + +static mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { + mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); + + mp_printf(&mp_plat_print, "[mbedtls_ssl_read](socket_read 1:\n"); + int ret = mbedtls_ssl_read(o->ssl, buf, size); + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // end of stream + return 0; + } + if (ret >= 0) { + return ret; + } + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + ret = MP_EWOULDBLOCK; + } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // If handshake is not finished, read attempt may end up in protocol + // wanting to write next handshake message. The same may happen with + // renegotation. + ret = MP_EWOULDBLOCK; + } + *errcode = -ret; + return MP_STREAM_ERROR; +} + +static mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { + mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); + + mp_printf(&mp_plat_print, "[mbedtls_ssl_write](socket_write 1:\n"); + + int ret = mbedtls_ssl_write(o->ssl, buf, size); + if (ret >= 0) { + return ret; + } + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + ret = MP_EWOULDBLOCK; + } else if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + // If handshake is not finished, write attempt may end up in protocol + // wanting to read next handshake message. The same may happen with + // renegotation. + ret = MP_EWOULDBLOCK; + } + *errcode = ret; + return MP_STREAM_ERROR; +} + +static mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { + mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in); + mp_obj_t sock = o->sock; + mp_obj_t dest[3]; + mp_load_method(sock, MP_QSTR_setblocking, dest); + dest[2] = flag_in; + return mp_call_method_n_kw(1, 0, dest); +} +static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +static mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in); + if (request == MP_STREAM_CLOSE) { + if (self->pkey) + { + mbedtls_pk_free(self->pkey); + free(self->pkey); + self->pkey = NULL; + } + if (self->cert) + { + mbedtls_x509_crt_free(self->cert); + free(self->cert); + self->cert = NULL; + } + if (self->cacert) + { + mbedtls_x509_crt_free(self->cacert); + free(self->cacert); + self->cacert = NULL; + } + if (self->ssl) + { + mbedtls_ssl_free(self->ssl); + free(self->ssl); + self->ssl = NULL; + } + if (self->conf) + { + mbedtls_ssl_config_free(self->conf); + free(self->conf); + self->conf = NULL; + } + if (self->ctr_drbg) + { + mbedtls_ctr_drbg_free(self->ctr_drbg); + free(self->ctr_drbg); + self->ctr_drbg = NULL; + } + if (self->entropy) + { + mbedtls_entropy_free(self->entropy); + free(self->entropy); + self->entropy = NULL; + } + } + // Pass all requests down to the underlying socket + return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode); +} + +static const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + #if MICROPY_PY_USSL_FINALISER + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) }, +}; + +static MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table); + +static const mp_stream_p_t ussl_socket_stream_p = { + .read = socket_read, + .write = socket_write, + .ioctl = socket_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + ussl_socket_type, + MP_QSTR_ussl, + MP_TYPE_FLAG_NONE, + protocol, &ussl_socket_stream_p, + locals_dict, &ussl_socket_locals_dict + ); + +bool mp_obj_is_ussl_socket(mp_obj_t o) { + return mp_obj_is_type(o, &ussl_socket_type); +} + +static mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // TODO: Implement more args + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ca, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + }; + + // TODO: Check that sock implements stream protocol + mp_obj_t sock = pos_args[0]; + struct ssl_args args; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + + return MP_OBJ_FROM_PTR(socket_new(sock, &args)); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket); + +static const mp_rom_map_elem_t mp_module_ssl_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ussl) }, + { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table); + +const mp_obj_module_t mp_module_ussl = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_ssl_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ussl, mp_module_ussl); + +#endif // MICROPY_PY_USSL diff --git a/ports/quectel/modutime.c b/ports/quectel/modutime.c new file mode 100644 index 0000000000000..1de09e4f5bea7 --- /dev/null +++ b/ports/quectel/modutime.c @@ -0,0 +1,220 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" +#include "utime_mphal.h" + +#if MICROPY_QPY_MODULE_UTIME + +#include "helios_debug.h" +#include "helios_rtc.h" + + +#define QPY_UTIME_LOG(msg, ...) custom_log(utime, msg, ##__VA_ARGS__) + +static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + timeutils_struct_time_t tm; + int64_t seconds; + if (n_args == 0 || args[0] == mp_const_none) { + Helios_RTCTime rtc_tm = {0, 0, 0, 1, 1, 1970, 4}; + Helios_RTC_GetLocalTime(&rtc_tm); + seconds = timeutils_seconds_since_2000(rtc_tm.tm_year, rtc_tm.tm_mon, rtc_tm.tm_mday, rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); + timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + } + else + { + //modified by freddy @20211227 Fix the exception when c=utime.mktime(utime.localtime()) utime.localtime(c) is executed when no network is injected + seconds = mp_obj_get_int_truncated(args[0]) - 946656000; // Pawn - 2021-03-17 -Fixed bug:Return time error + // Pawn - 2020-12-10 -Fixed bug:Return time error + timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + if (tm.tm_hour >= 24) + { + tm.tm_mday += 1; + tm.tm_hour = tm.tm_hour % 24; + + switch (tm.tm_mon) + { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + if (tm.tm_mday > 31) + { + tm.tm_mon += 1; + tm.tm_mday = tm.tm_mday % 31; + } + break; + case 4: + case 6: + case 9: + case 11: + if (tm.tm_mday > 30) + { + tm.tm_mon += 1; + tm.tm_mday = tm.tm_mday % 30; + } + break; + case 2: + if (((tm.tm_year%4 == 0) && (tm.tm_year%100 != 0)) || (tm.tm_year%400 == 0)) + { + if (tm.tm_mday > 29) + { + tm.tm_mon += 1; + tm.tm_mday = tm.tm_mday % 29; + } + } + else + { + if (tm.tm_mday > 28) + { + tm.tm_mon += 1; + tm.tm_mday = tm.tm_mday % 28; + } + } + break; + default: + QPY_UTIME_LOG("the value(%d) of month is invalid.\n", tm.tm_mon); + break; + } + + if (tm.tm_mon > 12) + { + tm.tm_year += 1; + tm.tm_mon = tm.tm_mon % 12; + } + } + } + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + +static mp_obj_t time_mktime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + mp_obj_get_array(tuple, &len, &elem); + Helios_RTCTime rtc_tm; + mp_uint_t time_unix; + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9 (%d given)"), len); + } + rtc_tm.tm_year = mp_obj_get_int(elem[0]); + rtc_tm.tm_mon = mp_obj_get_int(elem[1]); + rtc_tm.tm_mday = mp_obj_get_int(elem[2]); + rtc_tm.tm_hour = mp_obj_get_int(elem[3]); + rtc_tm.tm_min = mp_obj_get_int(elem[4]); + rtc_tm.tm_sec = mp_obj_get_int(elem[5]); + + if (0 >= (rtc_tm.tm_mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + rtc_tm.tm_mon += 12; /* Puts Feb last since it has leap day */ + rtc_tm.tm_year -= 1; + } + time_unix = ((( + ( mp_uint_t ) (rtc_tm.tm_year/4 - rtc_tm.tm_year/100 + rtc_tm.tm_year/400 + + 367*rtc_tm.tm_mon/12 + rtc_tm.tm_mday) + rtc_tm.tm_year*365 - 719499)*24 + + rtc_tm.tm_hour)*60 + rtc_tm.tm_min)*60 + rtc_tm.tm_sec - 28800; + + return mp_obj_new_int_from_uint(time_unix); +} +static MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); + +/*return rtc seconds since power on*/ +static mp_obj_t time_time(void) +{ + long seconds=0; + seconds = Helios_RTC_GetSecond(); + return mp_obj_new_int(seconds); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +#if 1 +static mp_obj_t time_set_timezone(mp_obj_t tz) +{ + int offset = mp_obj_get_int(tz); + if ((offset < -12) || (offset > 12)) + { + mp_raise_ValueError(MP_ERROR_TEXT("invalid value, timezone should be in [-12, +12].")); + } + Helios_RTC_SetTimeZoneOffset(offset); + return mp_obj_new_int(0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(time_set_timezone_obj, time_set_timezone); +#endif + +static mp_obj_t time_get_timezone(void) +{ + int offset = Helios_RTC_GetTimeZoneOffset(); + return mp_obj_new_int(offset); +} +static MP_DEFINE_CONST_FUN_OBJ_0(time_get_timezone_obj, time_get_timezone); + + + +static const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) }, + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, + + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, + { MP_ROM_QSTR(MP_QSTR_setTimeZone), MP_ROM_PTR(&time_set_timezone_obj) }, + { MP_ROM_QSTR(MP_QSTR_getTimeZone), MP_ROM_PTR(&time_get_timezone_obj) }, +}; + +static MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t utime_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&time_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_utime, utime_module); + +#endif /* MICROPY_QPY_MODULE_UTIME */ diff --git a/ports/quectel/mpconfigport.h b/ports/quectel/mpconfigport.h new file mode 100644 index 0000000000000..6cf8f636ec24e --- /dev/null +++ b/ports/quectel/mpconfigport.h @@ -0,0 +1,241 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 + +#include "mpconfigboard.h" + +/****************************** user define ************************************/ +// python grammar +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_CPYTHON_COMPAT (1) + +// builtin op +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_TEXT quecpython_help_text +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) + +// modules +#define MICROPY_PY_IO (1) +#define MICROPY_PY_JSON (1) +#define MICROPY_VFS (1) +#define MICROPY_PY_VFS (0) +#define MICROPY_PY_TIME (0) +#define MICROPY_PY_HEAPQ (0) +#define MICROPY_PY_OS (0) +#define MICROPY_PY_PLATFORM (0) +#define MICROPY_PY_SELECT (0) +#define MICROPY_PY_FRAMEBUF (0) +#define MICROPY_PY_ASYNCIO (0) +#define MICROPY_PY_BINASCII_CRC32 (0) + +#define MICROPY_PY_UCTYPES (MICROPY_QPY_PY_UCTYPES) +#define MICROPY_PY_DEFLATE (0) + +#define MICROPY_OPT_COMPUTED_GOTO (1) + +/******************************** base define **********************************/ +// options to control how MicroPython is built + +// Use the minimal starting configuration (disables all optional features). +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// You can disable the built-in MicroPython compiler by setting the following +// config option to 0. If you do this then you won't get a REPL prompt, but you +// will still be able to execute pre-compiled scripts, compiled with mpy-cross. +#define MICROPY_ENABLE_COMPILER (1) + +#define MICROPY_ENABLE_GC (1) +#define MICROPY_PY_GC (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_ENABLE_EXTERNAL_IMPORT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) //decimal numbers support +#define MICROPY_USE_INTERNAL_PRINTF (0) +#define MPZ_DIG_SIZE (16) +#define MICROPY_PY_SYS (1) + +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) + +#define MICROPY_ERROR_REPORTING MICROPY_ERROR_REPORTING_NORMAL +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_STATIC_NODES (0) +#define MICROPY_SCHEDULER_DEPTH (64)//8 +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_PY_UTIME_MP_HAL (1) + + +#define MICROPY_PY_THREAD (1) +#define MICROPY_PY_THREAD_GIL (1) +#define MICROPY_ENABLE_CALLBACK_DEAL (1) +#define MICROPY_READER_VFS (1) + +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_PY_SOFT_RESET (0) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_SYS_STDIO_BUFFER (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) +#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#endif + +#define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_PY_MICROPYTHON (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) + +#ifndef SSIZE_MAX +#define SSIZE_MAX 0xFFFFFFFF +#endif + +#if defined(PLAT_Unisoc) \ + || defined(PLAT_Unisoc_8850) \ + || defined(PLAT_ASR_1803s) \ + || defined(PLAT_RDA) \ + || defined(PLAT_Qualcomm) \ + || defined(PLAT_ASR) +#define MICROPY_PY_REPL_PASSWORD_PROTECT (1) +#endif + +#if defined(PLAT_SONY_ALT1350) +#define MICROPY_PY_USOCKET_EVENTS (1) +#endif + +#if defined(PLAT_SONY_ALT1350) +#define MICROPY_PY_USSL (1) +#define MICROPY_SSL_MBEDTLS (1) +#endif + +#if defined(PLAT_Qualcomm) +#define mp_type_fileio mp_type_vfs_efs_fileio +#define mp_type_textio mp_type_vfs_efs_textio +#elif defined(PLAT_EIGEN) +#define mp_type_fileio mp_type_vfs_lfs2_fileio +#define mp_type_textio mp_type_vfs_lfs2_textio +#elif defined(PLAT_EIGEN_718) +#define mp_type_fileio mp_type_vfs_lfs2_fileio +#define mp_type_textio mp_type_vfs_lfs2_textio +#else +#define mp_type_fileio mp_type_vfs_lfs1_fileio +#define mp_type_textio mp_type_vfs_lfs1_textio +#endif + + +// type definitions for the specific machine + +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef long mp_off_t; +#define UINT_FMT "%lu" +#define INT_FMT "%ld" + + +#if MICROPY_ENABLE_GC +#ifndef MICROPY_QPY_GC_HEAP_SIZE +#define MICROPY_QPY_GC_HEAP_SIZE (512 * 1024) +#endif +#define MICROPY_GC_HEAP_SIZE (MICROPY_QPY_GC_HEAP_SIZE) +#endif + +#ifdef MICROPY_QPY_MAIN_TASK_STACK_SIZE +#define MP_QPY_TASK_STACK_SIZE (MICROPY_QPY_MAIN_TASK_STACK_SIZE) +#else +#if defined(PLAT_RDA) +#define MP_QPY_TASK_STACK_SIZE (32 * 1024) +#elif defined(PLAT_EIGEN) +#define MP_QPY_TASK_STACK_SIZE (32 * 1024) +#elif defined(PLAT_EIGEN_718) +#define MP_QPY_TASK_STACK_SIZE (8 * 1024) +#elif defined(PLAT_ECR6600) +#define MP_QPY_TASK_STACK_SIZE (8 * 1024) +#elif defined(PLAT_aic8800m40) +#define MP_QPY_TASK_STACK_SIZE (10 * 1024) +#elif defined(PLAT_Unisoc_8850) || defined(PLAT_Unisoc_8850_R02) + #if defined(BOARD_EC600GCN_LD) || defined(BOARD_EC800GCN_LD) || defined(BOARD_EC800GCN_LD_XBND) \ + || defined(BOARD_EC800GCN_LD_HRXM) || defined(BOARD_EC600GCN_LD_YM) || defined(BOARD_EC800GCN_TT) \ + || defined(BOARD_EG800GLA_LD) + #define MP_QPY_TASK_STACK_SIZE (16 * 1024) + #else + #define MP_QPY_TASK_STACK_SIZE (64 * 1024) + #endif +#else +#define MP_QPY_TASK_STACK_SIZE (64 * 1024) +#endif +#endif + + +// We need to provide a declaration/definition of alloca() +#include + +#define STRINGIFY_VALUE(s) STRINGIFY(s) +#define STRINGIFY(s) #s +#define MICROPY_HW_BOARD_NAME STRINGIFY_VALUE(BOARD) +#define MICROPY_HW_MCU_NAME "QUECTEL" +// board specifics +#define MICROPY_PY_SYS_PLATFORM STRINGIFY_VALUE(BOARD) +#define QUECPYTHON_VERSION_STRING STRINGIFY_VALUE(QPYVER) + +#ifdef __linux__ +#define MICROPY_MIN_USE_STDOUT (1) +#endif + +#ifdef __thumb__ +#define MICROPY_MIN_USE_CORTEX_CPU (0) +#define MICROPY_MIN_USE_STM32_MCU (0) +#endif + +#define MP_STATE_PORT MP_STATE_VM + + +#if MICROPY_PY_THREAD +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + MP_THREAD_GIL_EXIT(); \ + MP_THREAD_GIL_ENTER(); \ + } while (0); +#else +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + asm ("waiti 0"); \ + } while (0); +#endif diff --git a/ports/quectel/mpconfigport.mk b/ports/quectel/mpconfigport.mk new file mode 100644 index 0000000000000..3eb1116478c94 --- /dev/null +++ b/ports/quectel/mpconfigport.mk @@ -0,0 +1,51 @@ +# Enable/disable modules and 3rd-party libs to be included in interpreter + +ifeq ($(strip $(PLAT)), $(filter $(PLAT),EIGEN EIGEN_718)) + +MICROPY_VFS_LFS1=0 +MICROPY_VFS_LFS2=1 + +else ifeq ($(strip $(PLAT)), $(filter $(PLAT),Unisoc_8850 Unisoc_8850_R02)) + +MICROPY_VFS_LFS1=1 +MICROPY_VFS_LFS2=0 + +else ifeq ($(strip $(PLAT)), $(filter $(PLAT),Unisoc Unisoc_8910_R05 Unisoc_8910_R06)) + +MICROPY_VFS_LFS1=1 +MICROPY_VFS_LFS2=0 + +else ifeq ($(strip $(PLAT)), $(filter $(PLAT),SONY SONY_ALT1350)) + +MICROPY_VFS_LFS1=0 +MICROPY_VFS_LFS2=0 +MICROPY_VFS_QUECFS=1 + +else ifeq ($(strip $(PLAT)), $(filter $(PLAT),ASR_1602 ASR_1609)) + +MICROPY_VFS_LFS1=0 +MICROPY_VFS_LFS2=1 + +else ifeq ($(strip $(PLAT)), $(filter $(PLAT),ASR ASR_1803s ASR_1803sc)) + +MICROPY_VFS_LFS1=1 +MICROPY_VFS_LFS2=0 + +else ifeq ($(strip $(PLAT)),ASR_1606) + +include $(TOP)/../../system/platform/ASR_1606/board/$(BOARD)/mpconfigboard.mk + +ifeq ($(MICROPY_VFS_LFS_VERSION), 1) +MICROPY_VFS_LFS1=1 +MICROPY_VFS_LFS2=0 +else +MICROPY_VFS_LFS1=0 +MICROPY_VFS_LFS2=1 +endif + +else + +MICROPY_VFS_LFS1=0 +MICROPY_VFS_LFS2=1 + +endif diff --git a/ports/quectel/mphalport.c b/ports/quectel/mphalport.c new file mode 100644 index 0000000000000..e7f4591ce10c1 --- /dev/null +++ b/ports/quectel/mphalport.c @@ -0,0 +1,384 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 +#include +#include +#include "py/stream.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/builtin.h" +#include "py/parse.h" +#include "py/ringbuf.h" +#include "py/mpthread.h" +#include "mphalport.h" + +#include "mpconfigport.h" +#include "helios_uart.h" +#include "helios_rtc.h" +#if MICROPY_PY_KBD_EXCEPTION +#include "shared/runtime/interrupt_char.h" +#include "py/runtime.h" +#endif + +#define QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM (2 * MICROPY_SCHEDULER_DEPTH) +static Helios_MsgQ_t qpy_mthread_sleep_queue = 0; +static int mthread_sleep_flag = 0; + +#define MTHREAD_SLEEP_ENTER() (mthread_sleep_flag = 1) +#define MTHREAD_SLEEP_EXIT() (mthread_sleep_flag = 0) +#define IS_MTHREAD_IN_SLEEP() (1 == mthread_sleep_flag) +#define MP_HAL_PORT_CHECK_OPEN (mp_hal_cdcPort_State == 1) + +static uint8_t stdin_ringbuf_array[256]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; + +//mia.zhong @20220308 fix input of multi thread dump problem. +Input_ListNode_t *Head_node = NULL; +static uint8_t mp_hal_cdcPort_State = 1; + + +static bool mp_mthread_sleep_deal_is_inited(void); +static void mp_hal_stdin_cb(uint64_t ind_type, Helios_UARTNum port, uint64_t size); + + +void mp_hal_port_open(uint8_t state) +{ + mp_hal_cdcPort_State = state; + Helios_UARTConfig UARTConfig = { + HELIOS_UART_BAUD, + HELIOS_UART_DATABIT_8, + HELIOS_UART_STOP_1, + HELIOS_UART_PARITY_NONE, + HELIOS_UART_FC_NONE + }; + Helios_UARTInitStruct UARTInitStruct = {&UARTConfig, mp_hal_stdin_cb}; + + if(state == 1) { + Helios_UART_Deinit(QPY_REPL_UART); + if (Helios_UART_Init(QPY_REPL_UART, &UARTInitStruct)) { + mp_hal_cdcPort_State = 0; + } + } +} + + +//mia.zhong @20220308 fix input of multi thread dump problem. +static bool mp_mthread_sleep_deal_is_inited_child(Input_ListNode_t *node) +{ + return (0 != node->msg_q); +} + +//mia.zhong @20220308 fix input of multi thread dump problem. +int mp_mthread_sleep_child(uint32_t ms) +{ + mp_uint_t msg; + int ret = -1; + if(Head_node) { + if(mp_mthread_sleep_deal_is_inited_child((Input_ListNode_t *)Head_node)) { + Head_node->mthread_sleep_flag = 1; + ret = Helios_MsgQ_Get(Head_node->msg_q, (mp_uint_t*)&msg, sizeof(msg), ms); + Head_node->mthread_sleep_flag = 0; + } + } + return ret; +} +//mia.zhong @20220308 fix input of multi thread dump problem. +void mp_mthread_wakeup_child(void) +{ + mp_uint_t msg = 0; + if(Head_node) { + if(mp_mthread_sleep_deal_is_inited_child((Input_ListNode_t *)Head_node) && (Head_node->mthread_sleep_flag == 1)) { + Helios_MsgQ_Put(Head_node->msg_q, (const void*)(&msg), sizeof(mp_uint_t), 0); + } + } +} + +//main thread sleep +int mp_mthread_sleep(uint32_t ms) +{ + mp_uint_t msg; + int ret = -1; + if(mp_mthread_sleep_deal_is_inited()) { + MTHREAD_SLEEP_ENTER(); + ret = Helios_MsgQ_Get(qpy_mthread_sleep_queue, (mp_uint_t*)&msg, sizeof(msg), ms); + MTHREAD_SLEEP_EXIT(); + } + return ret; +} + +//wake up main sleep +void mp_mthread_wakeup(void) +{ + mp_uint_t msg = 0; + if(mp_mthread_sleep_deal_is_inited() && (IS_MTHREAD_IN_SLEEP())) { + Helios_MsgQ_Put(qpy_mthread_sleep_queue, (const void*)(&msg), sizeof(mp_uint_t), 0); + } +} + +void mp_main_thread_wakeup() +{ + //mia.zhong @20220308 fix input of multi thread dump problem. + if (Head_node != NULL) { + mp_mthread_wakeup_child(); + } else { + mp_mthread_wakeup();//forrest.liu@20210809 add for quecpython task repl using waiting msg + } +} + +static void mp_hal_stdin_cb(uint64_t ind_type, Helios_UARTNum port, uint64_t size) +{ + if(MP_HAL_PORT_CHECK_OPEN) { +#if MICROPY_PY_KBD_EXCEPTION + if(IS_MAINPY_RUNNING_FLAG_TRUE() && Head_node == NULL) + { + uint64_t i = 0; + volatile unsigned char c = 0; + for(i= 0; i < size; i++) + { + if(Helios_UART_Read(QPY_REPL_UART, (void *)&c, sizeof(unsigned char)) >0 ) { + if (!IS_REPL_REFUSED() && (c == mp_interrupt_char)) { + // Signal keyboard interrupt to be raised as soon as the VM resumes + mp_sched_keyboard_interrupt(); + break; + } + continue; + } + break; + } + if(i < size) + { + for(; i < size; i++) + { + if(Helios_UART_Read(QPY_REPL_UART, (void *)&c, sizeof(unsigned char)) >0 ) { + continue; + } + break; + } + } + } + else +#endif + { + mp_main_thread_wakeup(); + + } + } +} + +int mp_hal_stdio_init(void) +{ + Helios_UARTConfig UARTConfig = { + HELIOS_UART_BAUD_115200, + HELIOS_UART_DATABIT_8, + HELIOS_UART_STOP_1, + HELIOS_UART_PARITY_NONE, + HELIOS_UART_FC_NONE + }; + + Helios_UARTInitStruct UARTInitStruct = {&UARTConfig, mp_hal_stdin_cb}; + int ret = (int)Helios_UART_Init(QPY_REPL_UART, &UARTInitStruct); + if(ret) + { + return -1; + } + return 0; +} + +int mp_hal_stdin_rx_chr(void) +{ + while(1) + { + volatile unsigned char c = 0; + + if(MP_HAL_PORT_CHECK_OPEN && Helios_UART_Read(QPY_REPL_UART, (void *)&c, sizeof(unsigned char)) >0 ) { + return c; + } + + //forrest.liu@20210809 add for quecpython task repl using waiting msg + MP_THREAD_GIL_EXIT(); + //mia.zhong @20220308 fix input of multi thread dump problem. + if (Head_node != NULL) + mp_mthread_sleep_child(HELIOS_WAIT_FOREVER); + else + mp_mthread_sleep(HELIOS_WAIT_FOREVER); + MP_THREAD_GIL_ENTER(); + + MICROPY_EVENT_POLL_HOOK + } +} + +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) +{ + if (!str|| !len) return 0; + if(MP_HAL_PORT_CHECK_OPEN) { + Helios_UART_Write(QPY_REPL_UART, (void *)str, len); + } + return len; +} + +mp_uint_t mp_hal_ticks_ms(void) +{ + return (mp_uint_t)Helios_RTC_TicksToMs(); +} + +mp_uint_t mp_hal_ticks_us(void) +{ + return (mp_uint_t)Helios_RTC_TicksToUs(); +} + +uint64_t mp_hal_time_ns(void) +{ + return 0; +} + +void mp_hal_delay_ms(mp_uint_t ms) { + mp_uint_t dt = 0; + mp_uint_t t0 = 0,t1 = 0; + Helios_Thread_t taskid = 0; + extern Helios_Thread_t ql_micropython_task_ref; + taskid = Helios_Thread_GetID(); + mp_uint_t qpy_mthread_sleep_deal_fun(mp_uint_t ms); + + if(ql_micropython_task_ref == taskid) + { + for(;;) + { + t0 = mp_hal_ticks_us(); + MP_THREAD_GIL_EXIT(); + mp_uint_t wait_time = qpy_mthread_sleep_deal_fun(ms); + MP_THREAD_GIL_ENTER(); + if(wait_time >= ms) + { + return; + } + MICROPY_EVENT_POLL_HOOK; + t1 = mp_hal_ticks_us(); + dt = t1 - t0; + if(dt / 1000 >= ms) + { + return; + } + ms -= dt / 1000; + } + } + else + { + MP_THREAD_GIL_EXIT(); + Helios_msleep(ms); + MP_THREAD_GIL_ENTER(); + } +} + +void mp_hal_delay_us(mp_uint_t us) { + mp_uint_t ms = us / 1000; + ms = ms ? ms : 1; + mp_hal_delay_ms(ms); +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return (mp_uint_t)Helios_RTC_GetTicks(); +} + + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { + ret |= MP_STREAM_POLL_RD; + } + return ret; +} + +mp_uint_t qpy_mthread_sleep_deal_fun(mp_uint_t ms) +{ + if(mp_mthread_sleep_deal_is_inited()) + { + mp_uint_t dt; + mp_uint_t t0 = mp_hal_ticks_us(); + int ret = mp_mthread_sleep(ms); + if(ret < 0) + { + return ms; + } + else + { + mp_uint_t t1 = mp_hal_ticks_us(); + dt = t1 - t0; + return dt / 1000; + } + } + else + { + Helios_msleep(ms); + return ms; + } +} + +//mia.zhong @20220308 fix input of multi thread dump problem. +void _add_list_node() +{ + Input_ListNode_t *node = (Input_ListNode_t *)malloc(sizeof(Input_ListNode_t)); + memset(node, 0, sizeof(Input_ListNode_t)); + + if(0 == node->msg_q) { + node->msg_q = Helios_MsgQ_Create(QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM, sizeof(mp_uint_t)); + } + node->next_node = Head_node; + Head_node = node; +} + +//mia.zhong @20220308 fix input of multi thread dump problem. +void _delete_list_node()//delete node +{ + Input_ListNode_t *node = NULL; + + if (Head_node) { + if(0 != Head_node->msg_q) { + Helios_MsgQ_Delete(Head_node->msg_q); + } + node = Head_node; + Head_node = (Input_ListNode_t *)Head_node->next_node; + node->next_node = NULL; + if (node) { + free(node); + node = NULL; + } + } +} + +void mp_mthread_sleep_deal_init(void) +{ + if(0 == qpy_mthread_sleep_queue) { + qpy_mthread_sleep_queue = \ + Helios_MsgQ_Create(QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM, sizeof(mp_uint_t)); + } +} + +static bool mp_mthread_sleep_deal_is_inited(void) +{ + return (0 != qpy_mthread_sleep_queue); +} + diff --git a/ports/quectel/mphalport.h b/ports/quectel/mphalport.h new file mode 100644 index 0000000000000..21375137df4c3 --- /dev/null +++ b/ports/quectel/mphalport.h @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef __MPHAL_PORT_H +#define __MPHAL_PORT_H + +#include "helios_os.h" +#include "helios_uart.h" + +#if defined(PLAT_RDA) +#define QPY_REPL_UART HELIOS_UART0 +#define HAL_TICK1S 16.384 +/*Due to low power consumption of BC25 series, the baud rate needs to be set to 57600, please do not modify.*/ +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_57600 +#elif defined(PLAT_Qualcomm) +#define HAL_TICK1S 32.768 +#define QPY_REPL_UART HELIOS_UART3 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 +#elif defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) +#define HAL_TICK1S 3.25 +#define QPY_REPL_UART HELIOS_UART3 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 +#elif defined(PLAT_ECR6600) +#define HAL_TICK1S 0.5 +#define QPY_REPL_UART HELIOS_UART2 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 +#elif defined(PLAT_aic8800m40) +#define HAL_TICK1S 32.768 +#define QPY_REPL_UART HELIOS_UART1 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 +#else +#define HAL_TICK1S 32.768 +#define QPY_REPL_UART HELIOS_UART3 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 +#endif + +//mia.zhong @20220308 input接口多线程调用导致dump问题 +typedef struct Input_ListNode +{ + //int id; + int mthread_sleep_flag; + Helios_MsgQ_t msg_q; + void *next_node; +} Input_ListNode_t; + +void _add_list_node(); +void _delete_list_node(); +mp_uint_t mp_hal_ticks_cpu(void); + +void mp_mthread_sleep_deal_init(void); +int mp_mthread_sleep(uint32_t ms); +void mp_mthread_wakeup(void); +int mp_mthread_sleep_child(uint32_t ms); +void mp_mthread_wakeup_child(void); + +int mp_hal_stdio_init(void); + +int mp_hal_stdin_rx_chr(void); + +void mp_hal_port_open(uint8_t state); + +void mp_hal_set_interrupt_char(int c); + +uint64_t mp_hal_time_ns(void); +#endif diff --git a/ports/quectel/mpthreadport.c b/ports/quectel/mpthreadport.c new file mode 100644 index 0000000000000..d8312b767303b --- /dev/null +++ b/ports/quectel/mpthreadport.c @@ -0,0 +1,334 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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 "stdio.h" + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "py/mphal.h" +#include "mpthreadport.h" +#include "helios_os.h" + +#if MICROPY_PY_THREAD + +// this structure forms a linked list, one node per active thread +typedef struct _thread_t { + Helios_Thread_t id; // system id of thread + int ready; // whether the thread is ready and running + void *arg; // thread Python args, a GC root pointer + void *stack; // pointer to the stack + size_t stack_len; // number of words in the stack + struct _thread_t *next; +} thread_t; + +// the mutex controls access to the linked list +static mp_thread_mutex_t thread_mutex = 0; +static thread_t thread_entry0; +static thread_t *thread = NULL; // root pointer, handled by mp_thread_gc_others + +void mp_thread_init(void *stack, uint32_t stack_len) { + mp_thread_set_state(&mp_state_ctx.thread); + // create the first entry in the linked list of all threads + thread = &thread_entry0; + thread->id = Helios_Thread_GetID(); + thread->ready = 1; + thread->arg = NULL; + thread->stack = stack; + thread->stack_len = stack_len; + thread->next = NULL; + mp_thread_mutex_init(&thread_mutex); +} + +void _vPortCleanUpTCB(void *tcb) { + if (thread == NULL) { + // threading not yet initialised + return; + } + thread_t *prev = NULL; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { + // unlink the node from the list + if ((void *)th->id == tcb) { + if (prev != NULL) { + prev->next = th->next; + } else { + // move the start pointer + thread = th->next; + } + // explicitly release all its memory + m_del(thread_t, th, 1); + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_gc_others(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + gc_collect_root((void **)&th, 1); + gc_collect_root(&th->arg, 1); // probably not needed + if (th->id == Helios_Thread_GetID()) { + continue; + } + if (!th->ready) { + continue; + } + gc_collect_root(th->stack, th->stack_len); // probably not needed + } + mp_thread_mutex_unlock(&thread_mutex); +} + +mp_state_thread_t *mp_thread_get_state(void) { + return (mp_state_thread_t *)Helios_Thread_GetSpecific(); +} + +void mp_thread_set_state(mp_state_thread_t *state) { + Helios_Thread_SetSpecific((mp_state_thread_t *)state); +} + +void mp_thread_start(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == Helios_Thread_GetID()) { + th->ready = 1; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +static void *(*ext_thread_entry)(void *) = NULL; +static void thread_entry(void *arg) { + if (ext_thread_entry) { + ext_thread_entry(arg); + } + _vPortCleanUpTCB((void*)Helios_Thread_GetID()); + Helios_Thread_Exit(); +} + + +int mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) { + // store thread entry function into a global variable so we can access it + ext_thread_entry = entry; + if (*stack_size == 0) { + *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size + } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) { + *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size + } + + // Allocate linked-list node (must be outside thread_mutex lock) + thread_t *th = m_new_obj(thread_t); + + mp_thread_mutex_lock(&thread_mutex, 1); + + // create thread + Helios_ThreadAttr ThreadAttr = { + .name = name, + .stack_size = *stack_size / sizeof(uint32_t), + .priority = priority, + .entry = thread_entry, + .argv = arg + }; + Helios_Thread_t thread_id = Helios_Thread_Create(&ThreadAttr); + if(thread_id == 0) + { + mp_thread_mutex_unlock(&thread_mutex); + mp_raise_msg(&mp_type_OSError, (mp_rom_error_text_t)"can't create thread"); + } + + // add thread to linked list of all threads + th->id = thread_id; + th->ready = 0; + th->arg = arg; + th->stack = Helios_Thread_GetStaskPtr(th->id); + + //stack_len must be 1/4 of ThreadAttr.stack_size + th->stack_len = ThreadAttr.stack_size / sizeof(uint32_t); + th->next = thread; + thread = th; + + // adjust the stack_size to provide room to recover from hitting the limit + *stack_size -= 1024; + + mp_thread_mutex_unlock(&thread_mutex); + + return (int)th;//return task_node +} + +//forrest.liu@20210408 increase the priority for that python thread can,t be scheduled +mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { + int th_node = mp_thread_create_ex(entry, arg, stack_size, (MP_THREAD_PRIORITY - 1), "mp_thread"); + return th_node; +} + +void mp_new_thread_add(uint32_t th_id, uint32_t stack_size) { + // Allocate linked-list node (must be outside thread_mutex lock) + thread_t *th = m_new_obj(thread_t); + // add thread to linked list of all threads + th->id = th_id; + th->ready = 0; + th->arg = NULL; + th->stack = Helios_Thread_GetStaskPtr((Helios_Thread_t)th->id); + + th->stack_len = stack_size / sizeof(uint32_t); + th->next = thread; + thread = th; +} + +void mp_thread_finish(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == Helios_Thread_GetID()) { + th->ready = 0; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +bool mp_thread_finish_by_threadid(int thread_id) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == (Helios_Thread_t)thread_id) { + th->ready = 0; + mp_thread_mutex_unlock(&thread_mutex); + return true; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return false; +} + +bool mp_is_python_thread(void) { + Helios_Thread_t curr_id = Helios_Thread_GetID(); + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == curr_id) { + mp_thread_mutex_unlock(&thread_mutex); + return true; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return false; +} + +int mp_thread_get_current_tsknode(void) { + Helios_Thread_t curr_id = Helios_Thread_GetID(); + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == (Helios_Thread_t)curr_id) { + mp_thread_mutex_unlock(&thread_mutex); + return (int)th; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return 0; +} + +int mp_thread_get_tskid_by_tsknode(void *th_node) { + thread_t *th_id = (thread_t *)th_node; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th == th_id) { + mp_thread_mutex_unlock(&thread_mutex); + return (int)th->id; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return 0; +} + +mp_uint_t mp_thread_get_id(void) { + return (mp_uint_t)Helios_Thread_GetID(); +} + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { + *mutex = Helios_Mutex_Create(); +} + +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { + return Helios_Mutex_Lock(*mutex, wait ? QPY_WAIT_FOREVER : QPY_NO_WAIT); +} + +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { + Helios_Mutex_Unlock(*mutex); +} + +//Added by Freddy @20210818 delete a lock +void mp_thread_mutex_del(mp_thread_mutex_t *mutex) { + Helios_Mutex_Delete(*mutex); +} + +void mp_thread_semphore_init(mp_thread_semphore_t *sem, uint32_t initcount) { + *sem = Helios_Semaphore_Create(initcount, initcount); +} + +int mp_thread_semphore_acquire(mp_thread_semphore_t *sem, int wait) { + return Helios_Semaphore_Acquire(*sem, wait); +} + +void mp_thread_semphore_release(mp_thread_semphore_t *sem) { + Helios_Semaphore_Release(*sem); +} + +void mp_thread_semphore_del(mp_thread_semphore_t *sem) { + Helios_Semaphore_Delete(*sem); +} + +void mp_thread_deinit(void) { + for (;;) { + // Find a task to delete + int id = 0; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + // Don't delete the current task + if (th->id != Helios_Thread_GetID()) { + id = th->id; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); + + if (id == 0) { + // No tasks left to delete + break; + } else { + // Call qpy_thread_delete to delete the task (it will call vPortCleanUpTCB) + Helios_Thread_Delete(id); + _vPortCleanUpTCB((void*)id); + } + } +} + +unsigned int mp_get_available_memory_size(void) +{ + return Helios_GetAvailableMemorySize(); +} + + +#endif // MICROPY_PY_THREAD \ No newline at end of file diff --git a/ports/quectel/mpthreadport.h b/ports/quectel/mpthreadport.h new file mode 100644 index 0000000000000..2f36bf9c1f06a --- /dev/null +++ b/ports/quectel/mpthreadport.h @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_MPTHREADPORT_H +#define MICROPY_INCLUDED_MPTHREADPORT_H + +#include "helios_os.h" + +#if defined(PLAT_EIGEN) || defined(PLAT_EIGEN_718) +#define MP_THREAD_MIN_STACK_SIZE (4 * 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024 + 1024) +#define MP_THREAD_PRIORITY 100 +#else +#define MP_THREAD_MIN_STACK_SIZE (32 * 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) +#define MP_THREAD_PRIORITY 100 +#endif + + +typedef Helios_Mutex_t mp_thread_mutex_t; +typedef Helios_Sem_t mp_thread_semphore_t; + + +#define QPY_WAIT_FOREVER HELIOS_WAIT_FOREVER +#define QPY_NO_WAIT HELIOS_NO_WAIT + +void mp_thread_init(void *stack, uint32_t stack_len); +void mp_thread_gc_others(void); +void mp_thread_deinit(void); +unsigned int mp_get_available_memory_size(void); +bool mp_is_python_thread(void); +int mp_thread_get_current_tsknode(void); +int mp_thread_get_tskid_by_tsknode(void *th_node); + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex); +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait); +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex); +void mp_thread_mutex_del(mp_thread_mutex_t *mutex); + +void mp_thread_semphore_init(mp_thread_semphore_t *sem, uint32_t initcount); +int mp_thread_semphore_acquire(mp_thread_semphore_t *sem, int wait); +void mp_thread_semphore_release(mp_thread_semphore_t *sem); +void mp_thread_semphore_del(mp_thread_semphore_t *sem); + +void mp_new_thread_add(uint32_t th_id, uint32_t stack_size); + +#endif // MICROPY_INCLUDED_MPTHREADPORT_H + diff --git a/ports/quectel/plat.mk b/ports/quectel/plat.mk new file mode 100644 index 0000000000000..48c748053e4b2 --- /dev/null +++ b/ports/quectel/plat.mk @@ -0,0 +1,9 @@ +# plat definition + +ifeq ($(strip $(PLAT)),) +export PLAT = Unisoc +export BOARD = EG915UEC_AC + +DFLAGS = PLAT_$(strip $(PLAT)) BOARD_$(strip $(BOARD)) BOARD=$(strip $(BOARD)) +endif + diff --git a/ports/quectel/qstrdefsport.h b/ports/quectel/qstrdefsport.h new file mode 100644 index 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/quectel/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/quectel/quectel.mk b/ports/quectel/quectel.mk new file mode 100644 index 0000000000000..a788f43a006f2 --- /dev/null +++ b/ports/quectel/quectel.mk @@ -0,0 +1,422 @@ +include plat.mk + +PLAT_DFLAGS = $(addprefix -D,$(DFLAGS)) +PLAT_CFLAGS = $(QUEC_CFLAGS) +PLAT_CFLAGS += -Wno-unused-parameter -Wformat=0 -Wno-unused-function + +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/config +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/include + +ifeq ($(strip $(PLAT)),Qualcomm) +INC += -I$(COMPILER_PATH)/armv7m-none-eabi/libc/include +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/c-utility +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/c-utility/inc +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/c-utility/inc/azure_c_shared_utility +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/c-utility/pal +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/c-utility/pal/generic +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/iothub_client +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/iothub_client/inc +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/iothub_client/inc/internal +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/serializer +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/serializer/inc +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/umqtt +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/umqtt/inc +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/azure_api/umqtt/inc/azure_umqtt_c +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/qapi +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include/threadx_api +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/at +INC += -I$(ROOT)/system/esim +endif + +INC += -I. +INC += -I$(HEADER_BUILD) +INC += -I$(TOP) +INC += -I$(TOP)/py +INC += -I$(TOP)/extmod +INC += -I$(TOP)/lib/utils +INC += -I$(TOP)/lib/mp-readline +INC += -I$(TOP)/lib/littlefs +INC += -I$(TOP)/lib/netutils +INC += -I$(TOP)/lib/timeutils +INC += -I$(ROOT)/peripheral +INC += -I$(ROOT)/system/include +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include +INC += -I$(ROOT)/system/debug +INC += -I$(ROOT)/system/dev +INC += -I$(ROOT)/system/fs +INC += -I$(ROOT)/system/hal +INC += -I$(ROOT)/system/gnss +INC += -I$(ROOT)/system/network +INC += -I$(ROOT)/system/at +INC += -I$(ROOT)/system/aliiot + +ifeq ($(strip $(PLAT)),$(filter $(PLAT),ASR_1803s ASR_1803sc)) +INC += -I$(ROOT)/system/csd +INC += -I$(ROOT)/system/esim +endif +ifeq ($(strip $(PLAT)),ASR_1606) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +endif +ifeq ($(strip $(PLAT)),ASR_1602) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +endif +ifeq ($(strip $(PLAT)),ASR_1609) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +endif + +ifeq ($(strip $(PLAT)),Unisoc) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/apps +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/arpa +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/net +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/netinet +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/sys +INC += -I$(ROOT)/system/at +INC += -I$(ROOT)/system/esim +endif + +ifeq ($(strip $(PLAT)),$(filter $(PLAT),Unisoc_8910_R05 Unisoc_8910_R06)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/apps +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif/ppp +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif/ppp/polarssl +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/arpa +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/net +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/netinet +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/sys +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/stdc +INC += -I$(ROOT)/system/esim +endif + +ifeq ($(strip $(PLAT)),$(filter $(PLAT),EIGEN EIGEN_718)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip/apps +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/arch +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/posix/sys +endif + +ifeq ($(strip $(PLAT)),$(filter $(PLAT),SONY_ALT1350)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/arch +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/lwip/prot +endif + +ifeq ($(strip $(PLAT)),$(filter $(PLAT),Unisoc_8850 Unisoc_8850_R02)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/apps +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif/ppp +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif/ppp/polarssl +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/arpa +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/net +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/netinet +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/posix/sys +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/compat/stdc +endif + +ifeq ($(strip $(PLAT)),RDA) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include/arch +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/apps +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/arpa +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/net +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/netinet +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/src/include/posix/sys +endif + +ifeq ($(strip $(PLAT)), $(filter $(PLAT),ASR ASR_1803s ASR_1803sc)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/at +endif + +ifeq ($(strip $(PLAT)),ECR6600) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT)) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/include +endif + +ifeq ($(strip $(PLAT)),aic8800m40) +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/ports/rtos/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/ports/rtos/include/arch +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/lwip +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/lwip/priv +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/lwip/prot +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/netif +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/posix +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/lwip-STABLE-2_0_2_RELEASE_VER/src/include/posix/sys +INC += -I$(ROOT)/system/lwip/$(strip $(PLAT))/net_al +endif + +#ifeq ($(CONFIG_MBEDTLS), 1) +INC += -I$(ROOT)/system/mbedtls +INC += -I$(ROOT)/system/mbedtls/include +INC += -I$(ROOT)/system/mbedtls/include/mbedtls +INC += -I$(ROOT)/system/mbedtls/library +INC += -I$(ROOT)/system/mbedtls/port/helios/inc +#endif + +INC += -I$(ROOT)/system/network +INC += -I$(ROOT)/system/os +INC += -I$(ROOT)/system/startup +INC += -I$(ROOT)/system/fota +INC += -I$(ROOT)/system/bt +INC += -I$(ROOT)/system/at +ifeq (y, $(CONFIG_LVGL)) +INC += -I$(TOP)/lib/lvgl +INC += -I$(TOP)/lib/lvgl/src +INC += -I$(TOP)/lib/lvgl/src/core +INC += -I$(TOP)/lib/lvgl/src/draw +INC += -I$(TOP)/lib/lvgl/src/extra +INC += -I$(TOP)/lib/lvgl/src/font +INC += -I$(TOP)/lib/lvgl/src/gpu +INC += -I$(TOP)/lib/lvgl/src/hal +INC += -I$(TOP)/lib/lvgl/src/misc +INC += -I$(TOP)/lib/lvgl/src/widgts +endif +INC += -I$(ROOT)/driver +INC += -I$(ROOT)/driver/lcd +ifeq (y, $(CONFIG_QRCODE)) +INC += -I$(ROOT)/components/qrcode +endif +ifeq ($(CONFIG_JRTC), y) +INC += -I$(ROOT)/components/jrtc/inc +endif + +ifeq ($(CONFIG_SENSOR_VC9202), y) +INC += -I$(ROOT)/components/sensor/VC9202/inc +INC += -I$(ROOT)/components/sensor/VC9202/algo/inc +endif + +ifeq (y, $(CONFIG_QUECTHING)) +INC += -I$(ROOT)/components/quecsdk/cloud +INC += -I$(ROOT)/components/quecsdk/driverLayer +INC += -I$(ROOT)/components/quecsdk/thirdLib/mqtt +INC += -I$(ROOT)/components/quecsdk/thirdLib/cJSON +INC += -I$(ROOT)/components/quecsdk/kernel +endif +ifeq ($(CONFIG_SPINAND), y) +INC += -I$(ROOT)/components/fs/include +INC += -I$(ROOT)/components/fs/yaffs/src/direct +INC += -I$(ROOT)/components/fs/yaffs/src/port +INC += -I$(ROOT)/components/fs/yaffs/src +INC += -I$(ROOT)/components/fs/yaffs +endif +ifeq ($(CONFIG_RTMP), y) +INC += -I$(ROOT)/components/rtmpdump/librtmp/inc +INC += -I$(ROOT)/components/rtmpdump/libz/inc +endif +INC += -I$(ROOT)/utilities +ifeq ($(CONFIG_JPEG), y) +INC += -I$(ROOT)/components/libjpeg-turbo/include +INC += -I$(ROOT)/components/libjpeg-turbo +INC += -I$(ROOT)/components/libjpeg-turbo/src +INC += -I$(ROOT)/components/libjpeg-turbo/src/simd +endif +ifeq ($(CONFIG_POC_BND), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/bnd/inc +endif +ifeq ($(CONFIG_POC_BND_XIN), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/bnd_xin/inc +endif +ifeq ($(CONFIG_POC_SL), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/sl/inc +endif +ifeq ($(CONFIG_POC_QS), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/qs/inc +endif +#Carlos.Meng add 2024/02/22 +ifeq ($(CONFIG_POC_QS_R07), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/qs/inc +endif +#Stephen.Gao add 2022/11/23 +ifeq ($(CONFIG_POC_ZZD), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/zzd/inc +endif +#Stephen.Gao add 2023/01/06 +ifeq ($(CONFIG_POC_YDWL), y) +INC += -I$(ROOT)/components/poc +INC += -I$(ROOT)/components/poc/ydwl/inc +endif + +ifeq ($(CONFIG_POC_SLPOC), y) +INC += -I$(ROOT)/components/poc -I$(ROOT)/components/poc/slpoc/src +INC += -I$(ROOT)/components/poc/slpoc/inc +SRC_QSTR += $(ROOT)/components/poc/slpoc/src/modpoc_slpoc.c +endif + +ifeq ($(CONFIG_POC_RDA_BND), y) +INC += -I$(ROOT)/components/poc -I$(ROOT)/components/poc/rda_bnd/src +INC += -I$(ROOT)/components/poc/rda_bnd/inc +SRC_QSTR += $(ROOT)/components/poc/rda_bnd/src/modpocrda.c +endif + +ifeq ($(CONFIG_POC_RDA_BND_XIN), y) +INC += -I$(ROOT)/components/poc -I$(ROOT)/components/poc/rda_bnd_xin/src +INC += -I$(ROOT)/components/poc/rda_bnd_xin/inc +SRC_QSTR += $(ROOT)/components/poc/rda_bnd_xin/src/modpocrda.c +endif + +ifeq ($(CONFIG_POC_TID), y) +INC += -I$(ROOT)/components/poc -I$(ROOT)/components/poc/tid/src +INC += -I$(ROOT)/components/poc/tid/inc +SRC_QSTR += $(ROOT)/components/poc/tid/src/modpoc_tid.c +endif + +ifeq (y,$(CONFIG_DECODE_ZBAR)) +INC += -I$(ROOT)/components/decode/ZBar/zbar +INC += -I$(ROOT)/components/decode/ZBar/include +INC += -I$(ROOT)/components/decode/ZBar/zbar/decoder +INC += -I$(ROOT)/components/decode/ZBar/zbar/qrcode +endif +ifeq (y,$(CONFIG_DECODE_QUECTEL)) +INC += -I$(ROOT)/components/decode/quectel/inc +endif + +ifeq (y,$(CONFIG_DECODE_QINGYA)) +ifneq ($(strip $(PLAT)),ASR) +ifneq ($(strip $(PLAT)),ASR_1606) +INC += -I$(ROOT)/components/decode/qingya +endif +endif +endif + +ifeq ($(CONFIG_SPI_SDCARD), y) +INC += -I$(ROOT)/components/fs/fatfs/include +endif + +ifeq ($(CONFIG_AUDIO_G711_COMPRESS), y) +INC += -I$(ROOT)/components/audio/G711 +endif +ifeq ($(strip $(PLAT)),EIGEN) +INC += -I$(ROOT)/components/audio +endif + +ifeq ($(CONFIG_GMSSL), y) +INC += -I$(ROOT)/components/GMSSL/include +INC += -I$(ROOT)/components/GMSSL/include/gmssl +endif + +ifeq ($(CONFIG_GB2312), y) +INC += -I$(ROOT)/components/decode/gb2312 +endif + +ifeq ($(CONFIG_ESIMSDK), y) +INC += -I$(ROOT)/components/esim/inc +endif + +ifeq ($(CONFIG_ESIM_IPA), y) +INC += -I$(ROOT)/system/esim +endif + +ifeq ($(CONFIG_AUDIO_G729_COMPRESS), y) +INC += -I$(ROOT)/components/audio/G729 +INC += -I$(ROOT)/components/audio/G729/src +INC += -I$(ROOT)/components/audio/G729/include +endif + +ifeq ($(CONFIG_NFC_SL6320), y) +INC += -I$(ROOT)/components/NFC/SL6320 +endif + +ifeq ($(CONFIG_VOIP), y) +INC += -I$(ROOT)/components/voip/libexosip2/include +INC += -I$(ROOT)/components/voip/libosip2/include +INC += -I$(ROOT)/components/voip/rtp +endif + +ifeq ($(CONFIG_ETHERNET_W5500), y) +INC += -I$(ROOT)/components/ethernet/wiznet +endif + +ifeq ($(CONFIG_SLIP), y) +INC += -I$(ROOT)/components/slip +endif + +ifeq ($(MICROPY_VFS_QUECFS),1) +QUEC_MOD_CFLAGS += -DMICROPY_VFS_QUECFS=1 +endif + +QUEC_SRC += \ + main.c \ + mphalport.c \ + mpthreadport.c \ + help.c + +# modules source file will used by gen +QUEC_SRC_MOD += \ + gccollect.c \ + moduos.c \ + modutime.c \ + modsocket.c \ + modostimer.c \ + modfota.c \ + callbackdeal.c \ + modmachine.c \ + machine_pin.c \ + machine_wdt.c \ + machine_rtc.c \ + machine_uart.c \ + machine_timer.c \ + moddev.c \ + misc_power.c \ + modmisc.c \ + misc_usbnet.c \ + modsim.c \ + modnet.c \ + moddatacall.c \ + machine_extint.c \ + misc_adc.c \ + misc_pwm.c \ + modexample.c \ + machine_iic.c \ + machine_hw_spi.c \ + utime_mphal.c \ + modflashdev.c \ + modussl_mbedtls.c \ + modhelios.c diff --git a/ports/quectel/utime_mphal.c b/ports/quectel/utime_mphal.c new file mode 100644 index 0000000000000..a208971ee50e5 --- /dev/null +++ b/ports/quectel/utime_mphal.c @@ -0,0 +1,110 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. + * + * 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_UTIME_MP_HAL + +#include + +#include "py/obj.h" +#include "py/mphal.h" +#include "py/smallint.h" +#include "py/runtime.h" +#include "utime_mphal.h" + +static mp_obj_t time_sleep(mp_obj_t seconds_o) { + #if MICROPY_PY_BUILTINS_FLOAT + mp_hal_delay_ms((mp_uint_t)(1000 * mp_obj_get_float(seconds_o))); + #else + mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o)); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep); + +static mp_obj_t time_sleep_ms(mp_obj_t arg) { + mp_int_t ms = mp_obj_get_int(arg); + if (ms > 0) { + mp_hal_delay_ms(ms); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj, time_sleep_ms); + +static mp_obj_t time_sleep_us(mp_obj_t arg) { + mp_int_t us = mp_obj_get_int(arg); + if (us > 0) { + mp_hal_delay_us(us); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj, time_sleep_us); + +static mp_obj_t time_ticks_ms(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj, time_ticks_ms); + +static mp_obj_t time_ticks_us(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_us() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj, time_ticks_us); + +static mp_obj_t time_ticks_cpu(void) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_cpu() & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj, time_ticks_cpu); + +static mp_obj_t time_ticks_diff(mp_obj_t end_in, mp_obj_t start_in) { + // we assume that the arguments come from ticks_xx so are small ints + mp_uint_t start = mp_obj_get_int_truncated(start_in); + mp_uint_t end = mp_obj_get_int_truncated(end_in); + //mp_obj_get_int_truncated + // Optimized formula avoiding if conditions. We adjust difference "forward", + // wrap it around and adjust back. + mp_int_t diff = ((end - start + MICROPY_PY_TIME_TICKS_PERIOD / 2) & (MICROPY_PY_TIME_TICKS_PERIOD - 1)) + - MICROPY_PY_TIME_TICKS_PERIOD / 2; + return MP_OBJ_NEW_SMALL_INT(diff); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj, time_ticks_diff); + +static mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { + // we assume that first argument come from ticks_xx so is small int + mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in); + mp_uint_t delta = mp_obj_get_int(delta_in); + return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_TIME_TICKS_PERIOD - 1)); +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add); + +// Returns the number of nanoseconds since the Epoch, as an integer. +static mp_obj_t time_time_ns(void) { + return mp_obj_new_int_from_ull(mp_hal_time_ns()); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_time_ns_obj, time_time_ns); + +#endif // MICROPY_PY_UTIME_MP_HAL diff --git a/ports/quectel/utime_mphal.h b/ports/quectel/utime_mphal.h new file mode 100644 index 0000000000000..5bb1d493b1fcd --- /dev/null +++ b/ports/quectel/utime_mphal.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H +#define MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H + +#include "py/obj.h" + +MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_utime_sleep_us_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_ms_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_time_ns_obj); + +#endif // MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy