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