From e32302c1a62f5b0736826bc3e241a705097ba223 Mon Sep 17 00:00:00 2001 From: Olivier Ortigues Date: Wed, 22 Apr 2020 13:57:54 +0200 Subject: [PATCH 001/352] esp8266/esppwm: Fix PWM glitch when setting duty on different channel. The PWM driver uses a double buffer for the PWM timing array, one in current use and the other one to update when changing duty parameters. The issue was that once the duty parameters were changed the updated buffer was applied immediately without synchronising to the start of the PWM period. By moving the buffer toggling/swapping to the interrupt when the cycle is done there are no more glitches. --- ports/esp8266/esppwm.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ports/esp8266/esppwm.c b/ports/esp8266/esppwm.c index 6116a468957fa..d5bcea9acd8cc 100644 --- a/ports/esp8266/esppwm.c +++ b/ports/esp8266/esppwm.c @@ -57,6 +57,7 @@ STATIC uint8_t pwm_timer_down = 1; STATIC uint8_t pwm_current_channel = 0; STATIC uint16_t pwm_gpio = 0; STATIC uint8_t pwm_channel_num = 0; +STATIC volatile uint8_t pwm_toggle_request = 0; // XXX: 0xffffffff/(80000000/16)=35A #define US_TO_RTC_TIMER_TICKS(t) \ @@ -126,6 +127,9 @@ pwm_start(void) { LOCK_PWM(critical); // enter critical + // if a toggle is pending, we reset it since we're changing the settings again + pwm_toggle_request = 0; + struct pwm_single_param *local_single = pwm_single_toggle[pwm_toggle ^ 0x01]; uint8 *local_channel = &pwm_channel_toggle[pwm_toggle ^ 0x01]; @@ -188,14 +192,14 @@ pwm_start(void) { // start gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0); + // do the first toggle because timer has to have a valid set to do it's job + pwm_toggle ^= 0x01; + pwm_timer_down = 0; RTC_REG_WRITE(FRC1_LOAD_ADDRESS, local_single[0].h_time); - } - - if (pwm_toggle == 1) { - pwm_toggle = 0; } else { - pwm_toggle = 1; + // request pwm_tim1_intr_handler to swap the timing buffers + pwm_toggle_request = 1; } UNLOCK_PWM(critical); // leave critical @@ -297,12 +301,18 @@ pwm_get_freq(uint8 channel) { STATIC void ICACHE_RAM_ATTR pwm_tim1_intr_handler(void *dummy) { (void)dummy; - uint8 local_toggle = pwm_toggle; // pwm_toggle may change outside + RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK); if (pwm_current_channel >= (*pwm_channel - 1)) { // *pwm_channel may change outside - pwm_single = pwm_single_toggle[local_toggle]; - pwm_channel = &pwm_channel_toggle[local_toggle]; + + if (pwm_toggle_request != 0) { + pwm_toggle ^= 1; + pwm_toggle_request = 0; + } + + pwm_single = pwm_single_toggle[pwm_toggle]; + pwm_channel = &pwm_channel_toggle[pwm_toggle]; gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set, pwm_single[*pwm_channel - 1].gpio_clear, From b3bc9808f2d1396a946ae899b7d81d043eac1ddd Mon Sep 17 00:00:00 2001 From: Albort Xue Date: Thu, 7 May 2020 16:47:48 +0800 Subject: [PATCH 002/352] mimxrt/boards: Add MIMXRT1060_EVK board. --- .../evkmimxrt1060_flexspi_nor_config.h | 268 ++++++++++++++++++ .../boards/MIMXRT1060_EVK/flash_config.c | 48 ++++ .../boards/MIMXRT1060_EVK/mpconfigboard.h | 8 + .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 7 + 4 files changed, 331 insertions(+) create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h new file mode 100644 index 0000000000000..8bb771fd8ad44 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h @@ -0,0 +1,268 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1060_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1060_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.0. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_120MHz = 7, + kFlexSpiSerialClk_133MHz = 8, + kFlexSpiSerialClk_166MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1060_FLEXSPI_NOR_CONFIG__ */ diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c b/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c new file mode 100644 index 0000000000000..095a922e9efe1 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c @@ -0,0 +1,48 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "evkmimxrt1060_flexspi_nor_config.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_100MHz, + .sflashA1Size = 8u * 1024u * 1024u, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 64u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h new file mode 100644 index 0000000000000..714f987a54c9d --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -0,0 +1,8 @@ +#define MICROPY_HW_BOARD_NAME "i.MX RT1060 EVK" +#define MICROPY_HW_MCU_NAME "MIMXRT1062DVJ6A" + +#define BOARD_FLASH_SIZE (8 * 1024 * 1024) + +#define MICROPY_HW_LED_PINMUX IOMUXC_GPIO_AD_B0_09_GPIO1_IO09 +#define MICROPY_HW_LED_PORT GPIO1 +#define MICROPY_HW_LED_PIN 9 diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk new file mode 100644 index 0000000000000..b34169801b900 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -0,0 +1,7 @@ +MCU_SERIES = MIMXRT1062 +MCU_VARIANT = MIMXRT1062DVJ6A + +JLINK_PATH ?= /media/RT1060-EVK/ + +deploy: $(BUILD)/firmware.bin + cp $< $(JLINK_PATH) From 25bc42e754e76f7dd45048bcfb139fdba22e3e29 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 13:08:35 +0930 Subject: [PATCH 003/352] powerpc: Fix Makefile rule when linking. The linker script was included in the "$^" inputs, causing the build to fail: LINK build/firmware.elf powerpc64le-linux-gnu-ld: error: linker script file 'powerpc.lds' appears multiple times As a fix the linker script is left as a dependency of the elf, but only the object files are linked. --- ports/powerpc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/powerpc/Makefile b/ports/powerpc/Makefile index 7aa4f1449c508..2e4dd0fd0261c 100644 --- a/ports/powerpc/Makefile +++ b/ports/powerpc/Makefile @@ -52,7 +52,7 @@ $(BUILD)/_frozen_mpy.c: frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h $(BUILD)/firmware.elf: $(OBJ) powerpc.lds $(ECHO) "LINK $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(SIZE) $@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf From b65482ffa8715f3eeeb593e26d96d80bd77e0dfd Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 13:07:09 +0930 Subject: [PATCH 004/352] powerpc: Set better default compiler. Most developers use a compiler which is called powerpc64le-linux-gnu-gcc. --- ports/powerpc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/powerpc/Makefile b/ports/powerpc/Makefile index 2e4dd0fd0261c..5f17a93557e5c 100644 --- a/ports/powerpc/Makefile +++ b/ports/powerpc/Makefile @@ -9,7 +9,7 @@ include $(TOP)/py/py.mk ARCH = $(shell uname -m) ifneq ("$(ARCH)", "ppc64") ifneq ("$(ARCH)", "ppc64le") - CROSS_COMPILE ?= powerpc64le-linux- + CROSS_COMPILE ?= powerpc64le-linux-gnu- endif endif From a9d96499b8f7310b9e43b4a8cda2f6e17cbd4248 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 13:24:19 +0930 Subject: [PATCH 005/352] travis: Run apt commands once, to slightly speed up the CI. --- .travis.yml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5460bf601f484..84f58625640bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,8 +64,7 @@ jobs: # need newer gcc version for Cortex-M7 support - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa - sudo apt-get update -qq || true - - sudo apt-get install gcc-arm-embedded - - sudo apt-get install libnewlib-arm-none-eabi + - sudo apt-get install gcc-arm-embedded libnewlib-arm-none-eabi - arm-none-eabi-gcc --version script: - make ${MAKEOPTS} -C mpy-cross @@ -86,9 +85,7 @@ jobs: dist: bionic # needed for more recent version of qemu-system-arm with mps2-an385 target env: NAME="qemu-arm port build and tests" install: - - sudo apt-get install gcc-arm-none-eabi - - sudo apt-get install libnewlib-arm-none-eabi - - sudo apt-get install qemu-system + - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi qemu-system - arm-none-eabi-gcc --version - qemu-system-arm --version script: @@ -304,9 +301,7 @@ jobs: - stage: test env: NAME="bare-arm and minimal ports build and size-diff check" install: - - sudo apt-get install gcc-multilib libffi-dev:i386 - - sudo apt-get install gcc-arm-none-eabi - - sudo apt-get install libnewlib-arm-none-eabi + - sudo apt-get install gcc-multilib libffi-dev:i386 gcc-arm-none-eabi libnewlib-arm-none-eabi - gcc --version - arm-none-eabi-gcc --version script: @@ -332,8 +327,7 @@ jobs: - stage: test env: NAME="cc3200 port build" install: - - sudo apt-get install gcc-arm-none-eabi - - sudo apt-get install libnewlib-arm-none-eabi + - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: - make ${MAKEOPTS} -C ports/cc3200 BTARGET=application BTYPE=release - make ${MAKEOPTS} -C ports/cc3200 BTARGET=bootloader BTYPE=release @@ -342,8 +336,7 @@ jobs: - stage: test env: NAME="samd port build" install: - - sudo apt-get install gcc-arm-none-eabi - - sudo apt-get install libnewlib-arm-none-eabi + - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: - make ${MAKEOPTS} -C ports/samd submodules - make ${MAKEOPTS} -C ports/samd @@ -352,8 +345,7 @@ jobs: - stage: test env: NAME="teensy port build" install: - - sudo apt-get install gcc-arm-none-eabi - - sudo apt-get install libnewlib-arm-none-eabi + - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: - make ${MAKEOPTS} -C ports/teensy @@ -361,7 +353,6 @@ jobs: - stage: test env: NAME="powerpc port build" install: - - sudo apt-get install gcc-powerpc64le-linux-gnu - - sudo apt-get install libc6-dev-ppc64el-cross + - sudo apt-get install gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross script: - make ${MAKEOPTS} -C ports/powerpc CROSS_COMPILE=powerpc64le-linux-gnu- From d6803067c0a15c03af0639aa02b68898a3f9cc81 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 13:27:33 +0930 Subject: [PATCH 006/352] travis: Set build name so it appears in the web interfaces. The env NAME="foo" syntax doesn't appear to set the name in the Travis web interface. Instead use the syntax from the docs: https://docs.travis-ci.com/user/build-stages/#naming-your-jobs-within-build-stages --- .travis.yml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84f58625640bc..133035949d484 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ jobs: - stage: test os: linux dist: bionic - env: NAME="code formatting" + name: "code formatting" before_install: - sudo apt-add-repository --yes --update ppa:pybricks/ppa install: @@ -41,8 +41,8 @@ jobs: - stage: test os: osx osx_image: xcode11.3 + name: "unix port build with clang on OSX" env: - - NAME="unix port build with clang on OSX" - PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig script: - make ${MAKEOPTS} -C mpy-cross @@ -59,7 +59,7 @@ jobs: # stm32 port - stage: test - env: NAME="stm32 port build" + name: "stm32 port build" install: # need newer gcc version for Cortex-M7 support - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa @@ -83,7 +83,7 @@ jobs: # qemu-arm port - stage: test dist: bionic # needed for more recent version of qemu-system-arm with mps2-an385 target - env: NAME="qemu-arm port build and tests" + name: "qemu-arm port build and tests" install: - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi qemu-system - arm-none-eabi-gcc --version @@ -96,7 +96,7 @@ jobs: # unix coverage - stage: test - env: NAME="unix coverage build and tests" + name: "unix coverage build and tests" install: - sudo apt-get install python3-pip - sudo pip install cpp-coveralls @@ -131,7 +131,7 @@ jobs: # unix coverage 32-bit - stage: test - env: NAME="unix coverage 32-bit build and tests" + name: "unix coverage 32-bit build and tests" install: - sudo apt-get install gcc-multilib libffi-dev:i386 - sudo apt-get install python3-pip @@ -163,7 +163,7 @@ jobs: # standard unix port - stage: test - env: NAME="unix port build and tests" + name: "unix port build and tests" script: - make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/unix submodules @@ -176,7 +176,7 @@ jobs: # unix nanbox/float (and using Python 2 to check it can run the build scripts) - stage: test - env: NAME="unix nanbox/float port build and tests" + name: "unix nanbox/float port build and tests" install: - sudo apt-get install gcc-multilib libffi-dev:i386 script: @@ -193,7 +193,7 @@ jobs: # unix stackless/float with clang - stage: test - env: NAME="unix stackless/float port build and tests with clang" + name: "unix stackless/float port build and tests with clang" install: - sudo apt-get install clang script: @@ -209,7 +209,7 @@ jobs: # unix with sys.settrace - stage: test - env: NAME="unix port with sys.settrace build and tests" + name: "unix port with sys.settrace build and tests" script: - make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/unix MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1" test || travis_terminate 1 @@ -220,7 +220,7 @@ jobs: # minimal unix port with tests - stage: test - env: NAME="minimal unix port build and tests" + name: "minimal unix port build and tests" script: - make ${MAKEOPTS} -C ports/unix VARIANT=minimal - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython-minimal ./run-tests -e exception_chain -e self_type_check -e subclass_native_init -d basics) @@ -229,7 +229,7 @@ jobs: # windows port via mingw - stage: test - env: NAME="windows port build via mingw" + name: "windows port build via mingw" install: - sudo apt-get install gcc-mingw-w64 script: @@ -238,7 +238,7 @@ jobs: # esp32 w/ESP-IDFv3 port - stage: test - env: NAME="esp32 ESP-IDFv3 port build" + name: "esp32 ESP-IDFv3 port build" install: - sudo apt-get install python3-pip - sudo pip3 install 'pyparsing<2.4' @@ -255,7 +255,7 @@ jobs: # esp32 w/ESP-IDFv4 port - stage: test - env: NAME="esp32 ESP-IDFv4 port build" + name: "esp32 ESP-IDFv4 port build" install: - sudo apt-get install python3-pip - sudo pip3 install 'pyparsing<2.4' @@ -272,7 +272,7 @@ jobs: # esp8266 port - stage: test - env: NAME="esp8266 port build" + name: "esp8266 port build" install: - wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz - zcat xtensa-lx106-elf-standalone.tar.gz | tar x @@ -286,7 +286,7 @@ jobs: # nrf port - stage: test - env: NAME="nrf port build" + name: "nrf port build" install: - sudo apt-get install gcc-arm-none-eabi - sudo apt-get install libnewlib-arm-none-eabi @@ -299,7 +299,7 @@ jobs: # bare-arm and minimal ports, with size-diff check - stage: test - env: NAME="bare-arm and minimal ports build and size-diff check" + name: "bare-arm and minimal ports build and size-diff check" install: - sudo apt-get install gcc-multilib libffi-dev:i386 gcc-arm-none-eabi libnewlib-arm-none-eabi - gcc --version @@ -325,7 +325,7 @@ jobs: # cc3200 port - stage: test - env: NAME="cc3200 port build" + name: "cc3200 port build" install: - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: @@ -334,7 +334,7 @@ jobs: # samd port - stage: test - env: NAME="samd port build" + name: "samd port build" install: - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: @@ -343,7 +343,7 @@ jobs: # teensy port - stage: test - env: NAME="teensy port build" + name: "teensy port build" install: - sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi script: @@ -351,7 +351,7 @@ jobs: # powerpc port - stage: test - env: NAME="powerpc port build" + name: "powerpc port build" install: - sudo apt-get install gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross script: From 4bbba3060d451d4dfa147ebc793acd1cc7364e5a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 27 May 2020 17:53:31 +1000 Subject: [PATCH 007/352] lib/utils: Lock the scheduler when executing hard callback functions. Otherwise scheduled functions may execute during the hard callback and then fail if they try to allocate heap memory. --- lib/utils/mpirq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/utils/mpirq.c b/lib/utils/mpirq.c index d04fab68bbb52..8de13b0b6a3c8 100644 --- a/lib/utils/mpirq.c +++ b/lib/utils/mpirq.c @@ -62,8 +62,10 @@ mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { void mp_irq_handler(mp_irq_obj_t *self) { if (self->handler != mp_const_none) { if (self->ishard) { - // When executing code within a handler we must lock the GC to prevent - // any memory allocations. + // 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) { @@ -77,6 +79,7 @@ void mp_irq_handler(mp_irq_obj_t *self) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } gc_unlock(); + mp_sched_unlock(); } else { // Schedule call to user function mp_sched_schedule(self->handler, self->parent); From a902b69dd51de0e3fe3bb6955296591d6a93abab Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 21 May 2020 11:27:09 +1000 Subject: [PATCH 008/352] py/py.mk: Use additional CFLAGS to compile string0.c. Otherwise functions like memset might get optimised to call themselves (eg with gcc 10). And provide CFLAGS_BUILTIN so these options can be changed by a port if needed. Fixes issue #6053. --- py/py.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py/py.mk b/py/py.mk index 0a98ccc00ca0f..d864a7ed3d843 100644 --- a/py/py.mk +++ b/py/py.mk @@ -266,6 +266,11 @@ $(HEADER_BUILD)/moduledefs.h: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER SRC_QSTR += $(HEADER_BUILD)/moduledefs.h +# Standard C functions like memset need to be compiled with special flags so +# the compiler does not optimise these functions in terms of themselves. +CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto +$(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) + # Force nlr code to always be compiled with space-saving optimisation so # that the function preludes are of a minimal and predictable form. $(PY_BUILD)/nlr%.o: CFLAGS += -Os From 81db22f693d06468d45571a29fc0648a8f5664ce Mon Sep 17 00:00:00 2001 From: stijn Date: Sun, 17 May 2020 12:29:25 +0200 Subject: [PATCH 009/352] py/modmath: Work around msvc float bugs in atan2, fmod and modf. Older implementations deal with infinity/negative zero incorrectly. This commit adds generic fixes that can be enabled by any port that needs them, along with new tests cases. --- ports/windows/mpconfigport.h | 8 ++++++++ py/modmath.c | 27 ++++++++++++++++++++++++++- py/mpconfig.h | 15 +++++++++++++++ tests/float/math_domain.py | 4 ++-- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 84c196e11fa9c..fa09dda7527e0 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -227,6 +227,14 @@ extern const struct _mp_obj_module_t mp_module_time; #define MP_SSIZE_MAX _I32_MAX #endif +// VC++ 12.0 fixes +#if (_MSC_VER <= 1800) +#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (1) +#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1) +#ifdef _WIN64 +#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1) +#endif +#endif // CL specific definitions diff --git a/py/modmath.c b/py/modmath.c index 5ff892ba112c8..b312eeb3d2c01 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -34,6 +34,8 @@ // M_PI is not part of the math.h standard and may not be defined // And by defining our own we can ensure it uses the correct const format. #define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) +#define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962) +#define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885) STATIC NORETURN void math_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); @@ -132,7 +134,17 @@ MATH_FUN_1(asin, asin) // atan(x) MATH_FUN_1(atan, atan) // atan2(y, x) +#if MICROPY_PY_MATH_ATAN2_FIX_INFNAN +mp_float_t atan2_func(mp_float_t x, mp_float_t y) { + if (isinf(x) && isinf(y)) { + return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x); + } + return atan2(x, y); +} +MATH_FUN_2(atan2, atan2_func) +#else MATH_FUN_2(atan2, atan2) +#endif // ceil(x) MATH_FUN_1_TO_INT(ceil, ceil) // copysign(x, y) @@ -148,7 +160,14 @@ MATH_FUN_1(fabs, fabs_func) // floor(x) MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float // fmod(x, y) +#if MICROPY_PY_MATH_FMOD_FIX_INFNAN +mp_float_t fmod_func(mp_float_t x, mp_float_t y) { + return (!isinf(x) && isinf(y)) ? x : fmod(x, y); +} +MATH_FUN_2(fmod, fmod_func) +#else MATH_FUN_2(fmod, fmod) +#endif // isfinite(x) MATH_FUN_1_TO_BOOL(isfinite, isfinite) // isinf(x) @@ -246,7 +265,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp); // modf(x) STATIC mp_obj_t mp_math_modf(mp_obj_t x_obj) { mp_float_t int_part = 0.0; - mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(mp_obj_get_float(x_obj), &int_part); + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part); + #if MICROPY_PY_MATH_MODF_FIX_NEGZERO + if (fractional_part == MICROPY_FLOAT_CONST(0.0)) { + fractional_part = copysign(fractional_part, x); + } + #endif mp_obj_t tuple[2]; tuple[0] = mp_obj_new_float(fractional_part); tuple[1] = mp_obj_new_float(int_part); diff --git a/py/mpconfig.h b/py/mpconfig.h index f2b3af1f2a314..27df3f483a0e1 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1128,6 +1128,21 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_ISCLOSE (0) #endif +// Whether to provide fix for atan2 Inf handling. +#ifndef MICROPY_PY_MATH_ATAN2_FIX_INFNAN +#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (0) +#endif + +// Whether to provide fix for fmod Inf handling. +#ifndef MICROPY_PY_MATH_FMOD_FIX_INFNAN +#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (0) +#endif + +// Whether to provide fix for modf negative zero handling. +#ifndef MICROPY_PY_MATH_MODF_FIX_NEGZERO +#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (0) diff --git a/tests/float/math_domain.py b/tests/float/math_domain.py index 2d4670f75dd48..e63628cf5078a 100644 --- a/tests/float/math_domain.py +++ b/tests/float/math_domain.py @@ -39,8 +39,8 @@ # double argument functions for name, f, args in ( ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))), - ("fmod", math.fmod, ((1.2, inf), (1.2, 0), (inf, 1.2))), - ("atan2", math.atan2, ((0, 0),)), + ("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))), + ("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))), ("copysign", math.copysign, ()), ): for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)): From 97ccde0c431674113f56b4c9d421b2bc1acb1b56 Mon Sep 17 00:00:00 2001 From: stijn Date: Sun, 17 May 2020 10:57:51 +0200 Subject: [PATCH 010/352] py/ringbuf: Fix compilation with msvc. Older versions do not have "inline" so fetch the definition from mpconfigport.h. --- py/ringbuf.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/ringbuf.h b/py/ringbuf.h index 4d316d961b2b5..293e418306051 100644 --- a/py/ringbuf.h +++ b/py/ringbuf.h @@ -29,6 +29,10 @@ #include #include +#ifdef _MSC_VER +#include "py/mpconfig.h" // For inline. +#endif + typedef struct _ringbuf_t { uint8_t *buf; uint16_t size; From 9523ca92e03e699494560ec964bea748b291c7a0 Mon Sep 17 00:00:00 2001 From: stijn Date: Sun, 17 May 2020 09:39:59 +0200 Subject: [PATCH 011/352] windows: Make appveyor.yml self-contained. Add configuration which otherwise has to be set via the UI so the file is more self-contained, and remove configuration which is not needed because it's the same as the default. The major change here is that for a while now Appveyor has been using Visual Studio 2015 by default while we still want to support 2013. --- ports/windows/.appveyor.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index 2e0dbdea5d116..2965543b05942 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -1,3 +1,7 @@ +image: Visual Studio 2013 +clone_depth: 1 +skip_tags: true + environment: # Python version used MICROPY_CPYTHON3: c:/python34/python.exe @@ -71,10 +75,3 @@ after_test: if ($LASTEXITCODE -ne 0) { throw "$env:MSYSTEM mpy-cross tests exited with code $LASTEXITCODE" } - -skip_tags: true - -deploy: off - -nuget: - disable_publish_on_pr: true From 093fd807606d088cfaf874e79060a6c68484cbfa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 22 May 2020 11:10:15 -0500 Subject: [PATCH 012/352] py/modsys: Use consistent naming pattern for module-level const objects. This renames a few identifiers to follow the usual naming convention of mp__. This makes them easier to find, e.g. when grep'ing. --- py/modsys.c | 10 +++++----- py/objint.h | 2 +- py/objint_longlong.c | 2 +- py/objint_mpz.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/py/modsys.c b/py/modsys.c index 8092d9799d81f..586b13e747130 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -53,7 +53,7 @@ const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adap #endif // version - Python language version that this implementation conforms to, as a string -STATIC const MP_DEFINE_STR_OBJ(version_obj, "3.4.0"); +STATIC const MP_DEFINE_STR_OBJ(mp_sys_version_obj, "3.4.0"); // version_info - Python language version that this implementation conforms to, as a tuple of ints #define I(n) MP_OBJ_NEW_SMALL_INT(n) @@ -105,7 +105,7 @@ STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = { #ifdef MICROPY_PY_SYS_PLATFORM // platform - the platform that MicroPython is running on -STATIC const MP_DEFINE_STR_OBJ(platform_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM); #endif // exit([retval]): raise SystemExit, with optional argument given to the exception @@ -189,11 +189,11 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&MP_STATE_VM(mp_sys_path_obj)) }, { MP_ROM_QSTR(MP_QSTR_argv), MP_ROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)) }, - { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&version_obj) }, + { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&mp_sys_version_obj) }, { MP_ROM_QSTR(MP_QSTR_version_info), MP_ROM_PTR(&mp_sys_version_info_obj) }, { MP_ROM_QSTR(MP_QSTR_implementation), MP_ROM_PTR(&mp_sys_implementation_obj) }, #ifdef MICROPY_PY_SYS_PLATFORM - { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&platform_obj) }, + { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&mp_sys_platform_obj) }, #endif #if MP_ENDIANNESS_LITTLE { MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_QSTR(MP_QSTR_little) }, @@ -210,7 +210,7 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { // of "one" bits in sys.maxsize. { MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_INT(MP_SMALL_INT_MAX) }, #else - { MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_PTR(&mp_maxsize_obj) }, + { MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_PTR(&mp_sys_maxsize_obj) }, #endif #endif diff --git a/py/objint.h b/py/objint.h index c4752384e04db..5eed87705dedb 100644 --- a/py/objint.h +++ b/py/objint.h @@ -38,7 +38,7 @@ typedef struct _mp_obj_int_t { #endif } mp_obj_int_t; -extern const mp_obj_int_t mp_maxsize_obj; +extern const mp_obj_int_t mp_sys_maxsize_obj; #if MICROPY_PY_BUILTINS_FLOAT mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 619b4d2b54475..f2e88c3ea5643 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -40,7 +40,7 @@ #if MICROPY_PY_SYS_MAXSIZE // Export value for sys.maxsize -const mp_obj_int_t mp_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; +const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) { diff --git a/py/objint_mpz.c b/py/objint_mpz.c index f2f0dbc3a51cf..6e52073a6e85b 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -66,7 +66,7 @@ STATIC const mpz_dig_t maxsize_dig[] = { #endif }; // *FORMAT-ON* -const mp_obj_int_t mp_maxsize_obj = { +const mp_obj_int_t mp_sys_maxsize_obj = { {&mp_type_int}, {.fixed_dig = 1, .len = NUM_DIG, .alloc = NUM_DIG, .dig = (mpz_dig_t *)maxsize_dig} }; From 2d1fef709664f5dc39bd7abac21bc063db90df61 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 26 May 2020 12:49:41 -0500 Subject: [PATCH 013/352] tools/codeformat.py: Use -q option on uncrustify to make output quiet. This suppresses the Parsing: as language C lines. This makes parsing run a bit faster and on CI it makes for less scrolling through logs (and black already uses the -q option). --- tools/codeformat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/codeformat.py b/tools/codeformat.py index 0a8bf2e0f4e8c..2fe72ef33d570 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -168,7 +168,7 @@ def batch(cmd, files, N=200): # Format C files with uncrustify. if format_c: - batch(["uncrustify", "-c", UNCRUSTIFY_CFG, "-lC", "--no-backup"], lang_files(C_EXTS)) + batch(["uncrustify", "-q", "-c", UNCRUSTIFY_CFG, "-lC", "--no-backup"], lang_files(C_EXTS)) for file in lang_files(C_EXTS): fixup_c(file) From 1662a0b06f2ad58aa4a632a88d0b3836c08bded2 Mon Sep 17 00:00:00 2001 From: cccc Date: Tue, 25 Feb 2020 23:09:59 +0800 Subject: [PATCH 014/352] esp32/machine_sdcard: Add "freq" keyword arg to SDCard constructor. To allow high speed access. --- docs/library/machine.SDCard.rst | 4 +++- ports/esp32/machine_sdcard.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/library/machine.SDCard.rst b/docs/library/machine.SDCard.rst index cf86b1fcb25e6..b1cf42ec0285b 100644 --- a/docs/library/machine.SDCard.rst +++ b/docs/library/machine.SDCard.rst @@ -23,7 +23,7 @@ arguments that might need to be set in order to use either a non-standard slot or a non-standard pin assignment. The exact subset of arguments supported will vary from platform to platform. -.. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, cs=None) +.. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, cs=None, freq=20000000) This class provides access to SD or MMC storage cards using either a dedicated SD/MMC interface hardware or through an SPI channel. @@ -50,6 +50,8 @@ vary from platform to platform. - *mosi* can be used to specify an SPI mosi pin. - *cs* can be used to specify an SPI chip select pin. + + - *freq* selects the SD/MMC interface frequency in Hz (only supported on the ESP32). Implementation-specific details ------------------------------- diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c index 0fd4e86211e86..40686508a1e20 100644 --- a/ports/esp32/machine_sdcard.c +++ b/ports/esp32/machine_sdcard.c @@ -126,6 +126,7 @@ STATIC mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args ARG_mosi, ARG_sck, ARG_cs, + ARG_freq, }; STATIC const mp_arg_t allowed_args[] = { { MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, @@ -137,6 +138,8 @@ STATIC mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + // freq is valid for both SPI and SDMMC interfaces + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20000000} }, }; mp_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)]; mp_map_t kw_args; @@ -175,11 +178,14 @@ STATIC mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args self->flags = 0; // Note that these defaults are macros that expand to structure // constants so we can't directly assign them to fields. + int freq = arg_vals[ARG_freq].u_int; if (is_spi) { sdmmc_host_t _temp_host = SDSPI_HOST_DEFAULT(); + _temp_host.max_freq_khz = freq / 1000; self->host = _temp_host; } else { sdmmc_host_t _temp_host = SDMMC_HOST_DEFAULT(); + _temp_host.max_freq_khz = freq / 1000; self->host = _temp_host; } From 50a7ba234850183693d2b236e48ec0b76399123d Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 2 May 2020 01:36:53 -0700 Subject: [PATCH 015/352] esp32/modmachine: Fix machine.reset_cause to use IDF's esp_reset_reason. The code previously called rtc_get_reset_reason which is a "raw" reset cause. The ESP-IDF massages that for the proper reset cause available from esp_reset_reason. Fixes issue #5134. --- ports/esp32/modmachine.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 8d8df995c294b..82a02522f7b41 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -146,36 +146,30 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_ma STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_deepsleep_obj, 0, machine_deepsleep); STATIC mp_obj_t machine_reset_cause(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - switch (rtc_get_reset_reason(0)) { - case POWERON_RESET: + switch (esp_reset_reason()) { + case ESP_RST_POWERON: + case ESP_RST_BROWNOUT: return MP_OBJ_NEW_SMALL_INT(MP_PWRON_RESET); break; - case SW_RESET: - case SW_CPU_RESET: - return MP_OBJ_NEW_SMALL_INT(MP_SOFT_RESET); - break; - case OWDT_RESET: - case TG0WDT_SYS_RESET: - case TG1WDT_SYS_RESET: - case RTCWDT_SYS_RESET: - case RTCWDT_BROWN_OUT_RESET: - case RTCWDT_CPU_RESET: - case RTCWDT_RTC_RESET: - case TGWDT_CPU_RESET: + + case ESP_RST_INT_WDT: + case ESP_RST_TASK_WDT: + case ESP_RST_WDT: return MP_OBJ_NEW_SMALL_INT(MP_WDT_RESET); break; - case DEEPSLEEP_RESET: + case ESP_RST_DEEPSLEEP: return MP_OBJ_NEW_SMALL_INT(MP_DEEPSLEEP_RESET); break; - case EXT_CPU_RESET: + case ESP_RST_SW: + case ESP_RST_PANIC: + case ESP_RST_EXT: // Comment in ESP-IDF: "For ESP32, ESP_RST_EXT is never returned" return MP_OBJ_NEW_SMALL_INT(MP_HARD_RESET); break; - case NO_MEAN: - case SDIO_RESET: - case INTRUSION_RESET: + case ESP_RST_SDIO: + case ESP_RST_UNKNOWN: default: return MP_OBJ_NEW_SMALL_INT(0); break; From f03d030080ccf89c6ad677c818c949c3bd71ec37 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 12:59:55 +0930 Subject: [PATCH 016/352] powerpc/uart: Choose which UART to use at build time, not runtime. Microwatt may have firmware that places data in r3, which was used to detect microwatt vs powernv. This breaks the existing probing of the UART type in this powerpc port. Instead build only the appropriate UART into the firmware, selected by passing the option UART=potato or UART=lpc_serial to the Makefile. A future enhancement would be to parse the device tree and configure MicroPython based on the settings. --- ports/powerpc/Makefile | 7 ++-- ports/powerpc/README.md | 6 ++- ports/powerpc/main.c | 1 - ports/powerpc/uart_core.c | 73 --------------------------------- ports/powerpc/uart_lpc_serial.c | 16 +++++--- ports/powerpc/uart_potato.c | 31 ++++++++------ 6 files changed, 38 insertions(+), 96 deletions(-) delete mode 100644 ports/powerpc/uart_core.c diff --git a/ports/powerpc/Makefile b/ports/powerpc/Makefile index 5f17a93557e5c..1f5ec80d43653 100644 --- a/ports/powerpc/Makefile +++ b/ports/powerpc/Makefile @@ -6,6 +6,9 @@ QSTR_DEFS = qstrdefsport.h # include py core make definitions include $(TOP)/py/py.mk +# potato or lpc_serial +UART ?= potato + ARCH = $(shell uname -m) ifneq ("$(ARCH)", "ppc64") ifneq ("$(ARCH)", "ppc64le") @@ -30,9 +33,7 @@ LIBS = SRC_C = \ main.c \ - uart_core.c \ - uart_potato.c \ - uart_lpc_serial.c \ + uart_$(UART).c \ lib/utils/printf.c \ lib/utils/stdout_helpers.c \ lib/utils/pyexec.c \ diff --git a/ports/powerpc/README.md b/ports/powerpc/README.md index 862bfcd3c5002..631924c7c0afb 100644 --- a/ports/powerpc/README.md +++ b/ports/powerpc/README.md @@ -6,10 +6,14 @@ potato UART. ## Building -By default the port will be built for the host machine: +By default the port will be built with the potato uart for microwatt: $ make +To instead build for a machine with LPC serial, such as QEMU powernv: + + $ make UART=lpc_serial + ## Cross compilation for POWERPC If you need to cross compilers you'll want to grab a powerpc64le diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index 421f9be714c90..fdeec13abe0a5 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -65,7 +65,6 @@ int main(int argc, char **argv) { int stack_dummy; stack_top = (char *)&stack_dummy; - // microwatt has argc/r3 = 0 whereas QEMU has r3 set in head.S uart_init_ppc(argc); #if MICROPY_ENABLE_PYSTACK diff --git a/ports/powerpc/uart_core.c b/ports/powerpc/uart_core.c deleted file mode 100644 index b0dcd031a5ad2..0000000000000 --- a/ports/powerpc/uart_core.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019, Michael Neuling, IBM Corporation. - * - * 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/mpconfig.h" -#include "uart_potato.h" -#include "uart_lpc_serial.h" - -static int lpc_console; -static int potato_console; - -void uart_init_ppc(int lpc) { - lpc_console = lpc; - - if (!lpc_console) { - potato_console = 1; - - potato_uart_init(); - } else { - lpc_uart_init(); - } -} - -// Receive single character -int mp_hal_stdin_rx_chr(void) { - unsigned char c = 0; - if (lpc_console) { - c = lpc_uart_read(); - } else if (potato_console) { - c = potato_uart_read(); - } - return c; -} - -// Send string of given length -void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { - if (lpc_console) { - int i; - for (i = 0; i < len; i++) { - lpc_uart_write(str[i]); - } - } else if (potato_console) { - int i; - for (i = 0; i < len; i++) { - potato_uart_write(str[i]); - } - } -} diff --git a/ports/powerpc/uart_lpc_serial.c b/ports/powerpc/uart_lpc_serial.c index c15fc049b9d5a..760615041cf94 100644 --- a/ports/powerpc/uart_lpc_serial.c +++ b/ports/powerpc/uart_lpc_serial.c @@ -96,20 +96,24 @@ static int lpc_uart_rx_empty(void) { return !(lpc_uart_reg_read(REG_LSR) & LSR_DR); } -void lpc_uart_init(void) { +void uart_init_ppc(void) { lpc_uart_base = LPC_UART_BASE; } -char lpc_uart_read(void) { +int mp_hal_stdin_rx_chr(void) { while (lpc_uart_rx_empty()) { ; } return lpc_uart_reg_read(REG_THR); } -void lpc_uart_write(char c) { - while (lpc_uart_tx_full()) { - ; + +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + int i; + for (i = 0; i < len; i++) { + while (lpc_uart_tx_full()) { + ; + } + lpc_uart_reg_write(REG_RBR, str[i]); } - lpc_uart_reg_write(REG_RBR, c); } diff --git a/ports/powerpc/uart_potato.c b/ports/powerpc/uart_potato.c index 4a487f30322da..29f21934ddf44 100644 --- a/ports/powerpc/uart_potato.c +++ b/ports/powerpc/uart_potato.c @@ -34,10 +34,12 @@ #include #include "py/mpconfig.h" -#define PROC_FREQ 50000000 +#define SYSCON_BASE 0xc0000000 /* System control regs */ +#define SYS_REG_CLKINFO 0x20 + #define UART_FREQ 115200 #define POTATO_UART_BASE 0xc0002000 -uint64_t potato_uart_base; +static uint64_t potato_uart_base; #define POTATO_CONSOLE_TX 0x00 #define POTATO_CONSOLE_RX 0x08 @@ -60,7 +62,7 @@ static uint64_t potato_uart_reg_read(int offset) { return val; } -void potato_uart_reg_write(int offset, uint64_t val) { +static void potato_uart_reg_write(int offset, uint64_t val) { uint64_t addr; addr = potato_uart_base + offset; @@ -96,13 +98,16 @@ static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long return proc_freq / (uart_freq * 16) - 1; } -void potato_uart_init(void) { +void uart_init_ppc(void) { + uint64_t proc_freq; + potato_uart_base = POTATO_UART_BASE; - potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ)); + proc_freq = *(volatile uint64_t *)(SYSCON_BASE + SYS_REG_CLKINFO); + potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(proc_freq, UART_FREQ)); } -char potato_uart_read(void) { +int mp_hal_stdin_rx_chr(void) { uint64_t val; while (potato_uart_rx_empty()) { @@ -113,13 +118,15 @@ char potato_uart_read(void) { return (char)(val & 0x000000ff); } -void potato_uart_write(char c) { - uint64_t val; +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + int i; - val = c; + for (i = 0; i < len; i++) { + uint64_t val = str[i]; - while (potato_uart_tx_full()) { - ; + while (potato_uart_tx_full()) { + ; + } + potato_uart_reg_write(POTATO_CONSOLE_TX, val); } - potato_uart_reg_write(POTATO_CONSOLE_TX, val); } From 5cfc09ffca3203eb474aec478b8baf1f35fd2a0b Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 May 2020 13:18:41 +0930 Subject: [PATCH 017/352] travis: For powerpc job, build both UART variants. The powerpc port can be built with two different UART drivers, so build both in CI. The default compiler is now powerpc64le-linux-gnu- so it does not need to be specified on the command line. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 133035949d484..414fe2b59d108 100644 --- a/.travis.yml +++ b/.travis.yml @@ -355,4 +355,5 @@ jobs: install: - sudo apt-get install gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross script: - - make ${MAKEOPTS} -C ports/powerpc CROSS_COMPILE=powerpc64le-linux-gnu- + - make ${MAKEOPTS} -C ports/powerpc UART=potato + - make ${MAKEOPTS} -C ports/powerpc UART=lpc_serial From 8f642677f724e725813155f74e5934b8c94fc1c4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 27 May 2020 11:10:40 -0500 Subject: [PATCH 018/352] tools/codeformat.py: Add verbose option to pass to uncrustify and black. This adds a new command line option `-v` to `tools/codeformat.py` to enable verbose printing of all files that are scanned. Normally `uncrustify` and `black` are called with the `-q` option so setting verbose suppresses the `-q` option and on `black` also enables the `-v` option which makes it print out all file names matching the filter similar to how `uncrustify` does by default. --- tools/codeformat.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/codeformat.py b/tools/codeformat.py index 2fe72ef33d570..5aef4ccfbf242 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -138,6 +138,7 @@ def main(): cmd_parser = argparse.ArgumentParser(description="Auto-format C and Python files.") cmd_parser.add_argument("-c", action="store_true", help="Format C code only") cmd_parser.add_argument("-p", action="store_true", help="Format Python code only") + cmd_parser.add_argument("-v", action="store_true", help="Enable verbose output") cmd_parser.add_argument("files", nargs="*", help="Run on specific globs") args = cmd_parser.parse_args() @@ -168,13 +169,21 @@ def batch(cmd, files, N=200): # Format C files with uncrustify. if format_c: - batch(["uncrustify", "-q", "-c", UNCRUSTIFY_CFG, "-lC", "--no-backup"], lang_files(C_EXTS)) + command = ["uncrustify", "-c", UNCRUSTIFY_CFG, "-lC", "--no-backup"] + if not args.v: + command.append("-q") + batch(command, lang_files(C_EXTS)) for file in lang_files(C_EXTS): fixup_c(file) # Format Python files with black. if format_py: - batch(["black", "-q", "--fast", "--line-length=99"], lang_files(PY_EXTS)) + command = ["black", "--fast", "--line-length=99"] + if args.v: + command.append("-v") + else: + command.append("-q") + batch(command, lang_files(PY_EXTS)) if __name__ == "__main__": From 22806ed5df27c10131af0cedb2f7e8b134fe6e7a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 May 2020 12:50:44 +1000 Subject: [PATCH 019/352] extmod/vfs: Retain previous working directory if chdir fails. Fixes issue #6069. --- extmod/vfs.c | 3 ++- tests/extmod/vfs_basic.py | 20 +++++++++++++++++++- tests/extmod/vfs_basic.py.exp | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index 79a8e8509ded1..d1291068ad8d6 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -322,7 +322,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open); mp_obj_t mp_vfs_chdir(mp_obj_t path_in) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); - MP_STATE_VM(vfs_cur) = vfs; if (vfs == MP_VFS_ROOT) { // If we change to the root dir and a VFS is mounted at the root then // we must change that VFS's current dir to the root dir so that any @@ -334,9 +333,11 @@ mp_obj_t mp_vfs_chdir(mp_obj_t path_in) { break; } } + vfs = MP_VFS_ROOT; } else { mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out); } + MP_STATE_VM(vfs_cur) = vfs; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir); diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py index c8e203b3df882..62b2a277381d6 100644 --- a/tests/extmod/vfs_basic.py +++ b/tests/extmod/vfs_basic.py @@ -10,8 +10,9 @@ class Filesystem: - def __init__(self, id): + def __init__(self, id, fail=0): self.id = id + self.fail = fail def mount(self, readonly, mkfs): print(self.id, "mount", readonly, mkfs) @@ -25,6 +26,8 @@ def ilistdir(self, dir): def chdir(self, dir): print(self.id, "chdir", dir) + if self.fail: + raise OSError(self.fail) def getcwd(self): print(self.id, "getcwd") @@ -158,3 +161,18 @@ def open(self, file, mode): uos.umount("/") print(uos.listdir("/")) uos.umount("/mnt") + +# chdir to a non-existent mount point (current directory should remain unchanged) +try: + uos.chdir("/foo") +except OSError: + print("OSError") +print(uos.getcwd()) + +# chdir to a non-existent subdirectory in a mounted filesystem +uos.mount(Filesystem(5, 1), "/mnt") +try: + uos.chdir("/mnt/subdir") +except OSError: + print("OSError") +print(uos.getcwd()) diff --git a/tests/extmod/vfs_basic.py.exp b/tests/extmod/vfs_basic.py.exp index 0ae2c2cc975c4..ebca31030451b 100644 --- a/tests/extmod/vfs_basic.py.exp +++ b/tests/extmod/vfs_basic.py.exp @@ -58,3 +58,9 @@ OSError 3 umount ['mnt'] 4 umount +OSError +/ +5 mount False False +5 chdir /subdir +OSError +/ From 88971342b17f48fb91619620f4c4c1b5da40aaa4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:22:03 +1000 Subject: [PATCH 020/352] stm32/machine_uart: Retain attached-to-repl setting when init'ing UART. --- ports/stm32/machine_uart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 232f3629ee6b9..1cf5817e010f7 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -283,11 +283,17 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const // flow control uint32_t flow = args.flow.u_int; + // Save attach_to_repl setting because uart_init will disable it. + bool attach_to_repl = self->attached_to_repl; + // init UART (if it fails, it's because the port doesn't exist) if (!uart_init(self, baudrate, bits, parity, stop, flow)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), self->uart_id); } + // Restore attach_to_repl setting so UART still works if attached to dupterm. + uart_attach_to_repl(self, attach_to_repl); + // set timeout self->timeout = args.timeout.u_int; From 9ae50d22c90f05bb03c6b6e97df7dffa69628fe7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:22:32 +1000 Subject: [PATCH 021/352] stm32/machine_uart: Allow re-init'ing a static UART object. Just disallow changing the rxbuf which will be some static RAM (can't free it and soft-reset would lose any dynamically allocated buffer). --- ports/stm32/machine_uart.c | 39 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 1cf5817e010f7..39a45a2a16060 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -237,11 +237,6 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); - // static UARTs are used for internal purposes and shouldn't be reconfigured - if (self->is_static) { - mp_raise_ValueError(MP_ERROR_TEXT("UART is static and can't be init'd")); - } - // baudrate uint32_t baudrate = args.baudrate.u_int; @@ -306,20 +301,28 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const self->timeout_char = min_timeout_char; } - // setup the read buffer - m_del(byte, self->read_buf, self->read_buf_len << self->char_width); - if (args.rxbuf.u_int >= 0) { - // rxbuf overrides legacy read_buf_len - args.read_buf_len.u_int = args.rxbuf.u_int; - } - if (args.read_buf_len.u_int <= 0) { - // no read buffer - uart_set_rxbuf(self, 0, NULL); + if (self->is_static) { + // Static UARTs have fixed memory for the rxbuf and can't be reconfigured. + if (args.rxbuf.u_int >= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("UART is static and rxbuf can't be changed")); + } + uart_set_rxbuf(self, self->read_buf_len, self->read_buf); } else { - // read buffer using interrupts - size_t len = args.read_buf_len.u_int + 1; // +1 to adjust for usable length of buffer - uint8_t *buf = m_new(byte, len << self->char_width); - uart_set_rxbuf(self, len, buf); + // setup the read buffer + m_del(byte, self->read_buf, self->read_buf_len << self->char_width); + if (args.rxbuf.u_int >= 0) { + // rxbuf overrides legacy read_buf_len + args.read_buf_len.u_int = args.rxbuf.u_int; + } + if (args.read_buf_len.u_int <= 0) { + // no read buffer + uart_set_rxbuf(self, 0, NULL); + } else { + // read buffer using interrupts + size_t len = args.read_buf_len.u_int + 1; // +1 to adjust for usable length of buffer + uint8_t *buf = m_new(byte, len << self->char_width); + uart_set_rxbuf(self, len, buf); + } } // compute actual baudrate that was configured From 68d053c66eb14b29ea16efba89cf01cb879eca3e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:24:48 +1000 Subject: [PATCH 022/352] stm32/modmachine: Allow changing AHB and APB bus frequencies on STM32WB. For now SYSCLK cannot be changed and must remain at 64MHz. --- ports/stm32/modmachine.c | 7 ++++++- ports/stm32/powerctrl.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 0795d2ccf04b8..6fd2488a5ae7b 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -307,7 +307,7 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple); } else { // set - #if defined(STM32F0) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32L0) || defined(STM32L4) mp_raise_NotImplementedError(MP_ERROR_TEXT("machine.freq set not supported yet")); #else mp_int_t sysclk = mp_obj_get_int(args[0]); @@ -317,8 +317,13 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { ahb /= 2; } #endif + #if defined(STM32WB) + mp_int_t apb1 = ahb; + mp_int_t apb2 = ahb; + #else mp_int_t apb1 = ahb / 4; mp_int_t apb2 = ahb / 2; + #endif if (n_args > 1) { ahb = mp_obj_get_int(args[1]); if (n_args > 2) { diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index e11de8b021fd9..946185fba09d3 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -201,7 +201,7 @@ int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk #endif -#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) +#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) STATIC uint32_t calc_ahb_div(uint32_t wanted_div) { #if defined(STM32H7) @@ -293,6 +293,8 @@ STATIC uint32_t calc_apb2_div(uint32_t wanted_div) { #endif } +#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) + int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2) { // Return straightaway if the clocks are already at the desired frequency if (sysclk == HAL_RCC_GetSysClockFreq() @@ -455,8 +457,36 @@ int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t return 0; } +#elif defined(STM32WB) + +int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2) { + // For now it's not supported to change SYSCLK (only bus dividers). + if (sysclk != HAL_RCC_GetSysClockFreq()) { + return -MP_EINVAL; + } + + // Return straightaway if the clocks are already at the desired frequency. + if (ahb == HAL_RCC_GetHCLKFreq() + && apb1 == HAL_RCC_GetPCLK1Freq() + && apb2 == HAL_RCC_GetPCLK2Freq()) { + return 0; + } + + // Calculate and configure the bus clock dividers. + uint32_t cfgr = RCC->CFGR; + cfgr &= ~(7 << RCC_CFGR_PPRE2_Pos | 7 << RCC_CFGR_PPRE1_Pos | 0xf << RCC_CFGR_HPRE_Pos); + cfgr |= calc_ahb_div(sysclk / ahb); + cfgr |= calc_apb1_div(ahb / apb1); + cfgr |= calc_apb2_div(ahb / apb2) << (RCC_CFGR_PPRE2_Pos - RCC_CFGR_PPRE1_Pos); + RCC->CFGR = cfgr; + + return 0; +} + #endif +#endif // !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) + void powerctrl_enter_stop_mode(void) { // Disable IRQs so that the IRQ that wakes the device from stop mode is not // executed until after the clocks are reconfigured From 5210fc51ec251e5331cddd305ae0da012e00af3e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:29:11 +1000 Subject: [PATCH 023/352] stm32/adc: Add support to pyb.ADC for STM32WB MCUs. --- ports/stm32/adc.c | 37 ++++++++++++-------- ports/stm32/boards/stm32l4xx_hal_conf_base.h | 1 + 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index bdd4ec1aeb8e8..3793ef5ff28a2 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -110,14 +110,14 @@ #define ADC_CAL2 ((uint16_t *)(0x1FF1E840)) #define ADC_CAL_BITS (16) -#elif defined(STM32L4) +#elif defined(STM32L4) || defined(STM32WB) #define ADC_FIRST_GPIO_CHANNEL (1) #define ADC_LAST_GPIO_CHANNEL (16) -#define ADC_SCALE_V (3.0f) -#define ADC_CAL_ADDRESS (0x1fff75aa) -#define ADC_CAL1 ((uint16_t *)(ADC_CAL_ADDRESS - 2)) -#define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 0x20)) +#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f) +#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR) +#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR) +#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR) #define ADC_CAL_BITS (12) #else @@ -147,7 +147,8 @@ #elif defined(STM32L432xx) || \ defined(STM32L451xx) || defined(STM32L452xx) || \ defined(STM32L462xx) || defined(STM32L475xx) || \ - defined(STM32L476xx) || defined(STM32L496xx) + defined(STM32L476xx) || defined(STM32L496xx) || \ + defined(STM32WB55xx) #define VBAT_DIV (3) #else #error Unsupported processor @@ -203,6 +204,11 @@ STATIC bool is_adcx_channel(int channel) { ADC_HandleTypeDef handle; handle.Instance = ADCx; return IS_ADC_CHANNEL(&handle, channel); + #elif defined(STM32WB) + ADC_HandleTypeDef handle; + handle.Instance = ADCx; + return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) + || IS_ADC_CHANNEL(&handle, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel)); #else #error Unsupported processor #endif @@ -212,7 +218,7 @@ STATIC void adc_wait_for_eoc_or_timeout(int32_t timeout) { uint32_t tickstart = HAL_GetTick(); #if defined(STM32F4) || defined(STM32F7) while ((ADCx->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) { - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) while (READ_BIT(ADCx->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) { #else #error Unsupported processor @@ -229,7 +235,10 @@ STATIC void adcx_clock_enable(void) { #elif defined(STM32H7) __HAL_RCC_ADC3_CLK_ENABLE(); __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP); - #elif defined(STM32L4) + #elif defined(STM32L4) || defined(STM32WB) + if (__HAL_RCC_GET_ADC_SOURCE() == RCC_ADCCLKSOURCE_NONE) { + __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_SYSCLK); + } __HAL_RCC_ADC_CLK_ENABLE(); #else #error Unsupported processor @@ -269,7 +278,7 @@ STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { adch->Init.OversamplingMode = DISABLE; adch->Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; adch->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; - #elif defined(STM32L4) + #elif defined(STM32L4) || defined(STM32WB) adch->Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; adch->Init.ScanConvMode = ADC_SCAN_DISABLE; adch->Init.LowPowerAutoWait = DISABLE; @@ -286,7 +295,7 @@ STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { #if defined(STM32H7) HAL_ADCEx_Calibration_Start(adch, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED); #endif - #if defined(STM32L4) + #if defined(STM32L4) || defined(STM32WB) HAL_ADCEx_Calibration_Start(adch, ADC_SINGLE_ENDED); #endif } @@ -313,7 +322,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) { ADC_ChannelConfTypeDef sConfig; - #if defined(STM32H7) + #if defined(STM32H7) || defined(STM32WB) sConfig.Rank = ADC_REGULAR_RANK_1; if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) { channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel); @@ -337,7 +346,7 @@ STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.OffsetRightShift = DISABLE; sConfig.OffsetSignedSaturation = DISABLE; - #elif defined(STM32L4) + #elif defined(STM32L4) || defined(STM32WB) if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; } else { @@ -523,7 +532,7 @@ STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_ // for subsequent samples we can just set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) ADCx->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) SET_BIT(ADCx->CR, ADC_CR_ADSTART); #else #error Unsupported processor @@ -633,7 +642,7 @@ STATIC mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i // ADC is started: set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) ADCx->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) SET_BIT(ADCx->CR, ADC_CR_ADSTART); #else #error Unsupported processor diff --git a/ports/stm32/boards/stm32l4xx_hal_conf_base.h b/ports/stm32/boards/stm32l4xx_hal_conf_base.h index 7e249138f566e..cfffcffbbe134 100644 --- a/ports/stm32/boards/stm32l4xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32l4xx_hal_conf_base.h @@ -50,6 +50,7 @@ #include "stm32l4xx_hal_uart.h" #include "stm32l4xx_hal_usart.h" #include "stm32l4xx_hal_wwdg.h" +#include "stm32l4xx_ll_adc.h" // Enable various HAL modules #define HAL_MODULE_ENABLED From 0f7b5cceeaf48e0d9710d9a1b1be766018cc812f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:31:07 +1000 Subject: [PATCH 024/352] stm32/machine_adc: Make setting of ADC1_COMMON->CCR clearer on STM32WB. --- ports/stm32/machine_adc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 5ba000a2338c8..f29896d37cf26 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -132,8 +132,10 @@ STATIC void adc_config(ADC_TypeDef *adc, uint32_t bits) { #elif defined(STM32H7) ADC12_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos; ADC3_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos; - #elif defined(STM32L0) || defined(STM32WB) + #elif defined(STM32L0) ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2 + #elif defined(STM32WB) + ADC1_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos | 0 << ADC_CCR_CKMODE_Pos; // PRESC=1, MODE=ASYNC #endif #if defined(STM32H7) || defined(STM32L4) || defined(STM32WB) From c8985d52d3fde45cea2020573a78fecc4d0de142 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:32:03 +1000 Subject: [PATCH 025/352] stm32/dma: Add support for DMA on STM32WB, with SPI settings provided. --- ports/stm32/dma.c | 75 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index cbcc8542a19a2..4320315ba4e5d 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -34,18 +34,6 @@ #include "dma.h" #include "irq.h" -#if defined(STM32WB) - -// DMA is currently not implemented for this MCU - -void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data) { -} - -void dma_deinit(const dma_descr_t *dma_descr) { -} - -#else - #define DMA_IDLE_ENABLED() (dma_idle.enabled != 0) #define DMA_SYSTICK_LOG2 (3) #define DMA_SYSTICK_MASK ((1 << DMA_SYSTICK_LOG2) - 1) @@ -82,7 +70,7 @@ typedef union { struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; - #elif defined(STM32F0) || defined(STM32L0) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) DMA_Channel_TypeDef *instance; #else #error "Unsupported Processor" @@ -97,7 +85,7 @@ struct _dma_descr_t { static const DMA_InitTypeDef dma_init_struct_spi_i2c = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) + #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -120,7 +108,7 @@ static const DMA_InitTypeDef dma_init_struct_spi_i2c = { static const DMA_InitTypeDef dma_init_struct_sdio = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32L0) || defined(STM32L4) + #elif defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -130,7 +118,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { .MemDataAlignment = DMA_MDATAALIGN_WORD, #if defined(STM32F4) || defined(STM32F7) .Mode = DMA_PFCTRL, - #elif defined(STM32L0) || defined(STM32L4) + #elif defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Mode = DMA_NORMAL, #endif .Priority = DMA_PRIORITY_VERY_HIGH, @@ -148,7 +136,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { static const DMA_InitTypeDef dma_init_struct_dac = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) + #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -438,6 +426,40 @@ static const uint8_t dma_irqn[NSTREAM] = { DMA2_Channel7_IRQn, }; +#elif defined(STM32WB) + +#define NCONTROLLERS (2) +#define NSTREAMS_PER_CONTROLLER (7) +#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER) + +#define DMA_SUB_INSTANCE_AS_UINT8(dma_request) (dma_request) + +#define DMA1_ENABLE_MASK (0x007f) // Bits in dma_enable_mask corresponding to DMA1 +#define DMA2_ENABLE_MASK (0x3f80) // Bits in dma_enable_mask corresponding to DMA2 + +// DMA1 streams +const dma_descr_t dma_SPI_1_RX = { DMA1_Channel1, DMA_REQUEST_SPI1_RX, dma_id_0, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_TX = { DMA1_Channel2, DMA_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_RX = { DMA1_Channel3, DMA_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_TX = { DMA1_Channel4, DMA_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c }; + +static const uint8_t dma_irqn[NSTREAM] = { + DMA1_Channel1_IRQn, + DMA1_Channel2_IRQn, + DMA1_Channel3_IRQn, + DMA1_Channel4_IRQn, + DMA1_Channel5_IRQn, + DMA1_Channel6_IRQn, + DMA1_Channel7_IRQn, + DMA2_Channel1_IRQn, + DMA2_Channel2_IRQn, + DMA2_Channel3_IRQn, + DMA2_Channel4_IRQn, + DMA2_Channel5_IRQn, + DMA2_Channel6_IRQn, + DMA2_Channel7_IRQn, +}; + #elif defined(STM32H7) #define NCONTROLLERS (2) @@ -717,7 +739,7 @@ void DMA1_Channel4_5_6_7_IRQHandler(void) { IRQ_EXIT(DMA1_Channel4_5_6_7_IRQn); } -#elif defined(STM32L4) +#elif defined(STM32L4) || defined(STM32WB) void DMA1_Channel1_IRQHandler(void) { IRQ_ENTER(DMA1_Channel1_IRQn); @@ -836,6 +858,13 @@ static void dma_enable_clock(dma_id_t dma_id) { dma_enable_mask |= (1 << dma_id); MICROPY_END_ATOMIC_SECTION(irq_state); + #if defined(STM32WB) + // This MCU has a DMAMUX peripheral which needs to be enabled to multiplex the channels. + if (!__HAL_RCC_DMAMUX1_IS_CLK_ENABLED()) { + __HAL_RCC_DMAMUX1_CLK_ENABLE(); + } + #endif + if (dma_id < NSTREAMS_PER_CONTROLLER) { if (((old_enable_mask & DMA1_ENABLE_MASK) == 0) && !DMA1_IS_CLK_ENABLED()) { __HAL_RCC_DMA1_CLK_ENABLE(); @@ -877,7 +906,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 dma->Instance = dma_descr->instance; dma->Init = *dma_descr->init; dma->Init.Direction = dir; - #if defined(STM32L0) || defined(STM32L4) || defined(STM32H7) + #if defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) dma->Init.Request = dma_descr->sub_instance; #else #if !defined(STM32F0) @@ -904,7 +933,7 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir dma_enable_clock(dma_id); - #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) + #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) // Always reset and configure the H7 and L0/L4 DMA peripheral // (dma->State is set to HAL_DMA_STATE_RESET by memset above) // TODO: understand how L0/L4 DMA works so this is not needed @@ -1062,6 +1091,10 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a dma->CCR |= DMA_CCR_EN; } +#elif defined(STM32WB) + +// These functions are currently not implemented or needed for this MCU. + #else void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { @@ -1135,5 +1168,3 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a } #endif - -#endif // defined(STM32WB) From 246f3f640dd635f67269bfdbebe23408f77cd2b0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 1 Jun 2020 21:38:11 +1000 Subject: [PATCH 026/352] stm32/boards/xxx_WB55: Enable pyb.ADC and hardware SPI on WB55 boards. These features are now supported (although machine.ADC is recommended over pyb.ADC). --- ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h | 3 --- ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h index 2992ccce71772..2061349417c64 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h @@ -10,7 +10,6 @@ #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_RNG (1) -#define MICROPY_HW_ENABLE_ADC (0) #define MICROPY_HW_ENABLE_USB (1) #define MICROPY_HW_HAS_SWITCH (1) @@ -34,7 +33,6 @@ #define MICROPY_HW_I2C3_SDA (pin_C1) // Arduino A1, pin 30 on CN7 // SPI buses -#if 0 // TODO need working DMA #define MICROPY_HW_SPI1_NSS (pin_A4) // Arduino D10 pin 17 on CN10 #define MICROPY_HW_SPI1_SCK (pin_A5) // Arduino D13, pin 11 on CN10 #define MICROPY_HW_SPI1_MISO (pin_A6) // Arduino D12, pin 13 on CN10 @@ -43,7 +41,6 @@ #define MICROPY_HW_SPI2_SCK (pin_B13) // pin 30 on CN10 #define MICROPY_HW_SPI2_MISO (pin_B14) // pin 28 on CN10 #define MICROPY_HW_SPI2_MOSI (pin_B15) // pin 26 on CN10 -#endif // User switch; pressing the button makes the input go low #define MICROPY_HW_USRSW_PIN (pin_C4) diff --git a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h index 0ff751d613a54..fdb061900b898 100644 --- a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h +++ b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h @@ -10,7 +10,6 @@ #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_RNG (1) -#define MICROPY_HW_ENABLE_ADC (0) #define MICROPY_HW_ENABLE_USB (1) #define MICROPY_HW_HAS_SWITCH (1) @@ -24,6 +23,11 @@ #define MICROPY_HW_I2C1_SCL (pin_B8) #define MICROPY_HW_I2C1_SDA (pin_B9) +// SPI buses +#define MICROPY_HW_SPI1_SCK (pin_A5) // pin 8 on CN1 +#define MICROPY_HW_SPI1_MISO (pin_A6) // pin 9 on CN1 +#define MICROPY_HW_SPI1_MOSI (pin_A7) // pin 10 on CN1 + // User switch; pressing the button makes the input go low #define MICROPY_HW_USRSW_PIN (pin_A10) #define MICROPY_HW_USRSW_PULL (GPIO_PULLUP) From a4086a2f13732c88f81f535982288e6729a9f7db Mon Sep 17 00:00:00 2001 From: David Spickett Date: Fri, 29 May 2020 13:42:08 +0100 Subject: [PATCH 027/352] qemu-arm/README: Update link to toolchain. New releases have moved from launchpad to developer.arm.com. --- ports/qemu-arm/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/qemu-arm/README.md b/ports/qemu-arm/README.md index 4f1e79b101772..2c815c54b088a 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu-arm/README.md @@ -15,8 +15,9 @@ The purposes of this port are to enable: - no need to use OpenOCD or anything else that might slow down the process in terms of plugging things together, pressing buttons, etc. -This port will only work with with [GCC ARM Embedded](launchpad.net/gcc-arm-embedded) -toolchain and not with CodeSourcery toolchain. You will need to modify +This port will only work with the [GNU ARM Embedded Toolchain]( +https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm) + and not with CodeSourcery toolchain. You will need to modify `LDFLAGS` if you want to use CodeSourcery's version of `arm-none-eabi`. The difference is that CodeSourcery needs `-T generic-m-hosted.ld` while ARM's version requires `--specs=nano.specs --specs=rdimon.specs` to be From da71f55e23a97102d241481e410fdb22d7c55758 Mon Sep 17 00:00:00 2001 From: stinos Date: Fri, 29 May 2020 20:46:46 +0200 Subject: [PATCH 028/352] stm32/Makefile: Quote libgcc path so spaces are not an issue. Fixes #3116. --- ports/stm32/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index fdea95e92f5c9..2614d4aa0fcf0 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -124,7 +124,7 @@ endif LDFLAGS = -nostdlib -L $(LD_DIR) $(addprefix -T,$(LD_FILES)) -Map=$(@:.elf=.map) --cref LDFLAGS += --defsym=_estack_reserve=8 -LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) +LIBS = "$(shell $(CC) $(CFLAGS) -print-libgcc-file-name)" # Remove uncalled code from the final image. CFLAGS += -fdata-sections -ffunction-sections From 203b10703e5df20939b88a2ce29bb961cce7e4b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 29 May 2020 10:28:38 +1000 Subject: [PATCH 029/352] py/modbuiltins: Fix getattr to work with class raising AttributeError. Fixes issue #6089. --- py/modbuiltins.c | 6 +++++- tests/basics/builtin_getattr.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 85d30ab66d052..cfbfc5a25683e 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -558,7 +558,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj, 1, mp_builtin_sorted); static inline mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) { mp_obj_t dest[2]; // use load_method, raising or not raising exception - ((defval == MP_OBJ_NULL) ? mp_load_method : mp_load_method_maybe)(base, attr, dest); + if (defval == MP_OBJ_NULL) { + mp_load_method(base, attr, dest); + } else { + mp_load_method_protected(base, attr, dest, false); + } if (dest[0] == MP_OBJ_NULL) { return defval; } else if (dest[1] == MP_OBJ_NULL) { diff --git a/tests/basics/builtin_getattr.py b/tests/basics/builtin_getattr.py index 59cb7e7f7a4b1..afa4ab3293ca6 100644 --- a/tests/basics/builtin_getattr.py +++ b/tests/basics/builtin_getattr.py @@ -16,3 +16,15 @@ def meth(self, i): print(getattr(a, "_none_such", 123)) print(getattr(list, "foo", 456)) print(getattr(a, "va" + "r2")) + +# test a class that defines __getattr__ and may raise AttributeError +class B: + def __getattr__(self, attr): + if attr == "a": + return attr + else: + raise AttributeError +b = B() +print(getattr(b, "a")) +print(getattr(b, "a", "default")) +print(getattr(b, "b", "default")) From b2030e1661ccf58cb0f62cba0c2009f97ebbf455 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 30 May 2020 07:34:40 +1000 Subject: [PATCH 030/352] lib/utils/pyexec: Add missing MP_ERROR_TEXT when compiler disabled. --- lib/utils/pyexec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index ec20daff4b23d..2c8ca2de0cecc 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -96,7 +96,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); #else - mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported"); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); #endif } From 8e591d412a9922d71629f4fea74954d547f0c955 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 30 May 2020 07:35:19 +1000 Subject: [PATCH 031/352] minimal: Make build more flexible and work as 64-bit build. Changes are: - string0 is no longer built when building for host as the target, because it'll be provided by the system libc and may in some cases clash with the system one (eg on OSX). - mp_int_t/mp_uint_t are defined in terms of intptr_t/uintptr_t to support both 32-bit and 64-bit builds. - Configuration values which are the default in py/mpconfig.h are removed from mpconfigport.h to make the configuration a bit more minimal, eg as a better starting point for new ports. --- ports/minimal/Makefile | 5 ++++- ports/minimal/main.c | 2 ++ ports/minimal/mpconfigport.h | 32 ++------------------------------ 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/ports/minimal/Makefile b/ports/minimal/Makefile index 5801d64087b21..bce544ec176b5 100644 --- a/ports/minimal/Makefile +++ b/ports/minimal/Makefile @@ -49,10 +49,13 @@ SRC_C = \ lib/utils/printf.c \ lib/utils/stdout_helpers.c \ lib/utils/pyexec.c \ - lib/libc/string0.c \ lib/mp-readline/readline.c \ $(BUILD)/_frozen_mpy.c \ +ifeq ($(CROSS), 1) +SRC_C += lib/libc/string0.c +endif + OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) ifeq ($(CROSS), 1) diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 6943ee7589f39..aa6d10ee3750c 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -60,6 +60,7 @@ int main(int argc, char **argv) { return 0; } +#if MICROPY_ENABLE_GC void gc_collect(void) { // WARNING: This gc_collect implementation doesn't try to get root // pointers from CPU registers, and thus may function incorrectly. @@ -69,6 +70,7 @@ void gc_collect(void) { gc_collect_end(); gc_dump_info(); } +#endif mp_lexer_t *mp_lexer_new_from_file(const char *filename) { mp_raise_OSError(MP_ENOENT); diff --git a/ports/minimal/mpconfigport.h b/ports/minimal/mpconfigport.h index 2507d0124d1ca..c3bdf66c10ae2 100644 --- a/ports/minimal/mpconfigport.h +++ b/ports/minimal/mpconfigport.h @@ -11,31 +11,18 @@ #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool #define MICROPY_ALLOC_PATH_MAX (256) #define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB (0) -#define MICROPY_COMP_MODULE_CONST (0) #define MICROPY_COMP_CONST (0) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) -#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) -#define MICROPY_MEM_STATS (0) -#define MICROPY_DEBUG_PRINTERS (0) #define MICROPY_ENABLE_GC (1) #define MICROPY_GC_ALLOC_THRESHOLD (0) -#define MICROPY_REPL_EVENT_DRIVEN (0) #define MICROPY_HELPER_REPL (1) -#define MICROPY_HELPER_LEXER_UNIX (0) -#define MICROPY_ENABLE_SOURCE_LINE (0) -#define MICROPY_ENABLE_DOC_STRING (0) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) #define MICROPY_PY_ASYNC_AWAIT (0) #define MICROPY_PY_BUILTINS_BYTEARRAY (0) #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (0) #define MICROPY_PY_BUILTINS_ENUMERATE (0) #define MICROPY_PY_BUILTINS_FILTER (0) -#define MICROPY_PY_BUILTINS_FROZENSET (0) #define MICROPY_PY_BUILTINS_REVERSED (0) #define MICROPY_PY_BUILTINS_SET (0) #define MICROPY_PY_BUILTINS_SLICE (0) @@ -48,34 +35,19 @@ #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_ATTRTUPLE (0) #define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) -#define MICROPY_PY_CMATH (0) #define MICROPY_PY_IO (0) #define MICROPY_PY_STRUCT (0) #define MICROPY_PY_SYS (0) #define MICROPY_MODULE_FROZEN_MPY (1) #define MICROPY_CPYTHON_COMPAT (0) #define MICROPY_MODULE_GETATTR (0) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) // type definitions for the specific machine -#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) - -// This port is intended to be 32-bit, but unfortunately, int32_t for -// different targets may be defined in different ways - either as int -// or as long. This requires different printf formatting specifiers -// to print such value. So, we avoid int32_t and use int directly. -#define UINT_FMT "%u" -#define INT_FMT "%d" -typedef int mp_int_t; // must be pointer size -typedef unsigned mp_uint_t; // must be pointer size - +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 MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, From e54626f4c1b5cb4de0b5dc86d820bb36c1d6499f Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 1 Jun 2020 16:16:25 +1000 Subject: [PATCH 032/352] docs/reference: Add note about multiple exceptions when heap is locked. --- docs/reference/isr_rules.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index 57690ac212c64..7f466ab42f41c 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -43,6 +43,11 @@ for the purpose. Debugging is simplified if the following code is included in an import micropython micropython.alloc_emergency_exception_buf(100) +The emergency exception buffer can only hold one exception stack trace. This means that if a second exception is +thrown during the handling of an exception while the heap is locked, that second exception's stack trace will +replace the original one - even if the second exception is cleanly handled. This can lead to confusing exception +messages if the buffer is later printed. + Simplicity ~~~~~~~~~~ From 02cc4462b70729a42cb127c840fee891dd08a0d4 Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Sun, 12 Apr 2020 22:00:06 +0200 Subject: [PATCH 033/352] mimxrt: Add initial impl of machine.LED class, and basic pin support. This commit implements an LED class with rudimentary parts of a pin C API to support it. The LED class does not yet support setting an intensity. This LED class is put in the machine module for the time being, until a better place is found. One LED is supported on TEENSY40 and MIMXRT1010_EVK boards. --- ports/mimxrt/Makefile | 11 +- ports/mimxrt/board_init.c | 14 --- .../boards/MIMXRT1010_EVK/mpconfigboard.h | 7 +- ports/mimxrt/boards/MIMXRT1010_EVK/pins.c | 33 ++++++ ports/mimxrt/boards/MIMXRT1010_EVK/pins.h | 30 ++++++ ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 7 +- ports/mimxrt/boards/TEENSY40/pins.c | 33 ++++++ ports/mimxrt/boards/TEENSY40/pins.h | 30 ++++++ ports/mimxrt/led.c | 102 ++++++++++++++++++ ports/mimxrt/led.h | 56 ++++++++++ ports/mimxrt/machine_led.c | 91 ++++++++++++++++ ports/mimxrt/main.c | 2 + ports/mimxrt/modmachine.c | 4 + ports/mimxrt/mphalport.h | 4 + ports/mimxrt/pin.c | 37 +++++++ ports/mimxrt/pin.h | 100 +++++++++++++++++ 16 files changed, 540 insertions(+), 21 deletions(-) create mode 100644 ports/mimxrt/boards/MIMXRT1010_EVK/pins.c create mode 100644 ports/mimxrt/boards/MIMXRT1010_EVK/pins.h create mode 100644 ports/mimxrt/boards/TEENSY40/pins.c create mode 100644 ports/mimxrt/boards/TEENSY40/pins.h create mode 100644 ports/mimxrt/led.c create mode 100644 ports/mimxrt/led.h create mode 100644 ports/mimxrt/machine_led.c create mode 100644 ports/mimxrt/pin.c create mode 100644 ports/mimxrt/pin.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index cbd2b8595791a..c4ea70128e0ad 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -88,9 +88,13 @@ SRC_TINYUSB_IMX_C += \ SRC_C = \ main.c \ + led.c \ + pin.c \ tusb_port.c \ board_init.c \ $(BOARD_DIR)/flash_config.c \ + $(BOARD_DIR)/pins.c \ + machine_led.c \ modutime.c \ modmachine.c \ mphalport.c \ @@ -108,7 +112,12 @@ SRC_SS = $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S SRC_S = lib/utils/gchelper_m3.s \ # List of sources for qstr extraction -SRC_QSTR += modutime.c modmachine.c +SRC_QSTR += \ + machine_led.c \ + modutime.c \ + modmachine.c \ + pin.c \ + $(BOARD_DIR)/pins.c \ OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index a3c27b045bba5..6f5235d3b635b 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -45,8 +45,6 @@ volatile uint32_t systick_ms = 0; const uint8_t dcd_data[] = { 0x00 }; -void board_led_write(bool state); - void board_init(void) { // Init clock BOARD_BootClockRUN(); @@ -58,14 +56,6 @@ void board_init(void) { // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); - // LED - IOMUXC_SetPinMux(MICROPY_HW_LED_PINMUX, 0U); - IOMUXC_SetPinConfig(MICROPY_HW_LED_PINMUX, 0x10B0U); - - gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0, kGPIO_NoIntmode }; - GPIO_PinInit(MICROPY_HW_LED_PORT, MICROPY_HW_LED_PIN, &led_config); - board_led_write(true); - // ------------- USB0 ------------- // // Clock @@ -95,10 +85,6 @@ void board_init(void) { // CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, 480000000U); } -void board_led_write(bool state) { - GPIO_PinWrite(MICROPY_HW_LED_PORT, MICROPY_HW_LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON)); -} - void SysTick_Handler(void) { systick_ms++; } diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index 02d34fc42f052..eeddd0e0213fb 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -3,6 +3,7 @@ #define BOARD_FLASH_SIZE (16 * 1024 * 1024) -#define MICROPY_HW_LED_PINMUX IOMUXC_GPIO_11_GPIOMUX_IO11 -#define MICROPY_HW_LED_PORT GPIO1 -#define MICROPY_HW_LED_PIN 11 +// i.MX RT1010 EVK has 1 board LED +#define MICROPY_HW_LED1_PIN (GPIO_11) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/pins.c b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.c new file mode 100644 index 0000000000000..e0397e09edb21 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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 "pin.h" + +static pin_af_obj_t GPIO_11_af[] = { + PIN_AF(GPIOMUX_IO11, PIN_AF_MODE_ALT5, GPIO1, 0x10B0U), +}; + +pin_obj_t GPIO_11 = PIN(GPIO_11, GPIO1, 5, GPIO_11_af); diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/pins.h b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.h new file mode 100644 index 0000000000000..99534932a19d5 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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. + */ + +// NOTE: pins.h shall only be included in in pin.h +// hence no include guards are needed since they will be provided by pin.h + +extern pin_obj_t GPIO_11; diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index 10c8b7a6d1ffa..7d564785797ad 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -3,6 +3,7 @@ #define BOARD_FLASH_SIZE (2 * 1024 * 1024) -#define MICROPY_HW_LED_PINMUX IOMUXC_GPIO_B0_03_GPIO2_IO03 // D13 -#define MICROPY_HW_LED_PORT GPIO2 -#define MICROPY_HW_LED_PIN 3 +// Teensy 4.0 has 1 board LED +#define MICROPY_HW_LED1_PIN (GPIO_B0_03) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) diff --git a/ports/mimxrt/boards/TEENSY40/pins.c b/ports/mimxrt/boards/TEENSY40/pins.c new file mode 100644 index 0000000000000..c7bfc102d4350 --- /dev/null +++ b/ports/mimxrt/boards/TEENSY40/pins.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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 "pin.h" + +static pin_af_obj_t GPIO_B0_03_af[] = { + PIN_AF(GPIO2_IO03, PIN_AF_MODE_ALT5, GPIO2, 0x10B0U), +}; + +pin_obj_t GPIO_B0_03 = PIN(GPIO_B0_03, GPIO2, 3, GPIO_B0_03_af); diff --git a/ports/mimxrt/boards/TEENSY40/pins.h b/ports/mimxrt/boards/TEENSY40/pins.h new file mode 100644 index 0000000000000..c0b6dcdfa9576 --- /dev/null +++ b/ports/mimxrt/boards/TEENSY40/pins.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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. + */ + +// NOTE: pins.h shall only be included in in pin.h +// hence no include guards are needed since they will be provided by pin.h + +extern pin_obj_t GPIO_B0_03; diff --git a/ports/mimxrt/led.c b/ports/mimxrt/led.c new file mode 100644 index 0000000000000..bec97dd8f7cc4 --- /dev/null +++ b/ports/mimxrt/led.c @@ -0,0 +1,102 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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 "fsl_gpio.h" +#include "fsl_iomuxc.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "led.h" + +#if NUM_LEDS + +const machine_led_obj_t machine_led_obj[NUM_LEDS] = { + { + .base = {&machine_led_type}, + .led_id = 1U, + .led_pin = &MICROPY_HW_LED1_PIN, + } +}; + +void led_init(void) { + // Turn off LEDs and initialize + for (mp_int_t led = 0; led < NUM_LEDS; led++) { + const pin_obj_t *led_pin = machine_led_obj[led].led_pin; + + gpio_pin_config_t pin_config = { + .outputLogic = 1U, + .direction = kGPIO_DigitalOutput, + .interruptMode = kGPIO_NoIntmode, + }; + + GPIO_PinInit(led_pin->gpio, led_pin->pin, &pin_config); + + // ALT mode for GPIO is always 5 + IOMUXC_SetPinMux(led_pin->muxRegister, 5U, 0, 0, led_pin->configRegister, + 1U); // Software Input On Field: Input Path is determined by functionality + IOMUXC_SetPinConfig(led_pin->muxRegister, 5U, 0, 0, led_pin->configRegister, 0x10B0U); + MICROPY_HW_LED_OFF(led_pin); + } +} + +void led_state(machine_led_t led, int state) { + if (led < 1 || led > NUM_LEDS) { + return; + } + + const pin_obj_t *led_pin = machine_led_obj[led - 1].led_pin; + + if (state == 0) { + // turn LED off + MICROPY_HW_LED_OFF(led_pin); + } else { + // turn LED on + MICROPY_HW_LED_ON(led_pin); + } +} + +void led_toggle(machine_led_t led) { + if (led < 1 || led > NUM_LEDS) { + return; + } + + const pin_obj_t *led_pin = machine_led_obj[led - 1].led_pin; + mp_hal_pin_toggle(led_pin); +} + +void led_debug(int value, int delay) { + for (mp_int_t i = 0; i < NUM_LEDS; i++) { + led_state(i + 1, (value & (1 << i))); + } + mp_hal_delay_ms(delay); +} + +#else + +void led_init(void) { +} + +#endif diff --git a/ports/mimxrt/led.h b/ports/mimxrt/led.h new file mode 100644 index 0000000000000..35418321d173d --- /dev/null +++ b/ports/mimxrt/led.h @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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_MIMXRT_LED_H +#define MICROPY_INCLUDED_MIMXRT_LED_H + +#include "pin.h" + +#if defined(MICROPY_HW_LED1_PIN) +#define NUM_LEDS (1) +#else +#define NUM_LEDS (0) +#endif + +typedef enum { + MACHINE_BOARD_LED = 1, +} machine_led_t; + +typedef struct _machine_led_obj_t { + mp_obj_base_t base; + mp_uint_t led_id; + const pin_obj_t *led_pin; +} machine_led_obj_t; + +void led_init(void); +void led_state(machine_led_t led, int state); +void led_toggle(machine_led_t led); +void led_debug(int value, int delay); + +extern const mp_obj_type_t machine_led_type; +extern const machine_led_obj_t machine_led_obj[NUM_LEDS]; + +#endif // MICROPY_INCLUDED_MIMXRT_LED_H diff --git a/ports/mimxrt/machine_led.c b/ports/mimxrt/machine_led.c new file mode 100644 index 0000000000000..07c7b180a10e6 --- /dev/null +++ b/ports/mimxrt/machine_led.c @@ -0,0 +1,91 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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" +#include "py/mphal.h" +#include "led.h" + +#if NUM_LEDS + +STATIC void led_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + machine_led_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "LED(%u)", self->led_id); +} + +STATIC mp_obj_t led_obj_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, 1, false); + + // Extract arguments + mp_int_t led_id = mp_obj_get_int(args[0]); + + // Check led id is in range + if (!(1 <= led_id && led_id <= NUM_LEDS)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "LED(%d) doesn't exist", led_id)); + } + + // Return reference to static object + return MP_OBJ_FROM_PTR(&machine_led_obj[led_id - 1]); +} + +STATIC mp_obj_t led_obj_on(mp_obj_t self_in) { + machine_led_obj_t *self = MP_OBJ_TO_PTR(self_in); + MICROPY_HW_LED_ON(self->led_pin); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on); + +STATIC mp_obj_t led_obj_off(mp_obj_t self_in) { + machine_led_obj_t *self = MP_OBJ_TO_PTR(self_in); + MICROPY_HW_LED_OFF(self->led_pin); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off); + +STATIC mp_obj_t led_obj_toggle(mp_obj_t self_in) { + machine_led_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_hal_pin_toggle(self->led_pin); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_toggle_obj, led_obj_toggle); + +STATIC const mp_rom_map_elem_t led_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&led_obj_on_obj)}, + {MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&led_obj_off_obj)}, + {MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&led_obj_toggle_obj)}, +}; + +STATIC MP_DEFINE_CONST_DICT(led_locals_dict, led_locals_dict_table); + +const mp_obj_type_t machine_led_type = { + {&mp_type_type}, + .name = MP_QSTR_LED, + .print = led_obj_print, + .make_new = led_obj_make_new, + .locals_dict = (mp_obj_dict_t *)&led_locals_dict, +}; + +#endif diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index d94bf24412452..e06283a8ab37b 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -33,6 +33,7 @@ #include "lib/utils/gchelper.h" #include "lib/utils/pyexec.h" #include "tusb.h" +#include "led.h" extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; @@ -41,6 +42,7 @@ void board_init(void); int main(void) { board_init(); tusb_init(); + led_init(); mp_stack_set_top(&_estack); mp_stack_set_limit(&_estack - &_sstack - 1024); diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 747b7bc27f56d..a6aab7b4aba53 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "extmod/machine_mem.h" +#include "led.h" #include CPU_HEADER_H @@ -48,6 +49,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + #if NUM_LEDS + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&machine_led_type) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/ports/mimxrt/mphalport.h b/ports/mimxrt/mphalport.h index e1cf84f6c2cc7..2b9e49432cc0f 100644 --- a/ports/mimxrt/mphalport.h +++ b/ports/mimxrt/mphalport.h @@ -29,6 +29,10 @@ #include +#define mp_hal_pin_high(p) (GPIO_PinWrite(p->gpio, p->pin, 1U)) +#define mp_hal_pin_low(p) (GPIO_PinWrite(p->gpio, p->pin, 0U)) +#define mp_hal_pin_toggle(p) (GPIO_PortToggle(p->gpio, (1 << p->pin))) + extern volatile uint32_t systick_ms; void mp_hal_set_interrupt_char(int c); diff --git a/ports/mimxrt/pin.c b/ports/mimxrt/pin.c new file mode 100644 index 0000000000000..b30f4be41ceae --- /dev/null +++ b/ports/mimxrt/pin.c @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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 "pin.h" + +const mp_obj_type_t pin_type = { + .base = {&mp_type_type}, + .name = MP_QSTR_Pin, +}; + +const mp_obj_type_t pin_af_type = { + {&mp_type_type}, + .name = MP_QSTR_PinAF, +}; diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h new file mode 100644 index 0000000000000..cfb1e6b572502 --- /dev/null +++ b/ports/mimxrt/pin.h @@ -0,0 +1,100 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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_MIMXRT_PIN_H +#define MICROPY_INCLUDED_MIMXRT_PIN_H + +#include "fsl_gpio.h" +#include "py/obj.h" + +enum { + PIN_MODE_IN = 0, + PIN_MODE_OUT, + PIN_MODE_ALT, +}; + +enum { + PIN_AF_MODE_ALT1 = 1, + PIN_AF_MODE_ALT2, + PIN_AF_MODE_ALT3, + PIN_AF_MODE_ALT4, + PIN_AF_MODE_ALT5, + PIN_AF_MODE_ALT6, + PIN_AF_MODE_ALT7, + PIN_AF_MODE_ALT8, +}; + +typedef struct { + mp_obj_base_t base; + qstr name; // port name + uint32_t af_mode; // alternate function + void *instance; // pointer to peripheral instance for alternate function + uint32_t pad_config; // pad configuration for alternate function +} pin_af_obj_t; + +typedef struct { + mp_obj_base_t base; + qstr name; // pad name + GPIO_Type *gpio; // gpio instance for pin + uint32_t pin; // pin number + uint32_t muxRegister; + uint32_t configRegister; + uint32_t mode; // current pin mode + uint32_t af_mode; // current alternate function mode + size_t af_list_len; // length of available alternate functions list + const pin_af_obj_t *af_list; // pointer tolist with alternate functions +} pin_obj_t; + +extern const mp_obj_type_t pin_type; +extern const mp_obj_type_t pin_af_type; + +#define PIN_AF(_name, _af_mode, _instance, _pad_config) \ + { \ + .base = { &pin_af_type }, \ + .name = MP_QSTR_##_name, \ + .af_mode = (uint32_t)(_af_mode), \ + .instance = (void *)(_instance), \ + .pad_config = (uint32_t)(_pad_config), \ + } \ + +#define PIN(_name, _gpio, _pin, _af_list) \ + { \ + .base = { &pin_type }, \ + .name = MP_QSTR_##_name, \ + .gpio = (_gpio), \ + .pin = (uint32_t)(_pin), \ + .muxRegister = (uint32_t)&(IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_##_name]), \ + .configRegister = (uint32_t)&(IOMUXC->SW_PAD_CTL_PAD[kIOMUXC_SW_PAD_CTL_PAD_##_name]), \ + .mode = PIN_MODE_IN, \ + .af_mode = PIN_AF_MODE_ALT5, \ + .af_list_len = (size_t)(sizeof((_af_list)) / sizeof(pin_af_obj_t)), \ + .af_list = (_af_list), \ + } \ + +// Include board specific pins +#include "pins.h" + +#endif // MICROPY_INCLUDED_MIMXRT_PIN_H From e6881f08292d03f089185718c131f543d095089b Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 11 May 2020 21:20:07 +1000 Subject: [PATCH 034/352] extmod/modbluetooth: Make modbluetooth event not a bitfield. There doesn't appear to be any use for only triggering on specific events, so it's just easier to number them sequentially. This makes them smaller values so they take up only 1 byte in the ringbuf, only 1 byte for the opcode in the bytecode, and makes room for more events. Also add a couple of new event types that need to be implemented (to avoid re-numbering later). And rename _COMPLETE and _STATUS to _DONE for consistency. In the future the "trigger" keyword argument can be reinstated by requiring the user to compute the bitmask, eg: ble.irq(handler, 1 << _IRQ_SCAN_RESULT | 1 << _IRQ_SCAN_DONE) --- examples/bluetooth/ble_temperature.py | 4 +- examples/bluetooth/ble_temperature_central.py | 37 +++++---- examples/bluetooth/ble_uart_peripheral.py | 6 +- extmod/modbluetooth.c | 37 ++++----- extmod/modbluetooth.h | 80 ++++++++++--------- extmod/nimble/modbluetooth_nimble.c | 4 +- py/ringbuf.h | 7 ++ 7 files changed, 92 insertions(+), 83 deletions(-) diff --git a/examples/bluetooth/ble_temperature.py b/examples/bluetooth/ble_temperature.py index 01d2f7441fa17..fd24e74d7498a 100644 --- a/examples/bluetooth/ble_temperature.py +++ b/examples/bluetooth/ble_temperature.py @@ -11,8 +11,8 @@ from micropython import const -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) # org.bluetooth.service.environmental_sensing _ENV_SENSE_UUID = bluetooth.UUID(0x181A) diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 04fed0a299078..8b679b9b807be 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -10,22 +10,25 @@ from micropython import const -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_GATTS_WRITE = const(1 << 2) -_IRQ_GATTS_READ_REQUEST = const(1 << 3) -_IRQ_SCAN_RESULT = const(1 << 4) -_IRQ_SCAN_COMPLETE = const(1 << 5) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_SERVICE_RESULT = const(1 << 8) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) -_IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10) -_IRQ_GATTC_READ_RESULT = const(1 << 11) -_IRQ_GATTC_WRITE_STATUS = const(1 << 12) -_IRQ_GATTC_NOTIFY = const(1 << 13) -_IRQ_GATTC_INDICATE = const(1 << 14) -_IRQ_ALL = const(0xFFFF) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_GATTS_READ_REQUEST = const(4) +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_DESCRIPTOR_RESULT = const(13) +_IRQ_GATTC_DESCRIPTOR_DONE = const(14) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) +_IRQ_GATTC_INDICATE = const(19) _ADV_IND = const(0x00) _ADV_DIRECT_IND = const(0x01) @@ -93,7 +96,7 @@ def _irq(self, event, data): self._name = decode_name(adv_data) or "?" self._ble.gap_scan(None) - elif event == _IRQ_SCAN_COMPLETE: + elif event == _IRQ_SCAN_DONE: if self._scan_callback: if self._addr: # Found a device during the scan (and the scan was explicitly stopped). diff --git a/examples/bluetooth/ble_uart_peripheral.py b/examples/bluetooth/ble_uart_peripheral.py index c013d96ec9ef1..cc8d589b0e35f 100644 --- a/examples/bluetooth/ble_uart_peripheral.py +++ b/examples/bluetooth/ble_uart_peripheral.py @@ -5,9 +5,9 @@ from micropython import const -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_GATTS_WRITE = const(1 << 2) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) _UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") _UART_TX = ( diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 7e6abb54909ca..b43bec0178df8 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 Ayke van Laethem - * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2019-2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -57,7 +57,6 @@ STATIC const mp_obj_type_t bluetooth_uuid_type; typedef struct { mp_obj_base_t base; mp_obj_t irq_handler; - uint16_t irq_trigger; bool irq_scheduled; mp_obj_t irq_data_tuple; uint8_t irq_data_addr_bytes[6]; @@ -249,7 +248,6 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, o->base.type = &bluetooth_ble_type; o->irq_handler = mp_const_none; - o->irq_trigger = 0; // Pre-allocate the event data tuple to prevent needing to allocate in the IRQ handler. o->irq_data_tuple = mp_obj_new_tuple(MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN, NULL); @@ -372,10 +370,9 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bluetooth_ble_config_obj, 1, bluetooth_ble_config); STATIC mp_obj_t bluetooth_ble_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_handler, ARG_trigger }; + enum { ARG_handler }; static const mp_arg_t allowed_args[] = { { MP_QSTR_handler, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_trigger, MP_ARG_INT, {.u_int = MP_BLUETOOTH_IRQ_ALL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -388,7 +385,6 @@ STATIC mp_obj_t bluetooth_ble_irq(size_t n_args, const mp_obj_t *pos_args, mp_ma MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); o->irq_handler = callback; - o->irq_trigger = args[ARG_trigger].u_int; MICROPY_PY_BLUETOOTH_EXIT return mp_const_none; @@ -854,7 +850,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { for (;;) { MICROPY_PY_BLUETOOTH_ENTER - mp_int_t event = event = ringbuf_get16(&o->ringbuf); + mp_int_t event = ringbuf_get(&o->ringbuf); if (event < 0) { // Nothing available in ringbuf. MICROPY_PY_BLUETOOTH_EXIT @@ -878,7 +874,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT) { // addr_type, addr, adv_type, rssi, adv_data ringbuf_extract(&o->ringbuf, data_tuple, 0, 1, &o->irq_data_addr, 2, NULL, &o->irq_data_data); - } else if (event == MP_BLUETOOTH_IRQ_SCAN_COMPLETE) { + } else if (event == MP_BLUETOOTH_IRQ_SCAN_DONE) { // No params required. data_tuple->len = 0; } else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) { @@ -893,7 +889,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_RESULT || event == MP_BLUETOOTH_IRQ_GATTC_NOTIFY || event == MP_BLUETOOTH_IRQ_GATTC_INDICATE) { // conn_handle, value_handle, data ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, &o->irq_data_data); - } else if (event == MP_BLUETOOTH_IRQ_GATTC_WRITE_STATUS) { + } else if (event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { // conn_handle, value_handle, status ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL); #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE @@ -915,23 +911,24 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_inv // Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data // into the ringbuf and schedule the callback via mp_sched_schedule. -STATIC bool enqueue_irq(mp_obj_bluetooth_ble_t *o, size_t len, uint16_t event) { - if (!o || !(o->irq_trigger & event) || o->irq_handler == mp_const_none) { +STATIC bool enqueue_irq(mp_obj_bluetooth_ble_t *o, size_t len, uint8_t event) { + if (!o || o->irq_handler == mp_const_none) { return false; } - if (ringbuf_free(&o->ringbuf) < len + 2) { + // Check if there is enough room for . + if (ringbuf_free(&o->ringbuf) < len + 1) { // Ringbuffer doesn't have room (and is therefore non-empty). // If this is another scan result, or the front of the ringbuffer isn't a scan result, then nothing to do. - if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT || ringbuf_peek16(&o->ringbuf) != MP_BLUETOOTH_IRQ_SCAN_RESULT) { + if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT || ringbuf_peek(&o->ringbuf) != MP_BLUETOOTH_IRQ_SCAN_RESULT) { return false; } // Front of the queue is a scan result, remove it. // event, addr_type, addr, adv_type, rssi - int n = 2 + 1 + 6 + 1 + 1; + int n = 1 + 1 + 6 + 1 + 1; for (int i = 0; i < n; ++i) { ringbuf_get(&o->ringbuf); } @@ -943,7 +940,7 @@ STATIC bool enqueue_irq(mp_obj_bluetooth_ble_t *o, size_t len, uint16_t event) { } // Append this event, the caller will then append the arguments. - ringbuf_put16(&o->ringbuf, event); + ringbuf_put(&o->ringbuf, event); return true; } @@ -959,7 +956,7 @@ STATIC void schedule_ringbuf(mp_uint_t atomic_state) { } } -void mp_bluetooth_gap_on_connected_disconnected(uint16_t event, uint16_t conn_handle, uint8_t addr_type, const uint8_t *addr) { +void mp_bluetooth_gap_on_connected_disconnected(uint8_t event, uint16_t conn_handle, uint8_t addr_type, const uint8_t *addr) { MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); if (enqueue_irq(o, 2 + 1 + 6, event)) { @@ -986,7 +983,7 @@ void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle) { void mp_bluetooth_gap_on_scan_complete(void) { MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); - if (enqueue_irq(o, 0, MP_BLUETOOTH_IRQ_SCAN_COMPLETE)) { + if (enqueue_irq(o, 0, MP_BLUETOOTH_IRQ_SCAN_DONE)) { } schedule_ringbuf(atomic_state); } @@ -1048,7 +1045,7 @@ void mp_bluetooth_gattc_on_descriptor_result(uint16_t conn_handle, uint16_t hand schedule_ringbuf(atomic_state); } -size_t mp_bluetooth_gattc_on_data_available_start(uint16_t event, uint16_t conn_handle, uint16_t value_handle, size_t data_len, mp_uint_t *atomic_state_out) { +size_t mp_bluetooth_gattc_on_data_available_start(uint8_t event, uint16_t conn_handle, uint16_t value_handle, size_t data_len, mp_uint_t *atomic_state_out) { MICROPY_PY_BLUETOOTH_ENTER *atomic_state_out = atomic_state; mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); @@ -1077,7 +1074,7 @@ void mp_bluetooth_gattc_on_data_available_end(mp_uint_t atomic_state) { void mp_bluetooth_gattc_on_write_status(uint16_t conn_handle, uint16_t value_handle, uint16_t status) { MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); - if (enqueue_irq(o, 2 + 2 + 2, MP_BLUETOOTH_IRQ_GATTC_WRITE_STATUS)) { + if (enqueue_irq(o, 2 + 2 + 2, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE)) { ringbuf_put16(&o->ringbuf, conn_handle); ringbuf_put16(&o->ringbuf, value_handle); ringbuf_put16(&o->ringbuf, status); @@ -1092,7 +1089,7 @@ void mp_bluetooth_gattc_on_write_status(uint16_t conn_handle, uint16_t value_han // On ESP32, for example, this is not the case. bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) { mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); - if ((o->irq_trigger & MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST) && o->irq_handler != mp_const_none) { + if (o->irq_handler != mp_const_none) { // Use pre-allocated tuple because this is a hard IRQ. mp_obj_tuple_t *data = MP_OBJ_TO_PTR(o->irq_data_tuple); data->items[0] = MP_OBJ_NEW_SMALL_INT(conn_handle); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 4e658a7a0a426..091a62c845ceb 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 Ayke van Laethem - * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2019-2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -88,48 +88,50 @@ #define MP_BLUETOOTH_ADDR_RANDOM_PRIVATE_NON_RESOLVABLE (0x13) // Random private non-resolvable address. (NRF SD 0x03) // Event codes for the IRQ handler. -// Can also be combined to pass to the trigger param to select which events you -// are interested in. -// Note this is currently stored in a uint16_t (in irq_trigger, and the event -// arg to the irq handler), so one spare value remaining. -#define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1 << 0) -#define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (1 << 1) -#define MP_BLUETOOTH_IRQ_GATTS_WRITE (1 << 2) -#define MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST (1 << 3) -#define MP_BLUETOOTH_IRQ_SCAN_RESULT (1 << 4) -#define MP_BLUETOOTH_IRQ_SCAN_COMPLETE (1 << 5) -#define MP_BLUETOOTH_IRQ_PERIPHERAL_CONNECT (1 << 6) -#define MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT (1 << 7) -#define MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT (1 << 8) -#define MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_RESULT (1 << 9) -#define MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_RESULT (1 << 10) -#define MP_BLUETOOTH_IRQ_GATTC_READ_RESULT (1 << 11) -#define MP_BLUETOOTH_IRQ_GATTC_WRITE_STATUS (1 << 12) -#define MP_BLUETOOTH_IRQ_GATTC_NOTIFY (1 << 13) -#define MP_BLUETOOTH_IRQ_GATTC_INDICATE (1 << 14) -#define MP_BLUETOOTH_IRQ_ALL (0xffff) +#define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1) +#define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (2) +#define MP_BLUETOOTH_IRQ_GATTS_WRITE (3) +#define MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST (4) +#define MP_BLUETOOTH_IRQ_SCAN_RESULT (5) +#define MP_BLUETOOTH_IRQ_SCAN_DONE (6) +#define MP_BLUETOOTH_IRQ_PERIPHERAL_CONNECT (7) +#define MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT (8) +#define MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT (9) +#define MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE (10) +#define MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_RESULT (11) +#define MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE (12) +#define MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_RESULT (13) +#define MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE (14) +#define MP_BLUETOOTH_IRQ_GATTC_READ_RESULT (15) +#define MP_BLUETOOTH_IRQ_GATTC_READ_DONE (16) +#define MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE (17) +#define MP_BLUETOOTH_IRQ_GATTC_NOTIFY (18) +#define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) /* These aren't included in the module for space reasons, but can be used in your Python code if necessary. from micropython import const -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_GATTS_WRITE = const(1 << 2) -_IRQ_GATTS_READ_REQUEST = const(1 << 3) -_IRQ_SCAN_RESULT = const(1 << 4) -_IRQ_SCAN_COMPLETE = const(1 << 5) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_SERVICE_RESULT = const(1 << 8) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) -_IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10) -_IRQ_GATTC_READ_RESULT = const(1 << 11) -_IRQ_GATTC_WRITE_STATUS = const(1 << 12) -_IRQ_GATTC_NOTIFY = const(1 << 13) -_IRQ_GATTC_INDICATE = const(1 << 14) -_IRQ_ALL = const(0xffff) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_GATTS_READ_REQUEST = const(4) +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_DESCRIPTOR_RESULT = const(13) +_IRQ_GATTC_DESCRIPTOR_DONE = const(14) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) +_IRQ_GATTC_INDICATE = const(19) */ // Common UUID type. @@ -238,7 +240,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const // API implemented by modbluetooth (called by port-specific implementations): // Notify modbluetooth that a connection/disconnection event has occurred. -void mp_bluetooth_gap_on_connected_disconnected(uint16_t event, uint16_t conn_handle, uint8_t addr_type, const uint8_t *addr); +void mp_bluetooth_gap_on_connected_disconnected(uint8_t event, uint16_t conn_handle, uint8_t addr_type, const uint8_t *addr); // Call this when a characteristic is written to. void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle); @@ -267,7 +269,7 @@ void mp_bluetooth_gattc_on_descriptor_result(uint16_t conn_handle, uint16_t hand // Notify modbluetooth that a read has completed with data (or notify/indicate data available, use `event` to disambiguate). // Note: these functions are to be called in a group protected by MICROPY_PY_BLUETOOTH_ENTER/EXIT. // _start returns the number of bytes to submit to the calls to _chunk, followed by a call to _end. -size_t mp_bluetooth_gattc_on_data_available_start(uint16_t event, uint16_t conn_handle, uint16_t value_handle, size_t data_len, mp_uint_t *atomic_state_out); +size_t mp_bluetooth_gattc_on_data_available_start(uint8_t event, uint16_t conn_handle, uint16_t value_handle, size_t data_len, mp_uint_t *atomic_state_out); void mp_bluetooth_gattc_on_data_available_chunk(const uint8_t *data, size_t data_len); void mp_bluetooth_gattc_on_data_available_end(mp_uint_t atomic_state); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index e2e95aa74eb6b..656b99a564006 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2019 Damien P. George - * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2019-2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -612,7 +612,7 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC void gattc_on_data_available(uint16_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) { +STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) { size_t len = OS_MBUF_PKTLEN(om); mp_uint_t atomic_state; len = mp_bluetooth_gattc_on_data_available_start(event, conn_handle, value_handle, len, &atomic_state); diff --git a/py/ringbuf.h b/py/ringbuf.h index 293e418306051..4685848961c52 100644 --- a/py/ringbuf.h +++ b/py/ringbuf.h @@ -63,6 +63,13 @@ static inline int ringbuf_get(ringbuf_t *r) { return v; } +static inline int ringbuf_peek(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + return r->buf[r->iget]; +} + static inline int ringbuf_put(ringbuf_t *r, uint8_t v) { uint32_t iput_new = r->iput + 1; if (iput_new >= r->size) { From 6a3c89d584db4f14c7e2432b7c5ad87951e6c2d5 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 11 May 2020 21:46:56 +1000 Subject: [PATCH 035/352] extmod/modbluetooth: Add discover complete events for svc/char/desc. Without this it's difficult to implement a state machine correctly if the desired services are not found. --- examples/bluetooth/ble_temperature_central.py | 17 +++- extmod/btstack/modbluetooth_btstack.c | 89 +++++++++++++------ extmod/modbluetooth.c | 13 +++ extmod/modbluetooth.h | 3 + extmod/nimble/modbluetooth_nimble.c | 6 ++ 5 files changed, 100 insertions(+), 28 deletions(-) diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 8b679b9b807be..19dc93409e291 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -80,6 +80,8 @@ def _reset(self): # Connected device. self._conn_handle = None + self._start_handle = None + self._end_handle = None self._value_handle = None def _irq(self, event, data): @@ -124,18 +126,31 @@ def _irq(self, event, data): # Connected device returned a service. conn_handle, start_handle, end_handle, uuid = data if conn_handle == self._conn_handle and uuid == _ENV_SENSE_UUID: + self._start_handle, self._end_handle = start_handle, end_handle + + elif event == _IRQ_GATTC_SERVICES_DONE: + # Service query complete. + if self._start_handle and self._end_handle: self._ble.gattc_discover_characteristics( - self._conn_handle, start_handle, end_handle + self._conn_handle, self._start_handle, self._end_handle ) + else: + print("Failed to find environmental sensing service.") elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: # Connected device returned a characteristic. conn_handle, def_handle, value_handle, properties, uuid = data if conn_handle == self._conn_handle and uuid == _TEMP_UUID: self._value_handle = value_handle + + elif event == _IRQ_GATTC_CHARACTERISTICS_DONE: + # Characteristic query complete. + if self._value_handle: # We've finished connecting and discovering device, fire the connect callback. if self._conn_callback: self._conn_callback() + else: + print("Failed to find temperature characteristic.") elif event == _IRQ_GATTC_READ_RESULT: # A read completed successfully. diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 772fe4bed1054..625ccf4c4ad99 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -56,10 +56,17 @@ volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; STATIC btstack_packet_callback_registration_t hci_event_callback_registration; STATIC int btstack_error_to_errno(int err) { + DEBUG_EVENT_printf(" --> btstack error: %d\n", err); if (err == ERROR_CODE_SUCCESS) { return 0; - } else if (err == BTSTACK_ACL_BUFFERS_FULL) { + } else if (err == BTSTACK_ACL_BUFFERS_FULL || err == BTSTACK_MEMORY_ALLOC_FAILED) { return MP_ENOMEM; + } else if (err == GATT_CLIENT_IN_WRONG_STATE) { + return MP_EALREADY; + } else if (err == GATT_CLIENT_BUSY) { + return MP_EBUSY; + } else if (err == GATT_CLIENT_NOT_CONNECTED) { + return MP_ENOTCONN; } else { return MP_EINVAL; } @@ -78,10 +85,8 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu return result; } -STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, channel=%u, packet=%p, size=%u)\n", packet_type, channel, packet, size); +STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { + DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { return; } @@ -157,6 +162,18 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_ #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { DEBUG_EVENT_printf(" --> gatt query complete\n"); + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + if (irq == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { + // TODO there is no value_handle available to pass here + mp_bluetooth_gattc_on_read_write_status(irq, conn_handle, 0, status); + } else if (irq == MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE || + irq == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || + irq == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { + mp_bluetooth_gattc_on_discover_complete(irq, conn_handle, status); + } else { + // Note that query complete is fired for other operations like query value too. + } } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { DEBUG_EVENT_printf(" --> gatt service query result\n"); uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); @@ -208,31 +225,50 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint16_t channel, uint8_ len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_INDICATE, conn_handle, value_handle, len, &atomic_state); mp_bluetooth_gattc_on_data_available_chunk(data, len); mp_bluetooth_gattc_on_data_available_end(atomic_state); - #endif + #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else { DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } } +// Because the packet handler callbacks don't support an argument, we use a specific +// handler when we need to provide additional state to the handler (in the "irq" parameter). +// This is the generic handler for when you don't need extra state. +STATIC void btstack_packet_handler_generic(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + btstack_packet_handler(packet_type, packet, 0); +} + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { +// For when the handler is being used for service discovery. +STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - DEBUG_EVENT_printf("btstack_packet_handler_write_with_response(packet_type=%u, channel=%u, packet=%p, size=%u)\n", packet_type, channel, packet, size); - if (packet_type != HCI_EVENT_PACKET) { - return; - } + btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE); +} - uint8_t event_type = hci_event_packet_get_type(packet); - if (event_type == GATT_EVENT_QUERY_COMPLETE) { - DEBUG_EVENT_printf(" --> gatt query complete\n"); - uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); - uint8_t status = gatt_event_query_complete_get_att_status(packet); - // TODO there is no value_handle to pass here - mp_bluetooth_gattc_on_write_status(conn_handle, 0, status); - } +// For when the handler is being used for characteristic discovery. +STATIC void btstack_packet_handler_discover_characteristics(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE); } -#endif + +// For when the handler is being used for descriptor discovery. +STATIC void btstack_packet_handler_discover_descriptors(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE); +} + +// For when the handler is being used for write-with-response. +STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE); +} +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC btstack_timer_source_t btstack_init_deinit_timeout; @@ -285,7 +321,7 @@ int mp_bluetooth_init(void) { #endif // Register for HCI events. - hci_event_callback_registration.callback = &btstack_packet_handler; + hci_event_callback_registration.callback = &btstack_packet_handler_generic; hci_add_event_handler(&hci_event_callback_registration); // Set a timeout for HCI initialisation. @@ -314,7 +350,7 @@ int mp_bluetooth_init(void) { #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. - gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler, GATT_CLIENT_ANY_CONNECTION, NULL); + gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); #endif return 0; @@ -643,12 +679,11 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); - return btstack_error_to_errno(gatt_client_discover_primary_services(&btstack_packet_handler, conn_handle)); + return btstack_error_to_errno(gatt_client_discover_primary_services(&btstack_packet_handler_discover_services, conn_handle)); } int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); - gatt_client_service_t service = { // Only start/end handles needed for gatt_client_discover_characteristics_for_service. .start_group_handle = start_handle, @@ -656,7 +691,7 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s .uuid16 = 0, .uuid128 = {0}, }; - return btstack_error_to_errno(gatt_client_discover_characteristics_for_service(&btstack_packet_handler, conn_handle, &service)); + return btstack_error_to_errno(gatt_client_discover_characteristics_for_service(&btstack_packet_handler_discover_characteristics, conn_handle, &service)); } int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { @@ -670,12 +705,12 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start .uuid16 = 0, .uuid128 = {0}, }; - return btstack_error_to_errno(gatt_client_discover_characteristic_descriptors(&btstack_packet_handler, conn_handle, &characteristic)); + return btstack_error_to_errno(gatt_client_discover_characteristic_descriptors(&btstack_packet_handler_discover_descriptors, conn_handle, &characteristic)); } int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); - return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler, conn_handle, value_handle)); + return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_generic, conn_handle, value_handle)); } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index b43bec0178df8..7664e182951f2 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -886,6 +886,9 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_RESULT) { // conn_handle, handle, uuid ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, &o->irq_data_uuid, NULL); + } else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE || event == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || event == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { + // conn_handle, status + ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, NULL); } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_RESULT || event == MP_BLUETOOTH_IRQ_GATTC_NOTIFY || event == MP_BLUETOOTH_IRQ_GATTC_INDICATE) { // conn_handle, value_handle, data ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, &o->irq_data_data); @@ -1045,6 +1048,16 @@ void mp_bluetooth_gattc_on_descriptor_result(uint16_t conn_handle, uint16_t hand schedule_ringbuf(atomic_state); } +void mp_bluetooth_gattc_on_discover_complete(uint8_t event, uint16_t conn_handle, uint16_t status) { + MICROPY_PY_BLUETOOTH_ENTER + mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); + if (enqueue_irq(o, 2 + 2, event)) { + ringbuf_put16(&o->ringbuf, conn_handle); + ringbuf_put16(&o->ringbuf, status); + } + schedule_ringbuf(atomic_state); +} + size_t mp_bluetooth_gattc_on_data_available_start(uint8_t event, uint16_t conn_handle, uint16_t value_handle, size_t data_len, mp_uint_t *atomic_state_out) { MICROPY_PY_BLUETOOTH_ENTER *atomic_state_out = atomic_state; diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 091a62c845ceb..235622389801b 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -266,6 +266,9 @@ void mp_bluetooth_gattc_on_characteristic_result(uint16_t conn_handle, uint16_t // Notify modbluetooth that a descriptor was found. void mp_bluetooth_gattc_on_descriptor_result(uint16_t conn_handle, uint16_t handle, mp_obj_bluetooth_uuid_t *descriptor_uuid); +// Notify modbluetooth that service, characteristic or descriptor discovery has finished. +void mp_bluetooth_gattc_on_discover_complete(uint8_t event, uint16_t conn_handle, uint16_t status); + // Notify modbluetooth that a read has completed with data (or notify/indicate data available, use `event` to disambiguate). // Note: these functions are to be called in a group protected by MICROPY_PY_BLUETOOTH_ENTER/EXIT. // _start returns the number of bytes to submit to the calls to _chunk, followed by a call to _end. diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 656b99a564006..abe38d1f182a8 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -763,6 +763,8 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble if (error->status == 0) { mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(&service->uuid); mp_bluetooth_gattc_on_primary_service_result(conn_handle, service->start_handle, service->end_handle, &service_uuid); + } else { + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE, conn_handle, error->status); } return 0; } @@ -783,6 +785,8 @@ STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gat if (error->status == 0) { mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(&characteristic->uuid); mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic->def_handle, characteristic->val_handle, characteristic->properties, &characteristic_uuid); + } else { + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE, conn_handle, error->status); } return 0; } @@ -803,6 +807,8 @@ STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_er if (error->status == 0) { mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(&descriptor->uuid); mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor->handle, &descriptor_uuid); + } else { + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE, conn_handle, error->status); } return 0; } From 919d640aec637004110d9f4df9814523a3bfd847 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 12 May 2020 23:55:49 +1000 Subject: [PATCH 036/352] extmod/modbluetooth: Allow discovery of svc/char by uuid. In most situations this is a more efficient way of going straight to the service and characteristic you need. --- extmod/btstack/modbluetooth_btstack.c | 38 ++++++++++++++++++++++++--- extmod/modbluetooth.c | 22 ++++++++++------ extmod/modbluetooth.h | 4 +-- extmod/nimble/modbluetooth_nimble.c | 36 +++++++++++++++++-------- extmod/nimble/nimble/npl_os.c | 6 +++++ 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 625ccf4c4ad99..112c11a65eac6 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -677,12 +677,27 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, return btstack_error_to_errno(gap_connect(btstack_addr, addr_type)); } -int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { +int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); - return btstack_error_to_errno(gatt_client_discover_primary_services(&btstack_packet_handler_discover_services, conn_handle)); + uint8_t err; + if (uuid) { + if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { + err = gatt_client_discover_primary_services_by_uuid16(&btstack_packet_handler_discover_services, conn_handle, get_uuid16(uuid)); + } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { + uint8_t buffer[16]; + reverse_128(uuid->data, buffer); + err = gatt_client_discover_primary_services_by_uuid128(&btstack_packet_handler_discover_services, conn_handle, buffer); + } else { + DEBUG_EVENT_printf(" --> unknown UUID size\n"); + return MP_EINVAL; + } + } else { + err = gatt_client_discover_primary_services(&btstack_packet_handler_discover_services, conn_handle); + } + return btstack_error_to_errno(err); } -int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { +int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) { DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); gatt_client_service_t service = { // Only start/end handles needed for gatt_client_discover_characteristics_for_service. @@ -691,7 +706,22 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s .uuid16 = 0, .uuid128 = {0}, }; - return btstack_error_to_errno(gatt_client_discover_characteristics_for_service(&btstack_packet_handler_discover_characteristics, conn_handle, &service)); + uint8_t err; + if (uuid) { + if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { + err = gatt_client_discover_characteristics_for_service_by_uuid16(&btstack_packet_handler_discover_characteristics, conn_handle, &service, get_uuid16(uuid)); + } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { + uint8_t buffer[16]; + reverse_128(uuid->data, buffer); + err = gatt_client_discover_characteristics_for_service_by_uuid128(&btstack_packet_handler_discover_characteristics, conn_handle, &service, buffer); + } else { + DEBUG_EVENT_printf(" --> unknown UUID size\n"); + return MP_EINVAL; + } + } else { + err = gatt_client_discover_characteristics_for_service(&btstack_packet_handler_discover_characteristics, conn_handle, &service); + } + return btstack_error_to_errno(err); } int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 7664e182951f2..3d66bc84c3564 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -688,21 +688,27 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_set_buffer_obj, 3 #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC mp_obj_t bluetooth_ble_gattc_discover_services(mp_obj_t self_in, mp_obj_t conn_handle_in) { - (void)self_in; - mp_int_t conn_handle = mp_obj_get_int(conn_handle_in); - return bluetooth_handle_errno(mp_bluetooth_gattc_discover_primary_services(conn_handle)); +STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) { + mp_int_t conn_handle = mp_obj_get_int(args[1]); + mp_obj_bluetooth_uuid_t *uuid = NULL; + if (n_args == 3) { + uuid = MP_OBJ_TO_PTR(args[2]); + } + return bluetooth_handle_errno(mp_bluetooth_gattc_discover_primary_services(conn_handle, uuid)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_discover_services_obj, bluetooth_ble_gattc_discover_services); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_discover_services_obj, 2, 3, bluetooth_ble_gattc_discover_services); STATIC mp_obj_t bluetooth_ble_gattc_discover_characteristics(size_t n_args, const mp_obj_t *args) { - (void)n_args; mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t start_handle = mp_obj_get_int(args[2]); mp_int_t end_handle = mp_obj_get_int(args[3]); - return bluetooth_handle_errno(mp_bluetooth_gattc_discover_characteristics(conn_handle, start_handle, end_handle)); + mp_obj_bluetooth_uuid_t *uuid = NULL; + if (n_args == 3) { + uuid = MP_OBJ_TO_PTR(args[4]); + } + return bluetooth_handle_errno(mp_bluetooth_gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_discover_characteristics_obj, 4, 4, bluetooth_ble_gattc_discover_characteristics); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_discover_characteristics_obj, 4, 5, bluetooth_ble_gattc_discover_characteristics); STATIC mp_obj_t bluetooth_ble_gattc_discover_descriptors(size_t n_args, const mp_obj_t *args) { (void)n_args; diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 235622389801b..5d02d96e3c4c5 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -221,10 +221,10 @@ int mp_bluetooth_gap_scan_stop(void); int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms); // Find all primary services on the connected peripheral. -int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle); +int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid); // Find all characteristics on the specified service on a connected peripheral. -int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle); +int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid); // Find all descriptors on the specified characteristic on a connected peripheral. int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index abe38d1f182a8..4b0f258481c23 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -74,19 +74,19 @@ STATIC int ble_hs_err_to_errno(int err) { } // Note: modbluetooth UUIDs store their data in LE. -STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid) { +STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid, ble_uuid_any_t *storage) { if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - ble_uuid16_t *result = m_new(ble_uuid16_t, 1); + ble_uuid16_t *result = storage ? &storage->u16 : m_new(ble_uuid16_t, 1); result->u.type = BLE_UUID_TYPE_16; result->value = (uuid->data[1] << 8) | uuid->data[0]; return (ble_uuid_t *)result; } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) { - ble_uuid32_t *result = m_new(ble_uuid32_t, 1); + ble_uuid32_t *result = storage ? &storage->u32 : m_new(ble_uuid32_t, 1); result->u.type = BLE_UUID_TYPE_32; result->value = (uuid->data[1] << 24) | (uuid->data[1] << 16) | (uuid->data[1] << 8) | uuid->data[0]; return (ble_uuid_t *)result; } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { - ble_uuid128_t *result = m_new(ble_uuid128_t, 1); + ble_uuid128_t *result = storage ? &storage->u128 : m_new(ble_uuid128_t, 1); result->u.type = BLE_UUID_TYPE_128; memcpy(result->value, uuid->data, 16); return (ble_uuid_t *)result; @@ -498,7 +498,7 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m struct ble_gatt_chr_def *characteristics = m_new(struct ble_gatt_chr_def, num_characteristics + 1); for (size_t i = 0; i < num_characteristics; ++i) { - characteristics[i].uuid = create_nimble_uuid(characteristic_uuids[i]); + characteristics[i].uuid = create_nimble_uuid(characteristic_uuids[i], NULL); characteristics[i].access_cb = characteristic_access_cb; characteristics[i].arg = NULL; characteristics[i].flags = characteristic_flags[i]; @@ -512,7 +512,7 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m struct ble_gatt_dsc_def *descriptors = m_new(struct ble_gatt_dsc_def, num_descriptors[i] + 1); for (size_t j = 0; j < num_descriptors[i]; ++j) { - descriptors[j].uuid = create_nimble_uuid(descriptor_uuids[descriptor_index]); + descriptors[j].uuid = create_nimble_uuid(descriptor_uuids[descriptor_index], NULL); descriptors[j].access_cb = characteristic_access_cb; descriptors[j].att_flags = descriptor_flags[descriptor_index]; descriptors[j].min_key_size = 0; @@ -530,7 +530,7 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m struct ble_gatt_svc_def *service = m_new(struct ble_gatt_svc_def, 2); service[0].type = BLE_GATT_SVC_TYPE_PRIMARY; - service[0].uuid = create_nimble_uuid(service_uuid); + service[0].uuid = create_nimble_uuid(service_uuid, NULL); service[0].includes = NULL; service[0].characteristics = characteristics; service[1].type = 0; // no more services @@ -769,11 +769,18 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble return 0; } -int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) { +int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - int err = ble_gattc_disc_all_svcs(conn_handle, &peripheral_discover_service_cb, NULL); + int err; + if (uuid) { + ble_uuid_any_t nimble_uuid; + create_nimble_uuid(uuid, &nimble_uuid); + err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &peripheral_discover_service_cb, NULL); + } else { + err = ble_gattc_disc_all_svcs(conn_handle, &peripheral_discover_service_cb, NULL); + } return ble_hs_err_to_errno(err); } @@ -791,11 +798,18 @@ STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gat return 0; } -int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { +int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - int err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gatt_characteristic_cb, NULL); + int err; + if (uuid) { + ble_uuid_any_t nimble_uuid; + create_nimble_uuid(uuid, &nimble_uuid); + err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gatt_characteristic_cb, NULL); + } else { + err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gatt_characteristic_cb, NULL); + } return ble_hs_err_to_errno(err); } diff --git a/extmod/nimble/nimble/npl_os.c b/extmod/nimble/nimble/npl_os.c index 620dcb0aec13c..20c260405444f 100644 --- a/extmod/nimble/nimble/npl_os.c +++ b/extmod/nimble/nimble/npl_os.c @@ -127,6 +127,12 @@ void *nimble_realloc(void *ptr, size_t size) { return ptr2; } +// No-op implementation (only used by NimBLE logging). +int nimble_sprintf(char *str, const char *fmt, ...) { + str[0] = 0; + return 0; +} + /******************************************************************************/ // EVENTQ From c07ea3e4c2cfd9fe75a8b207a39d8bb0295fcc3b Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 2 Jun 2020 14:22:47 +1000 Subject: [PATCH 037/352] extmod/modbluetooth: Implement read done event. On btstack there's no status associated with the read result, it comes through as a separate event. This allows you to detect read failures or timeouts. --- examples/bluetooth/ble_temperature_central.py | 4 ++++ extmod/btstack/modbluetooth_btstack.c | 11 ++++++++--- extmod/modbluetooth.c | 6 +++--- extmod/modbluetooth.h | 4 ++-- extmod/nimble/modbluetooth_nimble.c | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 19dc93409e291..6195784622093 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -161,6 +161,10 @@ def _irq(self, event, data): self._read_callback(self._value) self._read_callback = None + elif event == _IRQ_GATTC_READ_DONE: + # Read completed. + conn_handle, value_handle, status = data + elif event == _IRQ_GATTC_NOTIFY: # The ble_temperature.py demo periodically notifies its value. conn_handle, value_handle, notify_data = data diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 112c11a65eac6..31b2825758434 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -171,8 +171,6 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { mp_bluetooth_gattc_on_discover_complete(irq, conn_handle, status); - } else { - // Note that query complete is fired for other operations like query value too. } } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { DEBUG_EVENT_printf(" --> gatt service query result\n"); @@ -262,6 +260,13 @@ STATIC void btstack_packet_handler_discover_descriptors(uint8_t packet_type, uin btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE); } +// For when the handler is being used for a read query. +STATIC void btstack_packet_handler_read(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_READ_DONE); +} + // For when the handler is being used for write-with-response. STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; @@ -740,7 +745,7 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); - return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_generic, conn_handle, value_handle)); + return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle)); } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 3d66bc84c3564..34d18b0b74776 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -898,7 +898,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_RESULT || event == MP_BLUETOOTH_IRQ_GATTC_NOTIFY || event == MP_BLUETOOTH_IRQ_GATTC_INDICATE) { // conn_handle, value_handle, data ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, &o->irq_data_data); - } else if (event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { + } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { // conn_handle, value_handle, status ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL); #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE @@ -1090,10 +1090,10 @@ void mp_bluetooth_gattc_on_data_available_end(mp_uint_t atomic_state) { schedule_ringbuf(atomic_state); } -void mp_bluetooth_gattc_on_write_status(uint16_t conn_handle, uint16_t value_handle, uint16_t status) { +void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status) { MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); - if (enqueue_irq(o, 2 + 2 + 2, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE)) { + if (enqueue_irq(o, 2 + 2 + 2, event)) { ringbuf_put16(&o->ringbuf, conn_handle); ringbuf_put16(&o->ringbuf, value_handle); ringbuf_put16(&o->ringbuf, status); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 5d02d96e3c4c5..c8b8dc63f2a67 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -276,8 +276,8 @@ size_t mp_bluetooth_gattc_on_data_available_start(uint8_t event, uint16_t conn_h void mp_bluetooth_gattc_on_data_available_chunk(const uint8_t *data, size_t data_len); void mp_bluetooth_gattc_on_data_available_end(mp_uint_t atomic_state); -// Notify modbluetooth that a write has completed. -void mp_bluetooth_gattc_on_write_status(uint16_t conn_handle, uint16_t value_handle, uint16_t status); +// Notify modbluetooth that a read or write operation has completed. +void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status); #endif // For stacks that don't manage attribute value data (currently all of them), helpers diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 4b0f258481c23..9b95d05f32890 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -844,6 +844,7 @@ STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_err if (error->status == 0) { gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, attr->handle, attr->om); } + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, attr->handle, error->status); return 0; } @@ -861,7 +862,7 @@ STATIC int ble_gatt_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_er if (!mp_bluetooth_is_active()) { return 0; } - mp_bluetooth_gattc_on_write_status(conn_handle, attr->handle, error->status); + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, attr->handle, error->status); return 0; } From 9902ce12eb59e0503072d9d980d5712e32b78fd2 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 13 May 2020 14:35:32 +1000 Subject: [PATCH 038/352] tests/multi_bluetooth: Update to work with new BLE events. Updates the tests to use non-bitmask events, event renames, as well as some of the new completion events to improve reliability of the tests. --- tests/multi_bluetooth/ble_characteristic.py | 72 +++++++++------- .../multi_bluetooth/ble_characteristic.py.exp | 10 ++- tests/multi_bluetooth/ble_gap_advertise.py | 6 +- tests/multi_bluetooth/ble_gap_connect.py | 55 ++++++------ tests/multi_bluetooth/ble_gap_device_name.py | 53 ++++++------ .../multi_bluetooth/ble_gatt_data_transfer.py | 86 +++++++++++-------- .../ble_gatt_data_transfer.py.exp | 13 +-- .../ble_gattc_discover_services.py | 45 ++++++---- tests/run-multitests.py | 2 +- 9 files changed, 197 insertions(+), 145 deletions(-) diff --git a/tests/multi_bluetooth/ble_characteristic.py b/tests/multi_bluetooth/ble_characteristic.py index b5dfefc840dd6..33d92b823bbb2 100644 --- a/tests/multi_bluetooth/ble_characteristic.py +++ b/tests/multi_bluetooth/ble_characteristic.py @@ -5,15 +5,17 @@ TIMEOUT_MS = 5000 -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_GATTS_WRITE = const(1 << 2) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) -_IRQ_GATTC_READ_RESULT = const(1 << 11) -_IRQ_GATTC_WRITE_STATUS = const(1 << 12) -_IRQ_GATTC_NOTIFY = const(1 << 13) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444") @@ -27,15 +29,13 @@ ) SERVICES = (SERVICE,) -last_event = None -last_data = None +waiting_event = None +waiting_data = None value_handle = 0 def irq(event, data): - global last_event, last_data, value_handle - last_event = event - last_data = data + global waiting_event, waiting_data, value_handle if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") elif event == _IRQ_CENTRAL_DISCONNECT: @@ -53,21 +53,32 @@ def irq(event, data): value_handle = data[2] elif event == _IRQ_GATTC_READ_RESULT: print("_IRQ_GATTC_READ_RESULT", data[-1]) - elif event == _IRQ_GATTC_WRITE_STATUS: - print("_IRQ_GATTC_WRITE_STATUS", data[-1]) + elif event == _IRQ_GATTC_READ_DONE: + print("_IRQ_GATTC_READ_DONE", data[-1]) + elif event == _IRQ_GATTC_WRITE_DONE: + print("_IRQ_GATTC_WRITE_DONE", data[-1]) elif event == _IRQ_GATTC_NOTIFY: print("_IRQ_GATTC_NOTIFY", data[-1]) + if waiting_event is not None: + if (isinstance(waiting_event, int) and event == waiting_event) or ( + not isinstance(waiting_event, int) and waiting_event(event, data) + ): + waiting_event = None + waiting_data = data + def wait_for_event(event, timeout_ms): + global waiting_event, waiting_data + waiting_event = event + waiting_data = None + t0 = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: - if isinstance(event, int): - if last_event == event: - break - elif event(): - break + if waiting_data: + return True machine.idle() + return False # Acting in peripheral role. @@ -82,17 +93,18 @@ def instance0(): ble.gatts_write(char_handle, "periph0") # Wait for central to connect to us. - wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_CENTRAL_CONNECT: + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data # Wait for a write to the characteristic from the central. wait_for_event(_IRQ_GATTS_WRITE, TIMEOUT_MS) # Wait a bit, then write the characteristic and notify it. time.sleep_ms(1000) + print("gatts_write") ble.gatts_write(char_handle, "periph1") + print("gatts_notify") ble.gatts_notify(conn_handle, char_handle) # Wait for a write to the characteristic from the central. @@ -100,6 +112,7 @@ def instance0(): # Wait a bit, then notify a new value on the characteristic. time.sleep_ms(1000) + print("gatts_notify") ble.gatts_notify(conn_handle, char_handle, "periph2") # Wait for the central to disconnect. @@ -115,14 +128,13 @@ def instance1(): # Connect to peripheral and then disconnect. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data # Discover characteristics. ble.gattc_discover_characteristics(conn_handle, 1, 65535) - wait_for_event(lambda: value_handle, TIMEOUT_MS) + wait_for_event(lambda event, data: value_handle, TIMEOUT_MS) # Issue read of characteristic, should get initial value. print("gattc_read") @@ -132,7 +144,7 @@ def instance1(): # Write to the characteristic, and ask for a response. print("gattc_write") ble.gattc_write(conn_handle, value_handle, "central0", 1) - wait_for_event(_IRQ_GATTC_WRITE_STATUS, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS) # Wait for a notify, then read new value. wait_for_event(_IRQ_GATTC_NOTIFY, TIMEOUT_MS) @@ -143,7 +155,7 @@ def instance1(): # Write to the characteristic, and ask for a response. print("gattc_write") ble.gattc_write(conn_handle, value_handle, "central1", 1) - wait_for_event(_IRQ_GATTC_WRITE_STATUS, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS) # Wait for a notify (should have new data), then read old value (should be unchanged). wait_for_event(_IRQ_GATTC_NOTIFY, TIMEOUT_MS) diff --git a/tests/multi_bluetooth/ble_characteristic.py.exp b/tests/multi_bluetooth/ble_characteristic.py.exp index 1532574d9882c..d89842ef7b097 100644 --- a/tests/multi_bluetooth/ble_characteristic.py.exp +++ b/tests/multi_bluetooth/ble_characteristic.py.exp @@ -2,7 +2,10 @@ gap_advertise _IRQ_CENTRAL_CONNECT _IRQ_GATTS_WRITE b'central0' +gatts_write +gatts_notify _IRQ_GATTS_WRITE b'central1' +gatts_notify _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect @@ -10,15 +13,18 @@ _IRQ_PERIPHERAL_CONNECT _IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000000-1111-2222-3333-444444444444') gattc_read _IRQ_GATTC_READ_RESULT b'periph0' +_IRQ_GATTC_READ_DONE 0 gattc_write -_IRQ_GATTC_WRITE_STATUS 0 +_IRQ_GATTC_WRITE_DONE 0 _IRQ_GATTC_NOTIFY b'periph1' gattc_read _IRQ_GATTC_READ_RESULT b'periph1' +_IRQ_GATTC_READ_DONE 0 gattc_write -_IRQ_GATTC_WRITE_STATUS 0 +_IRQ_GATTC_WRITE_DONE 0 _IRQ_GATTC_NOTIFY b'periph2' gattc_read _IRQ_GATTC_READ_RESULT b'central1' +_IRQ_GATTC_READ_DONE 0 gap_disconnect: True _IRQ_PERIPHERAL_DISCONNECT diff --git a/tests/multi_bluetooth/ble_gap_advertise.py b/tests/multi_bluetooth/ble_gap_advertise.py index 644b1fe28d3e8..71a5b58a56f7a 100644 --- a/tests/multi_bluetooth/ble_gap_advertise.py +++ b/tests/multi_bluetooth/ble_gap_advertise.py @@ -3,8 +3,8 @@ from micropython import const import time, machine, bluetooth -_IRQ_SCAN_RESULT = const(1 << 4) -_IRQ_SCAN_COMPLETE = const(1 << 5) +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) ADV_TIME_S = 3 @@ -43,7 +43,7 @@ def irq(ev, data): else: if adv_data != data[4]: adv_data = b"MISMATCH" - elif ev == _IRQ_SCAN_COMPLETE: + elif ev == _IRQ_SCAN_DONE: finished = True ble.config(rxbuf=2000) diff --git a/tests/multi_bluetooth/ble_gap_connect.py b/tests/multi_bluetooth/ble_gap_connect.py index def256f2c8f51..8e3ed8b816b3a 100644 --- a/tests/multi_bluetooth/ble_gap_connect.py +++ b/tests/multi_bluetooth/ble_gap_connect.py @@ -5,19 +5,17 @@ TIMEOUT_MS = 4000 -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) -last_event = None -last_data = None +waiting_event = None +waiting_data = None def irq(event, data): - global last_event, last_data - last_event = event - last_data = data + global waiting_event, waiting_data if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") elif event == _IRQ_CENTRAL_DISCONNECT: @@ -27,11 +25,23 @@ def irq(event, data): elif event == _IRQ_PERIPHERAL_DISCONNECT: print("_IRQ_PERIPHERAL_DISCONNECT") + if waiting_event is not None: + if event == waiting_event: + waiting_event = None + waiting_data = data + def wait_for_event(event, timeout_ms): + global waiting_event, waiting_data + waiting_event = event + waiting_data = None + t0 = time.ticks_ms() - while last_event != event and time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: + while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: + if waiting_data: + return True machine.idle() + return False # Acting in peripheral role. @@ -42,20 +52,20 @@ def instance0(): multitest.next() try: # Wait for central to connect, then wait for it to disconnect. - wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) - wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) - if last_event != _IRQ_CENTRAL_DISCONNECT: + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + return + if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS): return # Start advertising again. ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") # Wait for central to connect, then disconnect it. - wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_CENTRAL_CONNECT: + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + return + print("gap_disconnect:", ble.gap_disconnect(waiting_data[0])) + if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS): return - print("gap_disconnect:", ble.gap_disconnect(last_data[0])) - wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) finally: ble.active(0) @@ -67,12 +77,10 @@ def instance1(): # Connect to peripheral and then disconnect. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return - print("gap_disconnect:", ble.gap_disconnect(last_data[0])) - wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_DISCONNECT: + print("gap_disconnect:", ble.gap_disconnect(waiting_data[0])) + if not wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS): return # Wait for peripheral to start advertising again. @@ -81,8 +89,7 @@ def instance1(): # Connect to peripheral and then let the peripheral disconnect us. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) finally: diff --git a/tests/multi_bluetooth/ble_gap_device_name.py b/tests/multi_bluetooth/ble_gap_device_name.py index ee4a7a544042c..dafa367128b49 100644 --- a/tests/multi_bluetooth/ble_gap_device_name.py +++ b/tests/multi_bluetooth/ble_gap_device_name.py @@ -5,24 +5,24 @@ TIMEOUT_MS = 5000 -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) -_IRQ_GATTC_READ_RESULT = const(1 << 11) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) GAP_DEVICE_NAME_UUID = bluetooth.UUID(0x2A00) -last_event = None -last_data = None +waiting_event = None +waiting_data = None value_handle = 0 def irq(event, data): - global last_event, last_data, value_handle - last_event = event - last_data = data + global waiting_event, waiting_data, value_handle if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") elif event == _IRQ_CENTRAL_DISCONNECT: @@ -38,16 +38,23 @@ def irq(event, data): elif event == _IRQ_GATTC_READ_RESULT: print("_IRQ_GATTC_READ_RESULT", data[-1]) + if waiting_event is not None: + if isinstance(waiting_event, int) and event == waiting_event: + waiting_event = None + waiting_data = data + def wait_for_event(event, timeout_ms): + global waiting_event, waiting_data + waiting_event = event + waiting_data = None + t0 = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: - if isinstance(event, int): - if last_event == event: - break - elif event(): - break + if waiting_data: + return True machine.idle() + return False # Acting in peripheral role. @@ -72,9 +79,9 @@ def instance0(): ble.gap_advertise(20_000) # Wait for central to connect, then wait for it to disconnect. - wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) - wait_for_event(_IRQ_CENTRAL_DISCONNECT, 4 * TIMEOUT_MS) - if last_event != _IRQ_CENTRAL_DISCONNECT: + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + return + if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, 4 * TIMEOUT_MS): return finally: ble.active(0) @@ -91,10 +98,9 @@ def instance1(): # Connect to peripheral. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data if iteration == 0: print("gattc_discover_characteristics") @@ -111,8 +117,7 @@ def instance1(): # Disconnect from peripheral. print("gap_disconnect:", ble.gap_disconnect(conn_handle)) - wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_DISCONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS): return finally: ble.active(0) diff --git a/tests/multi_bluetooth/ble_gatt_data_transfer.py b/tests/multi_bluetooth/ble_gatt_data_transfer.py index 1be557f9b938a..dba0c333be672 100644 --- a/tests/multi_bluetooth/ble_gatt_data_transfer.py +++ b/tests/multi_bluetooth/ble_gatt_data_transfer.py @@ -5,16 +5,19 @@ TIMEOUT_MS = 5000 -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_GATTS_WRITE = const(1 << 2) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_SERVICE_RESULT = const(1 << 8) -_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) -_IRQ_GATTC_READ_RESULT = const(1 << 11) -_IRQ_GATTC_WRITE_STATUS = const(1 << 12) -_IRQ_GATTC_NOTIFY = const(1 << 13) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) SERVICE_UUID = bluetooth.UUID("00000001-1111-2222-3333-444444444444") CHAR_CTRL_UUID = bluetooth.UUID("00000002-1111-2222-3333-444444444444") @@ -26,17 +29,15 @@ SERVICE = (SERVICE_UUID, (CHAR_CTRL, CHAR_RX, CHAR_TX)) SERVICES = (SERVICE,) -last_event = None -last_data = None +waiting_event = None +waiting_data = None ctrl_value_handle = 0 rx_value_handle = 0 tx_value_handle = 0 def irq(event, data): - global last_event, last_data, ctrl_value_handle, rx_value_handle, tx_value_handle - last_event = event - last_data = data + global waiting_event, waiting_data, ctrl_value_handle, rx_value_handle, tx_value_handle if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") elif event == _IRQ_CENTRAL_DISCONNECT: @@ -49,31 +50,44 @@ def irq(event, data): print("_IRQ_PERIPHERAL_DISCONNECT") elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: if data[-1] == CHAR_CTRL_UUID: - print("_IRQ_GATTC_SERVICE_RESULT", data[-1]) + print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) ctrl_value_handle = data[2] elif data[-1] == CHAR_RX_UUID: - print("_IRQ_GATTC_SERVICE_RESULT", data[-1]) + print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) rx_value_handle = data[2] elif data[-1] == CHAR_TX_UUID: - print("_IRQ_GATTC_SERVICE_RESULT", data[-1]) + print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) tx_value_handle = data[2] + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: + print("_IRQ_GATTC_CHARACTERISTIC_DONE") elif event == _IRQ_GATTC_READ_RESULT: print("_IRQ_GATTC_READ_RESULT", data[-1]) - elif event == _IRQ_GATTC_WRITE_STATUS: - print("_IRQ_GATTC_WRITE_STATUS", data[-1]) + elif event == _IRQ_GATTC_READ_DONE: + print("_IRQ_GATTC_READ_DONE", data[-1]) + elif event == _IRQ_GATTC_WRITE_DONE: + print("_IRQ_GATTC_WRITE_DONE", data[-1]) elif event == _IRQ_GATTC_NOTIFY: print("_IRQ_GATTC_NOTIFY", data[-1]) + if waiting_event is not None: + if (isinstance(waiting_event, int) and event == waiting_event) or ( + not isinstance(waiting_event, int) and waiting_event(event, data) + ): + waiting_event = None + waiting_data = data + def wait_for_event(event, timeout_ms): + global waiting_event, waiting_data + waiting_event = event + waiting_data = None + t0 = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: - if isinstance(event, int): - if last_event == event: - break - elif event(): - break + if waiting_data: + return True machine.idle() + return False # Acting in peripheral role. @@ -89,14 +103,13 @@ def instance0(): multitest.next() try: # Wait for central to connect to us. - wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_CENTRAL_CONNECT: + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data # Wait for the central to signal that it's done with its part of the test. wait_for_event( - lambda: last_event == _IRQ_GATTS_WRITE and last_data[1] == char_ctrl_handle, + lambda event, data: event == _IRQ_GATTS_WRITE and data[1] == char_ctrl_handle, 2 * TIMEOUT_MS, ) @@ -125,32 +138,33 @@ def instance1(): # Connect to peripheral and then disconnect. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data # Discover characteristics. ble.gattc_discover_characteristics(conn_handle, 1, 65535) wait_for_event( - lambda: ctrl_value_handle and rx_value_handle and tx_value_handle, TIMEOUT_MS + lambda event, data: ctrl_value_handle and rx_value_handle and tx_value_handle, + TIMEOUT_MS, ) + wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS) # Write to the characteristic a few times, with and without response. for i in range(4): print("gattc_write") ble.gattc_write(conn_handle, rx_value_handle, "central{}".format(i), i & 1) if i & 1: - wait_for_event(_IRQ_GATTC_WRITE_STATUS, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS) time.sleep_ms(400) # Write to say that we are done with our part of the test. ble.gattc_write(conn_handle, ctrl_value_handle, "OK", 1) - wait_for_event(_IRQ_GATTC_WRITE_STATUS, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS) # Wait for notification that peripheral is done with its part of the test. wait_for_event( - lambda: last_event == _IRQ_GATTC_NOTIFY and last_data[1] == ctrl_value_handle, + lambda event, data: event == _IRQ_GATTC_NOTIFY and data[1] == ctrl_value_handle, TIMEOUT_MS, ) diff --git a/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp b/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp index 6ecd48225f5fe..a1b3cbcd07374 100644 --- a/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp +++ b/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp @@ -11,16 +11,17 @@ _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect _IRQ_PERIPHERAL_CONNECT -_IRQ_GATTC_SERVICE_RESULT UUID128('00000002-1111-2222-3333-444444444444') -_IRQ_GATTC_SERVICE_RESULT UUID128('00000003-1111-2222-3333-444444444444') -_IRQ_GATTC_SERVICE_RESULT UUID128('00000004-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000002-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000003-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000004-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE gattc_write gattc_write -_IRQ_GATTC_WRITE_STATUS 0 +_IRQ_GATTC_WRITE_DONE 0 gattc_write gattc_write -_IRQ_GATTC_WRITE_STATUS 0 -_IRQ_GATTC_WRITE_STATUS 0 +_IRQ_GATTC_WRITE_DONE 0 +_IRQ_GATTC_WRITE_DONE 0 _IRQ_GATTC_NOTIFY b'message0' _IRQ_GATTC_NOTIFY b'message1' _IRQ_GATTC_NOTIFY b'message2' diff --git a/tests/multi_bluetooth/ble_gattc_discover_services.py b/tests/multi_bluetooth/ble_gattc_discover_services.py index b01a890c72038..e746b87458e1b 100644 --- a/tests/multi_bluetooth/ble_gattc_discover_services.py +++ b/tests/multi_bluetooth/ble_gattc_discover_services.py @@ -5,11 +5,12 @@ TIMEOUT_MS = 5000 -_IRQ_CENTRAL_CONNECT = const(1 << 0) -_IRQ_CENTRAL_DISCONNECT = const(1 << 1) -_IRQ_PERIPHERAL_CONNECT = const(1 << 6) -_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) -_IRQ_GATTC_SERVICE_RESULT = const(1 << 8) +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) UUID_A = bluetooth.UUID(0x180D) UUID_B = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") @@ -23,15 +24,13 @@ ) SERVICES = (SERVICE_A, SERVICE_B) -last_event = None -last_data = None +waiting_event = None +waiting_data = None num_service_result = 0 def irq(event, data): - global last_event, last_data, num_service_result - last_event = event - last_data = data + global waiting_event, waiting_data, num_service_result if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") elif event == _IRQ_CENTRAL_DISCONNECT: @@ -45,16 +44,25 @@ def irq(event, data): print("_IRQ_GATTC_SERVICE_RESULT", data[3]) num_service_result += 1 + if waiting_event is not None: + if (isinstance(waiting_event, int) and event == waiting_event) or ( + not isinstance(waiting_event, int) and waiting_event(event, data) + ): + waiting_event = None + waiting_data = data + def wait_for_event(event, timeout_ms): + global waiting_event, waiting_data + waiting_event = event + waiting_data = None + t0 = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: - if isinstance(event, int): - if last_event == event: - break - elif event(): - break + if waiting_data: + return True machine.idle() + return False # Acting in peripheral role. @@ -78,14 +86,13 @@ def instance1(): # Connect to peripheral and then disconnect. print("gap_connect") ble.gap_connect(0, BDADDR) - wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) - if last_event != _IRQ_PERIPHERAL_CONNECT: + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return - conn_handle, _, _ = last_data + conn_handle, _, _ = waiting_data # Discover services. ble.gattc_discover_services(conn_handle) - wait_for_event(lambda: num_service_result == 2, TIMEOUT_MS) + wait_for_event(lambda event, data: num_service_result == 2, TIMEOUT_MS) # Disconnect from peripheral. print("gap_disconnect:", ble.gap_disconnect(conn_handle)) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index c83895fb65f37..567e9b5ffa388 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -289,7 +289,7 @@ def run_test_on_instances(test_file, num_instances, instances): continue num_output += 1 last_read_time[idx] = time.time() - if out is not None: + if out is not None and not any(m in out for m in IGNORE_OUTPUT_MATCHES): trace_instance_output(idx, out) output[idx].append(out) if err is not None: From 9708fe8788eebb87bf3b0d6232dbed2012cf7358 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 13 May 2020 16:49:57 +1000 Subject: [PATCH 039/352] docs/library: Update ubluetooth for new events and discover by uuid. --- docs/library/ubluetooth.rst | 84 ++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index fd2efe87d913f..ee8fd75ade227 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -65,15 +65,12 @@ Configuration Event Handling -------------- -.. method:: BLE.irq(handler, trigger=0xffff) +.. method:: BLE.irq(handler) Registers a callback for events from the BLE stack. The *handler* takes two arguments, ``event`` (which will be one of the codes below) and ``data`` (which is an event-specific tuple of values). - The optional *trigger* parameter allows you to set a mask of events that - your program is interested in. The default is all events. - Note: the ``addr``, ``adv_data``, ``char_data``, ``notify_data``, and ``uuid`` entries in the tuples are references to data managed by the :mod:`ubluetooth` module (i.e. the same @@ -101,7 +98,7 @@ Event Handling elif event == _IRQ_SCAN_RESULT: # A single scan result. addr_type, addr, adv_type, rssi, adv_data = data - elif event == _IRQ_SCAN_COMPLETE: + elif event == _IRQ_SCAN_DONE: # Scan duration finished or manually stopped. pass elif event == _IRQ_PERIPHERAL_CONNECT: @@ -113,17 +110,31 @@ Event Handling elif event == _IRQ_GATTC_SERVICE_RESULT: # Called for each service found by gattc_discover_services(). conn_handle, start_handle, end_handle, uuid = data + elif event == _IRQ_GATTC_SERVICE_DONE: + # Called once service discovery is complete. + conn_handle, status = data elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: # Called for each characteristic found by gattc_discover_services(). conn_handle, def_handle, value_handle, properties, uuid = data + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: + # Called once service discovery is complete. + conn_handle, status = data elif event == _IRQ_GATTC_DESCRIPTOR_RESULT: # Called for each descriptor found by gattc_discover_descriptors(). conn_handle, dsc_handle, uuid = data + elif event == _IRQ_GATTC_DESCRIPTOR_DONE: + # Called once service discovery is complete. + conn_handle, status = data elif event == _IRQ_GATTC_READ_RESULT: # A gattc_read() has completed. conn_handle, value_handle, char_data = data - elif event == _IRQ_GATTC_WRITE_STATUS: + elif event == _IRQ_GATTC_READ_DONE: + # A gattc_read() has completed. + # Note: The value_handle will be zero on btstack (but present on NimBLE). + conn_handle, value_handle, status = data + elif event == _IRQ_GATTC_WRITE_DONE: # A gattc_write() has completed. + # Note: The value_handle will be zero on btstack (but present on NimBLE). conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: # A peripheral has sent a notify request. @@ -135,21 +146,25 @@ Event Handling The event codes are:: from micropython import const - _IRQ_CENTRAL_CONNECT = const(1 << 0) - _IRQ_CENTRAL_DISCONNECT = const(1 << 1) - _IRQ_GATTS_WRITE = const(1 << 2) - _IRQ_GATTS_READ_REQUEST = const(1 << 3) - _IRQ_SCAN_RESULT = const(1 << 4) - _IRQ_SCAN_COMPLETE = const(1 << 5) - _IRQ_PERIPHERAL_CONNECT = const(1 << 6) - _IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) - _IRQ_GATTC_SERVICE_RESULT = const(1 << 8) - _IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) - _IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10) - _IRQ_GATTC_READ_RESULT = const(1 << 11) - _IRQ_GATTC_WRITE_STATUS = const(1 << 12) - _IRQ_GATTC_NOTIFY = const(1 << 13) - _IRQ_GATTC_INDICATE = const(1 << 14) + _IRQ_CENTRAL_CONNECT = const(1) + _IRQ_CENTRAL_DISCONNECT = const(2) + _IRQ_GATTS_WRITE = const(3) + _IRQ_GATTS_READ_REQUEST = const(4) + _IRQ_SCAN_RESULT = const(5) + _IRQ_SCAN_DONE = const(6) + _IRQ_PERIPHERAL_CONNECT = const(7) + _IRQ_PERIPHERAL_DISCONNECT = const(8) + _IRQ_GATTC_SERVICE_RESULT = const(9) + _IRQ_GATTC_SERVICE_DONE = const(10) + _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) + _IRQ_GATTC_CHARACTERISTIC_DONE = const(12) + _IRQ_GATTC_DESCRIPTOR_RESULT = const(13) + _IRQ_GATTC_DESCRIPTOR_DONE = const(14) + _IRQ_GATTC_READ_RESULT = const(15) + _IRQ_GATTC_READ_DONE = const(16) + _IRQ_GATTC_WRITE_DONE = const(17) + _IRQ_GATTC_NOTIFY = const(18) + _IRQ_GATTC_INDICATE = const(19) In order to save space in the firmware, these constants are not included on the :mod:`ubluetooth` module. Add the ones that you need from the list above to your @@ -203,7 +218,7 @@ Observer Role (Scanner) * 0x04 - SCAN_RSP - scan response When scanning is stopped (either due to the duration finishing or when - explicitly stopped), the ``_IRQ_SCAN_COMPLETE`` event will be raised. + explicitly stopped), the ``_IRQ_SCAN_DONE`` event will be raised. Peripheral Role (GATT Server) @@ -313,33 +328,42 @@ Central Role (GATT Client) Returns ``False`` if the connection handle wasn't connected, and ``True`` otherwise. -.. method:: BLE.gattc_discover_services(conn_handle) +.. method:: BLE.gattc_discover_services(conn_handle, [uuid]) Query a connected peripheral for its services. - For each service discovered, the ``_IRQ_GATTC_SERVICE_RESULT`` event will be - raised. + Optionally specify a service *uuid* to query for that service only. -.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle) + For each service discovered, the ``_IRQ_GATTC_SERVICE_RESULT`` event will + be raised, followed by ``_IRQ_GATTC_SERVICE_DONE`` on completion. + +.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle, [uuid]) Query a connected peripheral for characteristics in the specified range. + Optionally specify a characteristic *uuid* to query for that + characteristic only. + + You can use ``start_handle=1``, ``end_handle=0xffff`` to search for a + characteristic in any service. + For each characteristic discovered, the ``_IRQ_GATTC_CHARACTERISTIC_RESULT`` - event will be raised. + event will be raised, followed by ``_IRQ_GATTC_CHARACTERISTIC_DONE`` on completion. .. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle) Query a connected peripheral for descriptors in the specified range. For each descriptor discovered, the ``_IRQ_GATTC_DESCRIPTOR_RESULT`` event - will be raised. + will be raised, followed by ``_IRQ_GATTC_DESCRIPTOR_DONE`` on completion. .. method:: BLE.gattc_read(conn_handle, value_handle) Issue a remote read to a connected peripheral for the specified characteristic or descriptor handle. - On success, the ``_IRQ_GATTC_READ_RESULT`` event will be raised. + When a value is available, the ``_IRQ_GATTC_READ_RESULT`` event will be + raised. Additionally, the ``_IRQ_GATTC_READ_DONE`` will be raised. .. method:: BLE.gattc_write(conn_handle, value_handle, data, mode=0, /) @@ -357,7 +381,7 @@ Central Role (GATT Client) data. If a response is received from the remote peripheral the - ``_IRQ_GATTC_WRITE_STATUS`` event will be raised. + ``_IRQ_GATTC_WRITE_DONE`` event will be raised. class UUID From 1cad63c0bcf63b12436a1438aa8512cd05ad4da0 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 19 May 2020 16:00:23 +1000 Subject: [PATCH 040/352] extmod/modbluetooth: Ensure status=0 always on success. This commit makes sure that all discovery complete and read/write status events set the status to zero on success. The status value will be implementation-dependent on non-success cases. --- docs/library/ubluetooth.rst | 5 +++++ examples/bluetooth/ble_temperature_central.py | 2 +- extmod/nimble/modbluetooth_nimble.c | 7 +++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index ee8fd75ade227..f96a7caaf828e 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -112,18 +112,21 @@ Event Handling conn_handle, start_handle, end_handle, uuid = data elif event == _IRQ_GATTC_SERVICE_DONE: # Called once service discovery is complete. + # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = data elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: # Called for each characteristic found by gattc_discover_services(). conn_handle, def_handle, value_handle, properties, uuid = data elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: # Called once service discovery is complete. + # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = data elif event == _IRQ_GATTC_DESCRIPTOR_RESULT: # Called for each descriptor found by gattc_discover_descriptors(). conn_handle, dsc_handle, uuid = data elif event == _IRQ_GATTC_DESCRIPTOR_DONE: # Called once service discovery is complete. + # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = data elif event == _IRQ_GATTC_READ_RESULT: # A gattc_read() has completed. @@ -131,10 +134,12 @@ Event Handling elif event == _IRQ_GATTC_READ_DONE: # A gattc_read() has completed. # Note: The value_handle will be zero on btstack (but present on NimBLE). + # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_WRITE_DONE: # A gattc_write() has completed. # Note: The value_handle will be zero on btstack (but present on NimBLE). + # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: # A peripheral has sent a notify request. diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 6195784622093..1ce99728b37d3 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -162,7 +162,7 @@ def _irq(self, event, data): self._read_callback = None elif event == _IRQ_GATTC_READ_DONE: - # Read completed. + # Read completed (no-op). conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 9b95d05f32890..05ec6117791da 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -764,7 +764,7 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(&service->uuid); mp_bluetooth_gattc_on_primary_service_result(conn_handle, service->start_handle, service->end_handle, &service_uuid); } else { - mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE, conn_handle, error->status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE, conn_handle, error->status == BLE_HS_EDONE ? 0 : error->status); } return 0; } @@ -793,7 +793,7 @@ STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gat mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(&characteristic->uuid); mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic->def_handle, characteristic->val_handle, characteristic->properties, &characteristic_uuid); } else { - mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE, conn_handle, error->status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE, conn_handle, error->status == BLE_HS_EDONE ? 0 : error->status); } return 0; } @@ -822,7 +822,7 @@ STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_er mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(&descriptor->uuid); mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor->handle, &descriptor_uuid); } else { - mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE, conn_handle, error->status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE, conn_handle, error->status == BLE_HS_EDONE ? 0 : error->status); } return 0; } @@ -840,7 +840,6 @@ STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_err if (!mp_bluetooth_is_active()) { return 0; } - // TODO: Maybe send NULL if error->status non-zero. if (error->status == 0) { gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, attr->handle, attr->om); } From 8b7ae4e099c18d839d1999c49dd8a1795bf9d1ae Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 28 May 2020 10:51:45 +1000 Subject: [PATCH 041/352] extmod/modbluetooth: Support bigger characteristic values. The ring buffer previously used a single unsigned byte field to save the length, meaning that it would overflow for large characteristic value responses. With this commit it now use a 16-bit length instead and has code to explicitly truncate at UINT16_MAX (although this should be impossible to achieve in practice). --- extmod/modbluetooth.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 34d18b0b74776..da40a1548541d 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -835,7 +835,7 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size // put more than bt->irq_data_data_alloc bytes into the ringbuf, because // that's what's available here in bt->irq_data_bytes. if (bytes_data) { - bytes_data->len = ringbuf_get(ringbuf); + bytes_data->len = ringbuf_get16(ringbuf); for (size_t i = 0; i < bytes_data->len; ++i) { // cast away const, this is actually bt->irq_data_bytes. ((uint8_t *)bytes_data->data)[i] = ringbuf_get(ringbuf); @@ -1001,7 +1001,7 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); data_len = MIN(o->irq_data_data_alloc, data_len); - if (enqueue_irq(o, 1 + 6 + 1 + 1 + 1 + data_len, MP_BLUETOOTH_IRQ_SCAN_RESULT)) { + if (enqueue_irq(o, 1 + 6 + 1 + 1 + 2 + data_len, MP_BLUETOOTH_IRQ_SCAN_RESULT)) { ringbuf_put(&o->ringbuf, addr_type); for (int i = 0; i < 6; ++i) { ringbuf_put(&o->ringbuf, addr[i]); @@ -1010,7 +1010,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin ringbuf_put(&o->ringbuf, adv_type); // Note conversion of int8_t rssi to uint8_t. Must un-convert on the way out. ringbuf_put(&o->ringbuf, (uint8_t)rssi); - ringbuf_put(&o->ringbuf, data_len); + // Length field is 16-bit. + data_len = MIN(UINT16_MAX, data_len); + ringbuf_put16(&o->ringbuf, data_len); for (size_t i = 0; i < data_len; ++i) { ringbuf_put(&o->ringbuf, data[i]); } @@ -1069,10 +1071,12 @@ size_t mp_bluetooth_gattc_on_data_available_start(uint8_t event, uint16_t conn_h *atomic_state_out = atomic_state; mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); data_len = MIN(o->irq_data_data_alloc, data_len); - if (enqueue_irq(o, 2 + 2 + 1 + data_len, event)) { + if (enqueue_irq(o, 2 + 2 + 2 + data_len, event)) { ringbuf_put16(&o->ringbuf, conn_handle); ringbuf_put16(&o->ringbuf, value_handle); - ringbuf_put(&o->ringbuf, data_len); + // Length field is 16-bit. + data_len = MIN(UINT16_MAX, data_len); + ringbuf_put16(&o->ringbuf, data_len); return data_len; } else { return 0; From 834b482e67ebb1b9ad250d62ddfa1275824a7e19 Mon Sep 17 00:00:00 2001 From: jxltom Date: Sat, 9 May 2020 14:34:15 +0800 Subject: [PATCH 042/352] examples/bluetooth: Fix incorrect value of BR/EDR flag in advertising. According to Supplement to the Bluetooth Core Specification v8 Part A 1.3.1, to support BR/EDR the code should set the fifth bit (Simultaneous LE and BR/EDR to Same Device Capable (Controller)) and fourth bit (Simultaneous LE and BR/EDR to Same Device Capable (Host)) of the flag. --- examples/bluetooth/ble_advertising.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/bluetooth/ble_advertising.py b/examples/bluetooth/ble_advertising.py index 3fb1281f636ee..a496119967a5d 100644 --- a/examples/bluetooth/ble_advertising.py +++ b/examples/bluetooth/ble_advertising.py @@ -30,7 +30,7 @@ def _append(adv_type, value): _append( _ADV_TYPE_FLAGS, - struct.pack("B", (0x01 if limited_disc else 0x02) + (0x00 if br_edr else 0x04)), + struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)), ) if name: From 596fb73927df4f9202729ff9d92a1db479a95ed7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 2 Jun 2020 16:24:53 +1000 Subject: [PATCH 043/352] qemu-arm: Support building in debug mode with DEBUG=1. Fixes issue #6095. --- ports/qemu-arm/Makefile | 4 ++-- ports/qemu-arm/startup.c | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 430740ccf73f7..e7b6523f88c2f 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -43,9 +43,9 @@ INC += -I$(BUILD) CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \ -ffunction-sections -fdata-sections -#Debugging/Optimization +# Debugging/Optimization ifeq ($(DEBUG), 1) -CFLAGS += -g -DPENDSV_DEBUG +CFLAGS += -g COPT = -O0 else COPT += -Os -DNDEBUG diff --git a/ports/qemu-arm/startup.c b/ports/qemu-arm/startup.c index 002d5ef62418a..58bdf7af9e98e 100644 --- a/ports/qemu-arm/startup.c +++ b/ports/qemu-arm/startup.c @@ -1,4 +1,5 @@ #include +#include #include #include "uart.h" @@ -73,6 +74,14 @@ __attribute__((naked)) void exit(int status) { } } +#ifndef NDEBUG +void __assert_func(const char *file, int line, const char *func, const char *expr) { + (void)func; + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + exit(1); +} +#endif + // The following are needed for tinytest #include From 621f40b12c272788533dcafcef62ed2a3449bb3b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 2 Jun 2020 16:42:37 +1000 Subject: [PATCH 044/352] esp32/mpthreadport: Fix calculation of thread stack size. With this commit the code should work correctly regardless of the size of StackType_t (it's actually 1 byte in size for the esp32's custom FreeRTOS). Fixes issue #6072. --- ports/esp32/main.c | 5 ++--- ports/esp32/mpthreadport.c | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 81e4a64346180..6f9ab82d0512a 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -65,7 +65,6 @@ // MicroPython runs as a task under FreeRTOS #define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) #define MP_TASK_STACK_SIZE (16 * 1024) -#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE / sizeof(StackType_t)) int vprintf_null(const char *format, va_list ap) { // do nothing: this is used as a log target during raw repl mode @@ -75,7 +74,7 @@ int vprintf_null(const char *format, va_list ap) { void mp_task(void *pvParameter) { volatile uint32_t sp = (uint32_t)get_sp(); #if MICROPY_PY_THREAD - mp_thread_init(pxTaskGetStackStart(NULL), MP_TASK_STACK_LEN); + mp_thread_init(pxTaskGetStackStart(NULL), MP_TASK_STACK_SIZE / sizeof(uintptr_t)); #endif uart_init(); @@ -169,7 +168,7 @@ void app_main(void) { nvs_flash_erase(); nvs_flash_init(); } - xTaskCreatePinnedToCore(mp_task, "mp_task", MP_TASK_STACK_LEN, NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); + xTaskCreatePinnedToCore(mp_task, "mp_task", MP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); } void nlr_jump_fail(void *val) { diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index f39c99b68d41a..d294c92727bf9 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -83,7 +83,7 @@ void mp_thread_gc_others(void) { if (!th->ready) { continue; } - gc_collect_root(th->stack, th->stack_len); // probably not needed + gc_collect_root(th->stack, th->stack_len); } mp_thread_mutex_unlock(&thread_mutex); } @@ -140,17 +140,17 @@ void mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("can't create thread")); } - // adjust the stack_size to provide room to recover from hitting the limit - *stack_size -= 1024; - // add thread to linked list of all threads th->ready = 0; th->arg = arg; th->stack = pxTaskGetStackStart(th->id); - th->stack_len = *stack_size / sizeof(StackType_t); + th->stack_len = *stack_size / sizeof(uintptr_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); } From eeca2c3cbe84975ffabd7fda932b2f5674e8b0fc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 3 Jun 2020 18:44:27 -0500 Subject: [PATCH 045/352] github: Add GitHub action to build docs. This builds docs, but only on pull requests that change a file in the docs/ directory. --- .github/workflows/docs.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000000..ec6b4c7f1962a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,18 @@ +name: Build docs + +on: + pull_request: + paths: + - docs/** + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + - name: Install Python packages + run: pip install Sphinx + - name: Build docs + run: make -C docs/ html From 1e6d18c915ccea0b6a19ffec9710d33dd7e5f866 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 3 Jun 2020 20:38:45 -0500 Subject: [PATCH 046/352] docs: Fix Sphinx 3.x warnings, and enable warnings-as-errors on build. This enables warnings as errors and fixes all current errors, namely: - reference to terms in the glossary must now be explicit (:term:) - method overloads must not be declared as a separate method or must use :noindex: - 2 cases where `` should have been used instead of ` --- docs/Makefile | 2 +- docs/library/esp32.rst | 4 ++-- docs/library/index.rst | 4 ++-- docs/library/micropython.rst | 2 +- docs/library/network.WLAN.rst | 4 ++-- docs/library/network.rst | 2 +- docs/library/pyb.Flash.rst | 5 +++-- docs/library/uerrno.rst | 4 ++-- docs/library/uio.rst | 2 ++ docs/library/uos.rst | 4 ++-- docs/library/ure.rst | 10 +++++----- docs/library/usocket.rst | 20 ++++++++++---------- docs/library/ussl.rst | 2 +- docs/make.bat | 1 + docs/reference/packages.rst | 34 +++++++++++++++++----------------- docs/reference/pyboard.py.rst | 2 +- docs/templates/replace.inc | 2 +- 17 files changed, 54 insertions(+), 50 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index e9c128e900788..05709617c35b9 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,7 +3,7 @@ # You can set these variables from the command line. PYTHON = python3 -SPHINXOPTS = +SPHINXOPTS = -W --keep-going SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build/$(MICROPY_PORT) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index f3be3692e3267..33b779a214336 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -84,9 +84,9 @@ methods to enable over-the-air (OTA) updates. Returns a 6-tuple ``(type, subtype, addr, size, label, encrypted)``. .. method:: Partition.readblocks(block_num, buf) -.. method:: Partition.readblocks(block_num, buf, offset) + Partition.readblocks(block_num, buf, offset) .. method:: Partition.writeblocks(block_num, buf) -.. method:: Partition.writeblocks(block_num, buf, offset) + Partition.writeblocks(block_num, buf, offset) .. method:: Partition.ioctl(cmd, arg) These methods implement the simple and :ref:`extended diff --git a/docs/library/index.rst b/docs/library/index.rst index 7b1636ce61b33..952e67d43ccec 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -23,7 +23,7 @@ into MicroPython. There are a few categories of such modules: * Modules which implement a subset of Python functionality, with a provision for extension by the user (via Python code). * Modules which implement MicroPython extensions to the Python standard libraries. -* Modules specific to a particular `MicroPython port` and thus not portable. +* Modules specific to a particular :term:`MicroPython port` and thus not portable. Note about the availability of the modules and their contents: This documentation in general aspires to describe all modules and functions/classes which are @@ -38,7 +38,7 @@ in a module (or even the entire module) described in this documentation **may be unavailable** in a particular build of MicroPython on a particular system. The best place to find general information of the availability/non-availability of a particular feature is the "General Information" section which contains -information pertaining to a specific `MicroPython port`. +information pertaining to a specific :term:`MicroPython port`. On some ports you are able to discover the available, built-in libraries that can be imported by entering the following at the REPL:: diff --git a/docs/library/micropython.rst b/docs/library/micropython.rst index ded52deb0d268..7106a1a2ff16d 100644 --- a/docs/library/micropython.rst +++ b/docs/library/micropython.rst @@ -100,7 +100,7 @@ Functions unlocked. Note: `heap_locked()` is not enabled on most ports by default, - requires `MICROPY_PY_MICROPYTHON_HEAP_LOCKED`. + requires ``MICROPY_PY_MICROPYTHON_HEAP_LOCKED``. .. function:: kbd_intr(chr) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 46a27a8fe462d..885a4460c56d4 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -101,7 +101,7 @@ Methods nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8')) .. method:: WLAN.config('param') -.. method:: WLAN.config(param=value, ...) + WLAN.config(param=value, ...) Get or set general network interface parameters. These methods allow to work with additional parameters beyond standard IP configuration (as dealt with by @@ -117,7 +117,7 @@ Methods print(ap.config('channel')) Following are commonly supported parameters (availability of a specific parameter - depends on network technology type, driver, and `MicroPython port`). + depends on network technology type, driver, and :term:`MicroPython port`). ============= =========== Parameter Description diff --git a/docs/library/network.rst b/docs/library/network.rst index 01e6f61369a40..208c58f852e6b 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -39,7 +39,7 @@ Common network adapter interface ================================ This section describes an (implied) abstract base class for all network -interface classes implemented by `MicroPython ports ` +interface classes implemented by :term:`MicroPython ports ` for different hardware. This means that MicroPython does not actually provide ``AbstractNIC`` class, but any actual NIC class, as described in the following sections, implements methods as described here. diff --git a/docs/library/pyb.Flash.rst b/docs/library/pyb.Flash.rst index 2d2f83da1c6ad..13f2097871913 100644 --- a/docs/library/pyb.Flash.rst +++ b/docs/library/pyb.Flash.rst @@ -26,6 +26,7 @@ Constructors This constructor is deprecated and will be removed in a future version of MicroPython. .. class:: pyb.Flash(\*, start=-1, len=-1) + :noindex: Create and return a block device that accesses the flash at the specified offset. The length defaults to the remaining size of the device. @@ -35,9 +36,9 @@ Methods ------- .. method:: Flash.readblocks(block_num, buf) -.. method:: Flash.readblocks(block_num, buf, offset) + Flash.readblocks(block_num, buf, offset) .. method:: Flash.writeblocks(block_num, buf) -.. method:: Flash.writeblocks(block_num, buf, offset) + Flash.writeblocks(block_num, buf, offset) .. method:: Flash.ioctl(cmd, arg) These methods implement the simple and :ref:`extended diff --git a/docs/library/uerrno.rst b/docs/library/uerrno.rst index e336eb5c5c85c..def01362f1387 100644 --- a/docs/library/uerrno.rst +++ b/docs/library/uerrno.rst @@ -7,7 +7,7 @@ |see_cpython_module| :mod:`python:errno`. This module provides access to symbolic error codes for `OSError` exception. -A particular inventory of codes depends on `MicroPython port`. +A particular inventory of codes depends on :term:`MicroPython port`. Constants --------- @@ -16,7 +16,7 @@ Constants Error codes, based on ANSI C/POSIX standard. All error codes start with "E". As mentioned above, inventory of the codes depends on - `MicroPython port`. Errors are usually accessible as ``exc.args[0]`` + :term:`MicroPython port`. Errors are usually accessible as ``exc.args[0]`` where ``exc`` is an instance of `OSError`. Usage example:: try: diff --git a/docs/library/uio.rst b/docs/library/uio.rst index 1a64b36582e52..dddb83a170750 100644 --- a/docs/library/uio.rst +++ b/docs/library/uio.rst @@ -114,7 +114,9 @@ Classes Get the current contents of the underlying buffer which holds data. .. class:: StringIO(alloc_size) + :noindex: .. class:: BytesIO(alloc_size) + :noindex: Create an empty `StringIO`/`BytesIO` object, preallocated to hold up to *alloc_size* number of bytes. That means that writing that amount diff --git a/docs/library/uos.rst b/docs/library/uos.rst index c49b13a5f5bca..ad10d9129713d 100644 --- a/docs/library/uos.rst +++ b/docs/library/uos.rst @@ -252,7 +252,7 @@ that the block device supports the extended interface. dependent on the specific block device. .. method:: readblocks(block_num, buf) - .. method:: readblocks(block_num, buf, offset) + readblocks(block_num, buf, offset) The first form reads aligned, multiples of blocks. Starting at the block given by the index *block_num*, read blocks from @@ -267,7 +267,7 @@ that the block device supports the extended interface. The number of bytes to read is given by the length of *buf*. .. method:: writeblocks(block_num, buf) - .. method:: writeblocks(block_num, buf, offset) + writeblocks(block_num, buf, offset) The first form writes aligned, multiples of blocks, and requires that the blocks that are written to be first erased (if necessary) by this method. diff --git a/docs/library/ure.rst b/docs/library/ure.rst index ca5f35b70302c..e94d286175cef 100644 --- a/docs/library/ure.rst +++ b/docs/library/ure.rst @@ -138,12 +138,12 @@ Functions If *count* is specified and non-zero then substitution will stop after this many substitutions are made. The *flags* argument is ignored. - Note: availability of this function depends on `MicroPython port`. + Note: availability of this function depends on :term:`MicroPython port`. .. data:: DEBUG Flag value, display debug information about compiled expression. - (Availability depends on `MicroPython port`.) + (Availability depends on :term:`MicroPython port`.) .. _regex: @@ -184,7 +184,7 @@ to the replacement function in `sub()`. Return a tuple containing all the substrings of the groups of the match. - Note: availability of this method depends on `MicroPython port`. + Note: availability of this method depends on :term:`MicroPython port`. .. method:: match.start([index]) match.end([index]) @@ -193,10 +193,10 @@ to the replacement function in `sub()`. substring group that was matched. *index* defaults to the entire group, otherwise it will select a group. - Note: availability of these methods depends on `MicroPython port`. + Note: availability of these methods depends on :term:`MicroPython port`. .. method:: match.span([index]) Returns the 2-tuple ``(match.start(index), match.end(index))``. - Note: availability of this method depends on `MicroPython port`. + Note: availability of this method depends on :term:`MicroPython port`. diff --git a/docs/library/usocket.rst b/docs/library/usocket.rst index e3b9d279a051e..bc4b4b6d5a952 100644 --- a/docs/library/usocket.rst +++ b/docs/library/usocket.rst @@ -37,8 +37,8 @@ power) and portable way to work with addresses. However, ``socket`` module (note the difference with native MicroPython ``usocket`` module described here) provides CPython-compatible way to specify addresses using tuples, as described below. Note that depending on a -`MicroPython port`, ``socket`` module can be builtin or need to be -installed from `micropython-lib` (as in the case of `MicroPython Unix port`), +:term:`MicroPython port`, ``socket`` module can be builtin or need to be +installed from `micropython-lib` (as in the case of :term:`MicroPython Unix port`), and some ports still accept only numeric addresses in the tuple format, and require to use `getaddrinfo` function to resolve domain names. @@ -61,7 +61,7 @@ Tuple address format for ``socket`` module: must be 0. *scopeid* is the interface scope identifier for link-local addresses. Note the domain names are not accepted as *ipv6_address*, they should be resolved first using `usocket.getaddrinfo()`. Availability - of IPv6 support depends on a `MicroPython port`. + of IPv6 support depends on a :term:`MicroPython port`. Functions --------- @@ -81,7 +81,7 @@ Functions .. function:: getaddrinfo(host, port, af=0, type=0, proto=0, flags=0, /) - Translate the host/port argument into a sequence of 5-tuples that contain all the + Translate the host/port argument into a sequence of 5-tuples that contain all the necessary arguments for creating a socket connected to that service. Arguments *af*, *type*, and *proto* (which have the same meaning as for the `socket()` function) can be used to filter which kind of addresses are returned. If a parameter is not @@ -141,7 +141,7 @@ Constants .. data:: AF_INET AF_INET6 - Address family types. Availability depends on a particular `MicroPython port`. + Address family types. Availability depends on a particular :term:`MicroPython port`. .. data:: SOCK_STREAM SOCK_DGRAM @@ -151,7 +151,7 @@ Constants .. data:: IPPROTO_UDP IPPROTO_TCP - IP protocol numbers. Availability depends on a particular `MicroPython port`. + IP protocol numbers. Availability depends on a particular :term:`MicroPython port`. Note that you don't need to specify these in a call to `usocket.socket()`, because `SOCK_STREAM` socket type automatically selects `IPPROTO_TCP`, and `SOCK_DGRAM` - `IPPROTO_UDP`. Thus, the only real use of these constants @@ -160,12 +160,12 @@ Constants .. data:: usocket.SOL_* Socket option levels (an argument to `setsockopt()`). The exact - inventory depends on a `MicroPython port`. + inventory depends on a :term:`MicroPython port`. .. data:: usocket.SO_* Socket options (an argument to `setsockopt()`). The exact - inventory depends on a `MicroPython port`. + inventory depends on a :term:`MicroPython port`. Constants specific to WiPy: @@ -185,7 +185,7 @@ Methods on the socket object will fail. The remote end will receive EOF indication if supported by protocol. - Sockets are automatically closed when they are garbage-collected, but it is recommended + Sockets are automatically closed when they are garbage-collected, but it is recommended to `close()` them explicitly as soon you finished working with them. .. method:: socket.bind(address) @@ -259,7 +259,7 @@ Methods completed. If zero is given, the socket is put in non-blocking mode. If None is given, the socket is put in blocking mode. - Not every `MicroPython port` supports this method. A more portable and + Not every :term:`MicroPython port` supports this method. A more portable and generic solution is to use `uselect.poll` object. This allows to wait on multiple objects at the same time (and not just on sockets, but on generic `stream` objects which support polling). Example:: diff --git a/docs/library/ussl.rst b/docs/library/ussl.rst index be84dc0545003..ffe146331ca08 100644 --- a/docs/library/ussl.rst +++ b/docs/library/ussl.rst @@ -24,7 +24,7 @@ Functions :meth:`~usocket.socket.accept()` on a non-SSL listening server socket. Depending on the underlying module implementation in a particular - `MicroPython port`, some or all keyword arguments above may be not supported. + :term:`MicroPython port`, some or all keyword arguments above may be not supported. .. warning:: diff --git a/docs/make.bat b/docs/make.bat index 44f9682790ef5..c09487fb741ef 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -6,6 +6,7 @@ if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build +set SPHINXOPTS=-W --keep-going set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 43217493e52a4..2854df23a0240 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -14,8 +14,8 @@ packages: 1. Python modules and packages are turned into distribution package archives, and published at the Python Package Index (PyPI). -2. `upip` package manager can be used to install a distribution package - on a `MicroPython port` with networking capabilities (for example, +2. :term:`upip` package manager can be used to install a distribution package + on a :term:`MicroPython port` with networking capabilities (for example, on the Unix port). 3. For ports without networking capabilities, an "installation image" can be prepared on the Unix port, and transferred to a device by @@ -51,14 +51,14 @@ even by the smallest devices. Besides the small compression dictionary size, MicroPython distribution packages also have other optimizations, like removing any files from the archive which aren't used by the installation process. In particular, -`upip` package manager doesn't execute ``setup.py`` during installation +:term:`upip` package manager doesn't execute ``setup.py`` during installation (see below), and thus that file is not included in the archive. At the same time, these optimizations make MicroPython distribution -packages not compatible with `CPython`'s package manager, ``pip``. +packages not compatible with :term:`CPython`'s package manager, ``pip``. This isn't considered a big problem, because: -1. Packages can be installed with `upip`, and then can be used with +1. Packages can be installed with :term:`upip`, and then can be used with CPython (if they are compatible with it). 2. In the other direction, majority of CPython packages would be incompatible with MicroPython by various reasons, first of all, @@ -73,12 +73,12 @@ resource constrained devices. ------------------------ MicroPython distribution packages are intended to be installed using -the `upip` package manager. `upip` is a Python application which is +the :term:`upip` package manager. :term:`upip` is a Python application which is usually distributed (as frozen bytecode) with network-enabled -`MicroPython ports `. At the very least, -`upip` is available in the `MicroPython Unix port`. +:term:`MicroPython ports `. At the very least, +:term:`upip` is available in the :term:`MicroPython Unix port`. -On any `MicroPython port` providing `upip`, it can be accessed as +On any :term:`MicroPython port` providing :term:`upip`, it can be accessed as following:: import upip @@ -123,12 +123,12 @@ commands which corresponds to the example above are:: Cross-installing packages ------------------------- -For `MicroPython ports ` without native networking +For :term:`MicroPython ports ` without native networking capabilities, the recommend process is "cross-installing" them into a -"directory image" using the `MicroPython Unix port`, and then +"directory image" using the :term:`MicroPython Unix port`, and then transferring this image to a device by suitable means. -Installing to a directory image involves using ``-p`` switch to `upip`:: +Installing to a directory image involves using ``-p`` switch to :term:`upip`:: micropython -m upip install -p install_dir micropython-pystone_lowmem @@ -137,13 +137,13 @@ packages) will be available in the ``install_dir/`` subdirectory. You would need to transfer contents of this directory (without the ``install_dir/`` prefix) to the device, at the suitable location, where it can be found by the Python ``import`` statement (see discussion of -the `upip` installation path above). +the :term:`upip` installation path above). Cross-installing packages with freezing --------------------------------------- -For the low-memory `MicroPython ports `, the process +For the low-memory :term:`MicroPython ports `, the process described in the previous section does not provide the most efficient resource usage,because the packages are installed in the source form, so need to be compiled to the bytecome on each import. This compilation @@ -160,7 +160,7 @@ mentioned above: * Filesystem is not required for frozen packages. Using frozen bytecode requires building the executable (firmware) -for a given `MicroPython port` from the C source code. Consequently, +for a given :term:`MicroPython port` from the C source code. Consequently, the process is: 1. Follow the instructions for a particular port on setting up a @@ -168,7 +168,7 @@ the process is: study instructions in ``ports/esp8266/README.md`` and follow them. Make sure you can build the port and deploy the resulting executable/firmware successfully before proceeding to the next steps. -2. Build `MicroPython Unix port` and make sure it is in your PATH and +2. Build :term:`MicroPython Unix port` and make sure it is in your PATH and you can execute ``micropython``. 3. Change to port's directory (e.g. ``ports/esp8266/`` for ESP8266). 4. Run ``make clean-frozen``. This step cleans up any previous @@ -281,7 +281,7 @@ following calls:: pkg_resources.resource_stream(__name__, "data/page.html") pkg_resources.resource_stream(__name__, "data/image.png") -You can develop and debug using the `MicroPython Unix port` as usual. +You can develop and debug using the :term:`MicroPython Unix port` as usual. When time comes to make a distribution package out of it, just use overridden "sdist" command from sdist_upip.py module as described in the previous section. diff --git a/docs/reference/pyboard.py.rst b/docs/reference/pyboard.py.rst index d404c738f1d26..30230eebc3bf4 100644 --- a/docs/reference/pyboard.py.rst +++ b/docs/reference/pyboard.py.rst @@ -68,7 +68,7 @@ example:: $ pyboard.py -c 'print(1+1)' Similarly, the ``PYBOARD_BAUDRATE`` environment variable can be used -to set the default for the `--baudrate` option. +to set the default for the ``--baudrate`` option. Running a script on the device ------------------------------ diff --git a/docs/templates/replace.inc b/docs/templates/replace.inc index 319c53735fc28..14f1875eeec6a 100644 --- a/docs/templates/replace.inc +++ b/docs/templates/replace.inc @@ -4,6 +4,6 @@ .. |see_cpython_module| replace:: - *This module implements a subset of the corresponding* `CPython` *module, + *This module implements a subset of the corresponding* :term:`CPython` *module, as described below. For more information, refer to the original CPython documentation:* From 6ac05af8e1408a1f5cd27d57c8581a9f4197a2f5 Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Sun, 7 Jun 2020 18:23:36 +0200 Subject: [PATCH 047/352] mimxrt/tusb_config.h: Preliminary fix for TinyUSB HS endpoint overflow. Sending more than 64 bytes to the USB CDC endpoint in HS mode will lead to a hard crash. This commit fixes the issue, although there may be a better fix from upstream TinyUSB in the future. --- ports/mimxrt/tusb_config.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/tusb_config.h b/ports/mimxrt/tusb_config.h index f2367c2044384..c7ec05e632323 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/tusb_config.h @@ -30,7 +30,8 @@ #define CFG_TUSB_OS (OPT_OS_NONE) #define CFG_TUD_CDC (1) -#define CFG_TUD_CDC_RX_BUFSIZE (256) -#define CFG_TUD_CDC_TX_BUFSIZE (256) +#define CFG_TUD_CDC_RX_BUFSIZE (512) +#define CFG_TUD_CDC_TX_BUFSIZE (512) +#define CFG_TUD_CDC_EPSIZE (512) #endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H From bd06c698f05b7b53070263653e74eb078b5278a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 8 Jun 2020 14:31:35 +1000 Subject: [PATCH 048/352] py/dynruntime.h: Make mp_obj_str_get_str raise if arg not a str/bytes. --- py/dynruntime.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/py/dynruntime.h b/py/dynruntime.h index 9f2803c53520f..696746f7510ff 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -114,7 +114,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_obj_cast_to_native_base(o, t) (mp_obj_cast_to_native_base_dyn((o), (t))) #define mp_obj_get_int(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_INT)) #define mp_obj_get_int_truncated(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_UINT)) -#define mp_obj_str_get_str(s) ((void *)mp_fun_table.native_from_obj(s, MP_NATIVE_TYPE_PTR)) +#define mp_obj_str_get_str(s) (mp_obj_str_get_data_dyn((s), NULL)) #define mp_obj_str_get_data(o, len) (mp_obj_str_get_data_dyn((o), (len))) #define mp_get_buffer_raise(o, bufinfo, fl) (mp_fun_table.get_buffer_raise((o), (bufinfo), (fl))) #define mp_get_stream_raise(s, flags) (mp_fun_table.get_stream_raise((s), (flags))) @@ -149,7 +149,9 @@ static inline mp_obj_t mp_obj_cast_to_native_base_dyn(mp_obj_t self_in, mp_const static inline void *mp_obj_str_get_data_dyn(mp_obj_t o, size_t *l) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(o, &bufinfo, MP_BUFFER_READ); - *l = bufinfo.len; + if (l != NULL) { + *l = bufinfo.len; + } return bufinfo.buf; } From 51fd6c97773a7e0d76bb61e220b35b98f392e27e Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 3 Jun 2020 10:18:49 +0200 Subject: [PATCH 049/352] extmod/ure: Use single function for match/search/sub. Saves about 500 bytes on unix x64 and enables CPython-conform usage of passing a re object to these functions. --- examples/natmod/ure/ure.c | 4 +-- extmod/modure.c | 58 ++++++++++++++------------------------- tests/extmod/ure1.py | 11 ++++++++ tests/extmod/ure_sub.py | 9 ++++++ 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/examples/natmod/ure/ure.c b/examples/natmod/ure/ure.c index 00ca2e9eb4c71..175b93e395ef9 100644 --- a/examples/natmod/ure/ure.c +++ b/examples/natmod/ure/ure.c @@ -71,8 +71,8 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a re_type.locals_dict = (void*)&re_locals_dict; mp_store_global(MP_QSTR_compile, MP_OBJ_FROM_PTR(&mod_re_compile_obj)); - mp_store_global(MP_QSTR_match, MP_OBJ_FROM_PTR(&mod_re_match_obj)); - mp_store_global(MP_QSTR_search, MP_OBJ_FROM_PTR(&mod_re_search_obj)); + mp_store_global(MP_QSTR_match, MP_OBJ_FROM_PTR(&re_match_obj)); + mp_store_global(MP_QSTR_search, MP_OBJ_FROM_PTR(&re_search_obj)); MP_DYNRUNTIME_INIT_EXIT } diff --git a/extmod/modure.c b/extmod/modure.c index 1847ec288075d..328c897d8890b 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -53,6 +53,10 @@ typedef struct _mp_obj_match_t { const char *caps[0]; } mp_obj_match_t; +STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args); +#if !MICROPY_ENABLE_DYNRUNTIME +STATIC const mp_obj_type_t re_type; +#endif STATIC void match_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; @@ -175,7 +179,12 @@ STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { (void)n_args; - mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_re_t *self; + if (mp_obj_is_type(args[0], &re_type)) { + self = MP_OBJ_TO_PTR(args[0]); + } else { + self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); + } Subject subj; size_t len; subj.begin = mp_obj_str_get_data(args[1], &len); @@ -253,8 +262,13 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split); #if MICROPY_PY_URE_SUB -STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) { - mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { + mp_obj_re_t *self; + if (mp_obj_is_type(args[0], &re_type)) { + self = MP_OBJ_TO_PTR(args[0]); + } else { + self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); + } mp_obj_t replace = args[1]; mp_obj_t where = args[2]; mp_int_t count = 0; @@ -358,10 +372,7 @@ STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *a return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return); } -STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) { - return re_sub_helper(args[0], n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub_helper); #endif @@ -414,41 +425,14 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile); -STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { - (void)n_args; - mp_obj_t self = mod_re_compile(1, args); - - const mp_obj_t args2[] = {self, args[1]}; - mp_obj_t match = ure_exec(is_anchored, 2, args2); - return match; -} - -STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) { - return mod_re_exec(true, n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match); - -STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) { - return mod_re_exec(false, n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search); - -#if MICROPY_PY_URE_SUB -STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) { - mp_obj_t self = mod_re_compile(1, args); - return re_sub_helper(self, n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub); -#endif - #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) }, { MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) }, - { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) }, - { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) }, + { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) }, + { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) }, #if MICROPY_PY_URE_SUB - { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) }, + { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) }, #endif #if MICROPY_PY_URE_DEBUG { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) }, diff --git a/tests/extmod/ure1.py b/tests/extmod/ure1.py index 9e1be5fc796bc..2bdf2d0cfb4f3 100644 --- a/tests/extmod/ure1.py +++ b/tests/extmod/ure1.py @@ -125,3 +125,14 @@ print(re.compile(r"[a\-x]").split("foo-bar")) print(re.compile(r"[\-ax]").split("foo-bar")) print("===") + +# Module functions take str/bytes/re. +for f in (re.match, re.search): + print(f(".", "foo").group(0)) + print(f(b".", b"foo").group(0)) + print(f(re.compile("."), "foo").group(0)) + try: + f(123, "a") + except TypeError: + print("TypeError") +print("===") diff --git a/tests/extmod/ure_sub.py b/tests/extmod/ure_sub.py index 6bb332077dd29..953e7bf62a478 100644 --- a/tests/extmod/ure_sub.py +++ b/tests/extmod/ure_sub.py @@ -60,3 +60,12 @@ def A(): re.sub("(a)", "b\\199999999999999999999999999999999999999", "a") except: print("invalid group") + +# Module function takes str/bytes/re. +print(re.sub("a", "a", "a")) +print(re.sub(b".", b"a", b"a")) +print(re.sub(re.compile("a"), "a", "a")) +try: + re.sub(123, "a", "a") +except TypeError: + print("TypeError") From 8e8dcdd34b39be58c4d9b64ee23ad40acb2a6b1d Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 1 Apr 2020 15:16:03 +1100 Subject: [PATCH 050/352] esp32: Update IDF v4.0 supported hash to v4.0.1. The main fix relevant to MicroPython is https://github.com/espressif/esp-idf/issues/4196 Release notes here https://github.com/espressif/esp-idf/releases/tag/v4.0.1 --- ports/esp32/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index bd89e8b0c5610..0f6a1969a6208 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -54,9 +54,9 @@ SDKCONFIG_COMBINED = $(BUILD)/sdkconfig.combined SDKCONFIG_H = $(BUILD)/sdkconfig.h # The git hash of the currently supported ESP IDF version. -# These correspond to v3.3.2 and v4.0. +# These correspond to v3.3.2 and v4.0.1. ESPIDF_SUPHASH_V3 := 9e70825d1e1cbf7988cf36981774300066580ea7 -ESPIDF_SUPHASH_V4 := 463a9d8b7f9af8205222b80707f9bdbba7c530e1 +ESPIDF_SUPHASH_V4 := 4c81978a3e2220674a432a588292a4c860eef27b define print_supported_git_hash $(info Supported git hash (v3.3): $(ESPIDF_SUPHASH_V3)) @@ -515,7 +515,7 @@ ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o,\ ESPIDF_MDNS_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/mdns/*.c)) ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DESP_SUPPLICANT -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DCONFIG_ECC -D__ets__ -Wno-strict-aliasing -I$(ESPCOMP)/wpa_supplicant/src -Wno-implicit-function-declaration +$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DCONFIG_WPA3_SAE -DCONFIG_IEEE80211W -DESP_SUPPLICANT -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DCONFIG_ECC -D__ets__ -Wno-strict-aliasing -I$(ESPCOMP)/wpa_supplicant/src -Wno-implicit-function-declaration else $(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DALLOW_EVEN_MOD -D__ets__ -Wno-strict-aliasing endif @@ -555,6 +555,7 @@ ESPIDF_BT_NIMBLE_O = $(patsubst %.c,%.o,\ $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/src/*.c) \ $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/porting/nimble/src/*.c) \ $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/porting/npl/freertos/src/*.c) \ + $(wildcard $(ESPCOMP)/bt/host/nimble/port/src/*.c) \ ) endif From e0d539f79d4ec020636ba7b043fac08030234112 Mon Sep 17 00:00:00 2001 From: Albort Xue Date: Sun, 7 Jun 2020 11:20:19 +0800 Subject: [PATCH 051/352] mimxrt/boards: Enable LED class for MIMXRT1060_EVK board. --- .../boards/MIMXRT1060_EVK/mpconfigboard.h | 7 ++-- ports/mimxrt/boards/MIMXRT1060_EVK/pins.c | 33 +++++++++++++++++++ ports/mimxrt/boards/MIMXRT1060_EVK/pins.h | 30 +++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/pins.c create mode 100644 ports/mimxrt/boards/MIMXRT1060_EVK/pins.h diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index 714f987a54c9d..8bd2a579174aa 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -3,6 +3,7 @@ #define BOARD_FLASH_SIZE (8 * 1024 * 1024) -#define MICROPY_HW_LED_PINMUX IOMUXC_GPIO_AD_B0_09_GPIO1_IO09 -#define MICROPY_HW_LED_PORT GPIO1 -#define MICROPY_HW_LED_PIN 9 +// MIMXRT1060_EVK has 1 user LED +#define MICROPY_HW_LED1_PIN (GPIO_AD_B0_09) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/pins.c b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.c new file mode 100644 index 0000000000000..d5da9c6f99275 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 NXP + * + * 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 "pin.h" + +static pin_af_obj_t GPIO_AD_B0_09_af[] = { + PIN_AF(GPIO1_IO09, PIN_AF_MODE_ALT5, GPIO1, 0x10B0U), +}; + +pin_obj_t GPIO_AD_B0_09 = PIN(GPIO_AD_B0_09, GPIO1, 9, GPIO_AD_B0_09_af); diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/pins.h b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.h new file mode 100644 index 0000000000000..baef51c6c8612 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 NXP + * + * 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. + */ + +// NOTE: pins.h shall only be included in in pin.h +// hence no include guards are needed since they will be provided by pin.h + +extern pin_obj_t GPIO_AD_B0_09; From 29e258611ace8e4de51d810d5ff14364a5fbb950 Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Tue, 14 Apr 2020 21:08:59 +0200 Subject: [PATCH 052/352] mimxrt/boards: Integrate support for MIMXRT1020_EVK board. --- .../evkmimxrt1020_flexspi_nor_config.h | 270 ++++++++++++++++++ .../boards/MIMXRT1020_EVK/flash_config.c | 51 ++++ .../boards/MIMXRT1020_EVK/mpconfigboard.h | 9 + .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 7 + ports/mimxrt/boards/MIMXRT1020_EVK/pins.c | 33 +++ ports/mimxrt/boards/MIMXRT1020_EVK/pins.h | 30 ++ ports/mimxrt/boards/MIMXRT1021.ld | 8 + 7 files changed, 408 insertions(+) create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/evkmimxrt1020_flexspi_nor_config.h create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/flash_config.c create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/pins.c create mode 100644 ports/mimxrt/boards/MIMXRT1020_EVK/pins.h create mode 100644 ports/mimxrt/boards/MIMXRT1021.ld diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/evkmimxrt1020_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1020_EVK/evkmimxrt1020_flexspi_nor_config.h new file mode 100644 index 0000000000000..c0c6ea243672c --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/evkmimxrt1020_flexspi_nor_config.h @@ -0,0 +1,270 @@ +/* + * Copyright 2019 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// Based on tinyusb/hw/bsp/teensy_40/evkmimxrt1020_flexspi_nor_config.h + +#ifndef __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.0. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_133MHz = 7, + kFlexSpiSerialClk_166MHz = 8, + kFlexSpiSerialClk_200MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ */ diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/flash_config.c b/ports/mimxrt/boards/MIMXRT1020_EVK/flash_config.c new file mode 100644 index 0000000000000..56007931a90d7 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/flash_config.c @@ -0,0 +1,51 @@ +/* + * Copyright 2019 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// Based on tinyusb/hw/bsp/teensy_40/evkmimxrt1010_flexspi_nor_config.c + +#include "evkmimxrt1020_flexspi_nor_config.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_100MHz, + .sflashA1Size = 8u * 1024u * 1024u, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h new file mode 100644 index 0000000000000..8598f76bf1833 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -0,0 +1,9 @@ +#define MICROPY_HW_BOARD_NAME "i.MX RT1020 EVK" +#define MICROPY_HW_MCU_NAME "MIMXRT1021DAG5A" + +#define BOARD_FLASH_SIZE (8 * 1024 * 1024) + +// i.MX RT1020 EVK has 1 board LED +#define MICROPY_HW_LED1_PIN (GPIO_AD_B0_05) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk new file mode 100644 index 0000000000000..f3b1689524e29 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -0,0 +1,7 @@ +MCU_SERIES = MIMXRT1021 +MCU_VARIANT = MIMXRT1021DAG5A + +JLINK_PATH ?= /media/RT1020-EVK/ + +deploy: $(BUILD)/firmware.bin + cp $< $(JLINK_PATH) diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/pins.c b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.c new file mode 100644 index 0000000000000..946b6efca8b27 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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 "pin.h" + +static pin_af_obj_t GPIO_AD_B0_05_af[] = { + PIN_AF(GPIO1_IO05, PIN_AF_MODE_ALT5, GPIO1, 0x10B0U), +}; + +pin_obj_t GPIO_AD_B0_05 = PIN(GPIO_AD_B0_05, GPIO1, 5, GPIO_AD_B0_05_af); diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/pins.h b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.h new file mode 100644 index 0000000000000..158929911c7a5 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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. + */ + +// NOTE: pins.h shall only be included in in pin.h +// hence no include guards are needed since they will be provided by pin.h + +extern pin_obj_t GPIO_AD_B0_05; diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld new file mode 100644 index 0000000000000..6b59649fcaf7e --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -0,0 +1,8 @@ +/* 24kiB stack. */ +__stack_size__ = 0x6000; +_estack = __StackTop; +_sstack = __StackLimit; + +/* Use second OCRAM bank for GC heap. */ +_gc_heap_start = ORIGIN(m_data2); +_gc_heap_end = ORIGIN(m_data2) + LENGTH(m_data2); From 28370c04509a6255cd3d6b9424443a5b57bb7467 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 10 Jun 2020 10:45:24 +1000 Subject: [PATCH 053/352] py/objtype: Add __dict__ attribute for class objects. The behavior mirrors the instance object dict attribute where a copy of the local attributes are provided (unless the dict is read-only, then that dict itself is returned, as an optimisation). MicroPython does not support modifying this dict because the changes will not be reflected in the class. The feature is only enabled if MICROPY_CPYTHON_COMPAT is set, the same as the instance version. --- py/obj.h | 1 + py/objdict.c | 4 ++-- py/objtype.c | 15 +++++++++++++++ tests/basics/class_dict.py | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/basics/class_dict.py diff --git a/py/obj.h b/py/obj.h index 125acf118f08d..468125eb75699 100644 --- a/py/obj.h +++ b/py/obj.h @@ -895,6 +895,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in); mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index); mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key); +mp_obj_t mp_obj_dict_copy(mp_obj_t self_in); static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) { return &((mp_obj_dict_t *)MP_OBJ_TO_PTR(dict))->map; } diff --git a/py/objdict.c b/py/objdict.c index 7690eeab29af1..69eda99738eec 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -227,7 +227,7 @@ STATIC mp_obj_t dict_clear(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); -STATIC mp_obj_t dict_copy(mp_obj_t self_in) { +mp_obj_t mp_obj_dict_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_dict_type(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); @@ -240,7 +240,7 @@ STATIC mp_obj_t dict_copy(mp_obj_t self_in) { memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); return other_out; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, mp_obj_dict_copy); #if MICROPY_PY_BUILTINS_DICT_FROMKEYS // this is a classmethod diff --git a/py/objtype.c b/py/objtype.c index d08c69e284b3e..cb0fb267c8165 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1013,6 +1013,21 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_NEW_QSTR(self->name); return; } + #if MICROPY_CPYTHON_COMPAT + if (attr == MP_QSTR___dict__) { + // Returns a read-only dict of the class attributes. + // If the internal locals is not fixed, a copy will be created. + mp_obj_dict_t *dict = self->locals_dict; + if (dict->map.is_fixed) { + dest[0] = MP_OBJ_FROM_PTR(dict); + } else { + dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(dict)); + dict = MP_OBJ_TO_PTR(dest[0]); + dict->map.is_fixed = 1; + } + return; + } + #endif if (attr == MP_QSTR___bases__) { if (self == &mp_type_object) { dest[0] = mp_const_empty_tuple; diff --git a/tests/basics/class_dict.py b/tests/basics/class_dict.py new file mode 100644 index 0000000000000..f80ded678b0f8 --- /dev/null +++ b/tests/basics/class_dict.py @@ -0,0 +1,19 @@ +# test __dict__ attribute of a class + +if not hasattr(int, "__dict__"): + print("SKIP") + raise SystemExit + + +# dict of a built-in type +print("from_bytes" in int.__dict__) + + +# dict of a user class +class Foo: + a = 1 + b = "bar" + + +d = Foo.__dict__ +print(d["a"], d["b"]) From 95cbe6b65ec84793f0f5ac2fd89c1375969a3bea Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 10 Jun 2020 10:46:38 +1000 Subject: [PATCH 054/352] py/objtype: Use mp_obj_dict_copy() for creating obj.__dict__ attribute. The resulting dict is now marked as read-only (is_fixed=1) to enforce the fact that changes to this dict will not be reflected in the class instance. This commit reduces code size by about 20 bytes, and should be more efficient because it creates a direct copy of the dict rather than reinserting all elements. --- py/objtype.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index cb0fb267c8165..a539fcab29414 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -588,16 +588,13 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des #if MICROPY_CPYTHON_COMPAT if (attr == MP_QSTR___dict__) { // Create a new dict with a copy of the instance's map items. - // This creates, unlike CPython, a 'read-only' __dict__: modifying - // it will not result in modifications to the actual instance members. - mp_map_t *map = &self->members; - mp_obj_t attr_dict = mp_obj_new_dict(map->used); - for (size_t i = 0; i < map->alloc; ++i) { - if (mp_map_slot_is_filled(map, i)) { - mp_obj_dict_store(attr_dict, map->table[i].key, map->table[i].value); - } - } - dest[0] = attr_dict; + // This creates, unlike CPython, a read-only __dict__ that can't be modified. + mp_obj_dict_t dict; + dict.base.type = &mp_type_dict; + dict.map = self->members; + dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(&dict)); + mp_obj_dict_t *dest_dict = MP_OBJ_TO_PTR(dest[0]); + dest_dict->map.is_fixed = 1; return; } #endif From f3062b5cbdeea5900486a38902e3155117ebf820 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 9 Jun 2020 14:46:57 +1000 Subject: [PATCH 055/352] py/obj.h: Clarify comments about mp_map_t is_fixed and is_ordered. Long ago, prior to 0ef01d0a75b8b2f48a72f0041e048a390b9e75b6, fixed and ordered maps were the same setting with the "table_is_fixed_array" member of mp_map_t. But these settings are actually independent, and it is possible to have is_fixed=1, is_ordered=0 (although this can currently only be done by tools/cc1). So update the comments to reflect this. --- py/obj.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/obj.h b/py/obj.h index 468125eb75699..f80f00031dc77 100644 --- a/py/obj.h +++ b/py/obj.h @@ -407,8 +407,8 @@ typedef struct _mp_rom_map_elem_t { typedef struct _mp_map_t { size_t all_keys_are_qstrs : 1; - size_t is_fixed : 1; // a fixed array that can't be modified; must also be ordered - size_t is_ordered : 1; // an ordered array + size_t is_fixed : 1; // if set, table is fixed/read-only and can't be modified + size_t is_ordered : 1; // if set, table is an ordered array, not a hash map size_t used : (8 * sizeof(size_t) - 3); size_t alloc; mp_map_elem_t *table; From a4c96fb3b0c5e3bf83238a0edd0fbcbfd96208c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 5 Jun 2020 21:26:27 +1000 Subject: [PATCH 056/352] extmod/uasyncio: Add asyncio.wait_for_ms function. Fixes issue #6107. --- docs/library/uasyncio.rst | 6 ++++ extmod/uasyncio/__init__.py | 1 + extmod/uasyncio/funcs.py | 12 +++++--- tests/extmod/uasyncio_micropython.py | 37 ++++++++++++++++++++++++ tests/extmod/uasyncio_micropython.py.exp | 7 +++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/extmod/uasyncio_micropython.py create mode 100644 tests/extmod/uasyncio_micropython.py.exp diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index 0f363a076ea3b..31b38a4e05f2f 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -74,6 +74,12 @@ Additional functions This is a coroutine. +.. function:: wait_for_ms(awaitable, timeout) + + Similar to `wait_for` but *timeout* is an integer in milliseconds. + + This is a coroutine, and a MicroPython extension. + .. function:: gather(\*awaitables, return_exceptions=False) Run all *awaitables* concurrently. Any *awaitables* that are not tasks are diff --git a/extmod/uasyncio/__init__.py b/extmod/uasyncio/__init__.py index da8b58061e6ca..08f924cf29b9b 100644 --- a/extmod/uasyncio/__init__.py +++ b/extmod/uasyncio/__init__.py @@ -7,6 +7,7 @@ _attrs = { "wait_for": "funcs", + "wait_for_ms": "funcs", "gather": "funcs", "Event": "event", "Lock": "lock", diff --git a/extmod/uasyncio/funcs.py b/extmod/uasyncio/funcs.py index 7a4bddf25695a..6e1305c94f418 100644 --- a/extmod/uasyncio/funcs.py +++ b/extmod/uasyncio/funcs.py @@ -4,16 +4,16 @@ from . import core -async def wait_for(aw, timeout): +async def wait_for(aw, timeout, sleep=core.sleep): aw = core._promote_to_task(aw) if timeout is None: return await aw - def cancel(aw, timeout): - await core.sleep(timeout) + def cancel(aw, timeout, sleep): + await sleep(timeout) aw.cancel() - cancel_task = core.create_task(cancel(aw, timeout)) + cancel_task = core.create_task(cancel(aw, timeout, sleep)) try: ret = await aw except core.CancelledError: @@ -29,6 +29,10 @@ def cancel(aw, timeout): return ret +def wait_for_ms(aw, timeout): + return wait_for(aw, timeout, core.sleep_ms) + + async def gather(*aws, return_exceptions=False): ts = [core._promote_to_task(aw) for aw in aws] for i in range(len(ts)): diff --git a/tests/extmod/uasyncio_micropython.py b/tests/extmod/uasyncio_micropython.py new file mode 100644 index 0000000000000..69e5fa3224df7 --- /dev/null +++ b/tests/extmod/uasyncio_micropython.py @@ -0,0 +1,37 @@ +# Test MicroPython extensions on CPython asyncio: +# - sleep_ms +# - wait_for_ms + +try: + import utime, uasyncio +except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, t): + print("task start", id) + await uasyncio.sleep_ms(t) + print("task end", id) + return id * 2 + + +async def main(): + # Simple sleep_ms + t0 = utime.ticks_ms() + await uasyncio.sleep_ms(1) + print(utime.ticks_diff(utime.ticks_ms(), t0) < 100) + + # When task finished before the timeout + print(await uasyncio.wait_for_ms(task(1, 5), 50)) + + # When timeout passes and task is cancelled + try: + print(await uasyncio.wait_for_ms(task(2, 50), 5)) + except uasyncio.TimeoutError: + print("timeout") + + print("finish") + + +uasyncio.run(main()) diff --git a/tests/extmod/uasyncio_micropython.py.exp b/tests/extmod/uasyncio_micropython.py.exp new file mode 100644 index 0000000000000..f5be1dc75a254 --- /dev/null +++ b/tests/extmod/uasyncio_micropython.py.exp @@ -0,0 +1,7 @@ +True +task start 1 +task end 1 +2 +task start 2 +timeout +finish From 3705bc418cadd9d6ad87f2bcd5443ba82f730e15 Mon Sep 17 00:00:00 2001 From: jp-96 Date: Mon, 8 Jun 2020 12:19:56 +0900 Subject: [PATCH 057/352] extmod/modbluetooth: Register default GATT service and fix esp32 init. This is for the NimBLE bindings, to make sure the default GATT service appears and that the esp32 initialises NimBLE correctly (it now matches stm32). --- extmod/nimble/modbluetooth_nimble.c | 7 +++++-- ports/esp32/nimble.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 05ec6117791da..035ec3d26fc6d 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -39,6 +39,7 @@ #include "nimble/ble.h" #include "nimble/nimble_port.h" #include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" #ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE" @@ -271,8 +272,9 @@ int mp_bluetooth_init(void) { nimble_port_init(); mp_bluetooth_nimble_port_postinit(); - // By default, just register the default gap service. + // By default, just register the default gap/gatt service. ble_svc_gap_init(); + ble_svc_gatt_init(); mp_bluetooth_nimble_port_start(); @@ -466,8 +468,9 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { // Reset the gatt characteristic value db. mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db); - // By default, just register the default gap service. + // By default, just register the default gap/gatt service. ble_svc_gap_init(); + ble_svc_gatt_init(); if (!append) { // Unref any previous service definitions. diff --git a/ports/esp32/nimble.c b/ports/esp32/nimble.c index e60e08badfbe1..16829732c30e5 100644 --- a/ports/esp32/nimble.c +++ b/ports/esp32/nimble.c @@ -44,7 +44,6 @@ void mp_bluetooth_nimble_port_preinit(void) { } void mp_bluetooth_nimble_port_postinit(void) { - nimble_port_freertos_init(ble_host_task); } void mp_bluetooth_nimble_port_deinit(void) { @@ -52,6 +51,7 @@ void mp_bluetooth_nimble_port_deinit(void) { } void mp_bluetooth_nimble_port_start(void) { + nimble_port_freertos_init(ble_host_task); } #endif From c6fd6a0d728fea590e7f731e41a33754111770bd Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 5 Jun 2020 15:40:29 +1000 Subject: [PATCH 058/352] examples/bluetooth: Fix event code in ble_temperature_central.py. --- examples/bluetooth/ble_temperature_central.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 1ce99728b37d3..f9db225c16942 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -128,7 +128,7 @@ def _irq(self, event, data): if conn_handle == self._conn_handle and uuid == _ENV_SENSE_UUID: self._start_handle, self._end_handle = start_handle, end_handle - elif event == _IRQ_GATTC_SERVICES_DONE: + elif event == _IRQ_GATTC_SERVICE_DONE: # Service query complete. if self._start_handle and self._end_handle: self._ble.gattc_discover_characteristics( @@ -143,7 +143,7 @@ def _irq(self, event, data): if conn_handle == self._conn_handle and uuid == _TEMP_UUID: self._value_handle = value_handle - elif event == _IRQ_GATTC_CHARACTERISTICS_DONE: + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: # Characteristic query complete. if self._value_handle: # We've finished connecting and discovering device, fire the connect callback. From 3f77f2c60ca80ea9189f85193dbd746e9a6034a9 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 5 Jun 2020 15:41:08 +1000 Subject: [PATCH 059/352] unix/btstack_usb: Allow choosing adaptor via environment variable. This allows running (for example): env MICROPYBTUSB=2-2 ./micropython-dev ../../examples/bluetooth/ble_temperature_central.py --- ports/unix/btstack_usb.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ports/unix/btstack_usb.c b/ports/unix/btstack_usb.c index da9d72fe15258..76f32d6d27cfa 100644 --- a/ports/unix/btstack_usb.c +++ b/ports/unix/btstack_usb.c @@ -110,7 +110,23 @@ void mp_bluetooth_btstack_port_init(void) { btstack_run_loop_embedded_get_instance()->init(); } - // TODO: allow setting USB device path via cmdline/env var. + // MICROPYBTUSB can be a ':'' or '-' separated port list. + char *path = getenv("MICROPYBTUSB"); + if (path != NULL) { + uint8_t usb_path[7] = {0}; + size_t usb_path_len = 0; + + while (usb_path_len < MP_ARRAY_SIZE(usb_path)) { + char *delimiter; + usb_path[usb_path_len++] = strtol(path, &delimiter, 16); + if (!delimiter || (*delimiter != ':' && *delimiter != '-')) { + break; + } + path = delimiter + 1; + } + + hci_transport_usb_set_path(usb_path_len, usb_path); + } // hci_dump_open(NULL, HCI_DUMP_STDOUT); hci_init(hci_transport_usb_instance(), NULL); From 00c3e2156a7b5447966019ad46a1c9763f0890ee Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 5 Jun 2020 15:41:40 +1000 Subject: [PATCH 060/352] tests/run-multitests.py: Allow passing unique env vars to each instance. For example, to run the BLE multitests entirely with the unix port: env MICROPY_MICROPYTHON=../ports/unix/micropython-dev ./run-multitests.py \ -i micropython,MICROPYBTUSB=01 \ -i micropython,MICROPYBTUSB=02:02 \ multi_bluetooth/ble_*.py --- tests/run-multitests.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 567e9b5ffa388..7ab4e85c57d79 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -92,20 +92,25 @@ def start_file(self, filename, prepend="", append=""): class PyInstanceSubProcess(PyInstance): - def __init__(self, cmd): - self.cmd = cmd + def __init__(self, argv, env=None): + self.argv = argv + self.env = {n: v for n, v in (i.split("=") for i in env)} if env else None self.popen = None self.finished = True def __str__(self): - return self.cmd[0].rsplit("/")[-1] + return self.argv[0].rsplit("/")[-1] def run_script(self, script): output = b"" err = None try: p = subprocess.run( - self.cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, input=script + self.argv, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + input=script, + env=self.env, ) output = p.stdout except subprocess.CalledProcessError as er: @@ -114,7 +119,11 @@ def run_script(self, script): def start_script(self, script): self.popen = subprocess.Popen( - self.cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + self.argv, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=self.env, ) self.popen.stdin.write(script) self.popen.stdin.close() @@ -404,16 +413,20 @@ def main(): instances_test = [] for i in cmd_args.instance: - if i.startswith("exec:"): - instances_test.append(PyInstanceSubProcess([i[len("exec:") :]])) - elif i == "micropython": - instances_test.append(PyInstanceSubProcess([MICROPYTHON])) - elif i == "cpython": - instances_test.append(PyInstanceSubProcess([CPYTHON3])) - elif i.startswith("pyb:"): - instances_test.append(PyInstancePyboard(i[len("pyb:") :])) + # Each instance arg is ,ENV=VAR,ENV=VAR... + i = i.split(",") + cmd = i[0] + env = i[1:] + if cmd.startswith("exec:"): + instances_test.append(PyInstanceSubProcess([cmd[len("exec:") :]], env)) + elif cmd == "micropython": + instances_test.append(PyInstanceSubProcess([MICROPYTHON], env)) + elif cmd == "cpython": + instances_test.append(PyInstanceSubProcess([CPYTHON3], env)) + elif cmd.startswith("pyb:"): + instances_test.append(PyInstancePyboard(cmd[len("pyb:") :])) else: - print("unknown instance string: {}".format(i), file=sys.stderr) + print("unknown instance string: {}".format(cmd), file=sys.stderr) sys.exit(1) for _ in range(max_instances - len(instances_test)): From 2934e41df0938b26cffc8eb1ea9f24a077990b61 Mon Sep 17 00:00:00 2001 From: Nick Crabtree Date: Sun, 7 Jun 2020 11:42:54 +0100 Subject: [PATCH 061/352] docs/esp8266: Add quickref documentation for UART on esp8266. This patch adds quickref documentation for the change in commit afd0701bf7a9dcb50c5ab46b0ae88b303fec6ed3. This commit added the ability to disable the REPL and hence use UART0 for serial communication on the esp8266, but was not previously documented anywhere. The text is largely taken from the commit message, with generic information on using the UART duplicated from the Wipy quickref document. --- docs/esp8266/quickref.rst | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index 9c7db3205a7e6..c884d93fc9940 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -138,6 +138,45 @@ Also note that Pin(16) is a special pin (used for wakeup from deepsleep mode) and may be not available for use with higher-level classes like ``Neopixel``. +UART (serial bus) +----------------- + +See :ref:`machine.UART `. :: + + from machine import UART + uart = UART(0, baudrate=9600) + uart.write('hello') + uart.read(5) # read up to 5 bytes + +Two UARTs are available. UART0 is on Pins 1 (TX) and 3 (RX). UART0 is +bidirectional, and by default is used for the REPL. UART1 is on Pins 2 +(TX) and 8 (RX) however Pin 8 is used to connect the flash chip, so +UART1 is TX only. + +When UART0 is attached to the REPL, all incoming chars on UART(0) go +straight to stdin so uart.read() will always return None. Use +sys.stdin.read() if it's needed to read characters from the UART(0) +while it's also used for the REPL (or detach, read, then reattach). +When detached the UART(0) can be used for other purposes. + +If there are no objects in any of the dupterm slots when the REPL is +started (on hard or soft reset) then UART(0) is automatically attached. +Without this, the only way to recover a board without a REPL would be to +completely erase and reflash (which would install the default boot.py which +attaches the REPL). + +To detach the REPL from UART0, use:: + + import uos + uos.dupterm(None, 1) + +The REPL is attached by default. If you have detached it, to reattach +it use:: + + import uos, machine + uart = machine.UART(0, 115200) + uos.dupterm(uart, 1) + PWM (pulse width modulation) ---------------------------- From 05e5d411b53dcba9529b6ac1e9388bab65f8c11b Mon Sep 17 00:00:00 2001 From: Albort Xue Date: Wed, 10 Jun 2020 17:11:30 +0800 Subject: [PATCH 062/352] mimxrt/boards: Set __heap_size__ to 0 in MIMXRT1011.ld. Do not use the traditional C heap in order to save memory, because the traditional C heap is unused in MicroPython. --- ports/mimxrt/boards/MIMXRT1011.ld | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 6b59649fcaf7e..0512c48a7f2d9 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -3,6 +3,9 @@ __stack_size__ = 0x6000; _estack = __StackTop; _sstack = __StackLimit; +/* Do not use the traditional C heap. */ +__heap_size__ = 0; + /* Use second OCRAM bank for GC heap. */ _gc_heap_start = ORIGIN(m_data2); _gc_heap_end = ORIGIN(m_data2) + LENGTH(m_data2); From 38b4f1569e7755f78b991352ae5d53055ad67790 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Sat, 2 May 2020 11:21:48 -0500 Subject: [PATCH 063/352] zephyr: Fix and rename stacks_analyze function in zephyr module. Zephyr deprecated and then removed its stack_analyze function because it was unsafe. Use the new zephyr thread analyzer instead and rename the MicroPython function to zephyr.thread_analyze() to be more consistent with the implementation. Tested on mimxrt1050_evk. The output now looks like this: >>> zephyr.thread_analyze() Thread analyze: 80004ff4 : unused 400 usage 112 / 512 (21 %) rx_workq : unused 1320 usage 180 / 1500 (12 %) tx_workq : unused 992 usage 208 / 1200 (17 %) net_mgmt : unused 656 usage 112 / 768 (14 %) sysworkq : unused 564 usage 460 / 1024 (44 %) idle : unused 256 usage 64 / 320 (20 %) main : unused 2952 usage 1784 / 4736 (37 %) --- ports/zephyr/modzephyr.c | 29 +++++++---------------------- ports/zephyr/prj.conf | 5 ++--- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/ports/zephyr/modzephyr.c b/ports/zephyr/modzephyr.c index d4ee610b204c6..71b44d7df1611 100644 --- a/ports/zephyr/modzephyr.c +++ b/ports/zephyr/modzephyr.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include "modzephyr.h" #include "py/runtime.h" @@ -45,27 +45,12 @@ STATIC mp_obj_t mod_current_tid(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_current_tid_obj, mod_current_tid); -#ifdef CONFIG_THREAD_STACK_INFO -extern k_tid_t const _main_thread; -extern k_tid_t const _idle_thread; - -static void thread_stack_dump(const struct k_thread *thread, void *user_data) { - const char *th_name = k_thread_name_get((k_tid_t)thread); - - if (th_name == NULL) { - static char tid[9]; - snprintf(tid, sizeof(tid), "%08x", (int)thread); - th_name = tid; - } - - stack_analyze(th_name, (char *)thread->stack_info.start, thread->stack_info.size); -} - -STATIC mp_obj_t mod_stacks_analyze(void) { - k_thread_foreach(thread_stack_dump, NULL); +#ifdef CONFIG_THREAD_ANALYZER +STATIC mp_obj_t mod_thread_analyze(void) { + thread_analyzer_print(); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_stacks_analyze_obj, mod_stacks_analyze); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_analyze_obj, mod_thread_analyze); #endif #ifdef CONFIG_NET_SHELL @@ -84,8 +69,8 @@ STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr) }, { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) }, { MP_ROM_QSTR(MP_QSTR_current_tid), MP_ROM_PTR(&mod_current_tid_obj) }, - #ifdef CONFIG_THREAD_STACK_INFO - { MP_ROM_QSTR(MP_QSTR_stacks_analyze), MP_ROM_PTR(&mod_stacks_analyze_obj) }, + #ifdef CONFIG_THREAD_ANALYZER + { MP_ROM_QSTR(MP_QSTR_thread_analyze), MP_ROM_PTR(&mod_thread_analyze_obj) }, #endif #ifdef CONFIG_NET_SHELL diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index 993dfdc26f9ec..e6ccdadcf063b 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -50,10 +50,9 @@ CONFIG_NET_DHCPV4=y # Diagnostics and debugging # Required for zephyr.stack_analyze() -CONFIG_INIT_STACKS=y -CONFIG_THREAD_MONITOR=y +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_USE_PRINTK=y CONFIG_THREAD_NAME=y -CONFIG_THREAD_STACK_INFO=y # Required for usocket.pkt_get_info() CONFIG_NET_BUF_POOL_USAGE=y From 8b061f2d799d120f06d68023eb17e5b5e24d50cc Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Sat, 2 May 2020 11:38:58 -0500 Subject: [PATCH 064/352] zephyr: Fix floating point configuration. Zephyr renamed CONFIG_FLOAT to CONFIG_FPU to better reflect its semantics of enabling the hardware floating point unit (FPU) rather than enabling toolchain-level floating point support (i.e., software floating point for FPU-less socs). --- ports/zephyr/prj.conf | 2 +- ports/zephyr/prj_minimal.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index e6ccdadcf063b..50cfa005025dc 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -11,7 +11,7 @@ CONFIG_CONSOLE_GETCHAR_BUFSIZE=128 CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 CONFIG_NEWLIB_LIBC=y -CONFIG_FLOAT=y +CONFIG_FPU=y CONFIG_MAIN_STACK_SIZE=4736 # Enable sensor subsystem (doesn't add code if not used). diff --git a/ports/zephyr/prj_minimal.conf b/ports/zephyr/prj_minimal.conf index d9c2894e6f2de..8b2104925ab67 100644 --- a/ports/zephyr/prj_minimal.conf +++ b/ports/zephyr/prj_minimal.conf @@ -1,5 +1,5 @@ CONFIG_NEWLIB_LIBC=y -CONFIG_FLOAT=y +CONFIG_FPU=y CONFIG_MAIN_STACK_SIZE=4096 CONFIG_UART_INTERRUPT_DRIVEN=y From db02cb061d8ecd4c7dec90f37e314197a6030ee8 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Thu, 14 May 2020 08:07:49 -0500 Subject: [PATCH 065/352] zephyr: Update for refactored zephyr device structures. Updates the zephyr port to use refactored device structures introduced in zephyr 2.3. --- ports/zephyr/machine_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/machine_i2c.c b/ports/zephyr/machine_i2c.c index f00c1d34225ac..4b29f41d1ef71 100644 --- a/ports/zephyr/machine_i2c.c +++ b/ports/zephyr/machine_i2c.c @@ -48,7 +48,7 @@ typedef struct _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, "%s", self->dev->config->name); + mp_printf(print, "%s", self->dev->name); } 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) { From 6aff27ac3c2115734e5b0181956de0c04cfbb651 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Thu, 14 May 2020 08:32:32 -0500 Subject: [PATCH 066/352] zephyr: Update to new zephyr timeout API. Updates the zephyr port to use the new timeout api introduced in zephyr 2.3. --- ports/zephyr/mphalport.h | 2 +- ports/zephyr/uart_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h index 594f6a1f69830..8434a388b0a5e 100644 --- a/ports/zephyr/mphalport.h +++ b/ports/zephyr/mphalport.h @@ -21,7 +21,7 @@ static inline void mp_hal_delay_us(mp_uint_t delay) { } static inline void mp_hal_delay_ms(mp_uint_t delay) { - k_sleep(delay); + k_msleep(delay); } #define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us)) diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c index fef9f00ab1625..63ecc8289e3c9 100644 --- a/ports/zephyr/uart_core.c +++ b/ports/zephyr/uart_core.c @@ -49,7 +49,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { while (len--) { char c = *str++; while (console_putchar(c) == -1) { - k_sleep(1); + k_msleep(1); } } #else From 4837b1caa2b7a149769a8e3302350acc4f4bd4d9 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Tue, 9 Jun 2020 18:10:40 -0500 Subject: [PATCH 067/352] zephyr: Convert DT_FLASH_AREA usages to new dts macros. Converts DT_FLASH_AREA usages in the zephyr port to new device tree macros introduced in zephyr 2.3. Tested with littlefs on the reel_board. --- ports/zephyr/main.c | 8 ++++++-- ports/zephyr/zephyr_storage.c | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index e304dfd25824d..104954f595c0a 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -38,6 +38,10 @@ #include #endif +#ifdef CONFIG_FLASH_MAP +#include +#endif + #include "py/mperrno.h" #include "py/compile.h" #include "py/runtime.h" @@ -100,8 +104,8 @@ STATIC void vfs_init(void) { mp_obj_t args[] = { mp_obj_new_str(CONFIG_DISK_SDHC_VOLUME_NAME, strlen(CONFIG_DISK_SDHC_VOLUME_NAME)) }; bdev = zephyr_disk_access_type.make_new(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args); mount_point_str = "/sd"; - #elif defined(CONFIG_FLASH_MAP) && defined(DT_FLASH_AREA_STORAGE_ID) - mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(DT_FLASH_AREA_STORAGE_ID), MP_OBJ_NEW_SMALL_INT(4096) }; + #elif defined(CONFIG_FLASH_MAP) && FLASH_AREA_LABEL_EXISTS(storage) + mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(FLASH_AREA_ID(storage)), MP_OBJ_NEW_SMALL_INT(4096) }; bdev = zephyr_flash_area_type.make_new(&zephyr_flash_area_type, ARRAY_SIZE(args), 0, args); mount_point_str = "/flash"; #endif diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index c533cb8fd721b..83f19a8feeadd 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -245,8 +245,8 @@ STATIC const mp_rom_map_elem_t zephyr_flash_area_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_flash_area_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_flash_area_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_flash_area_ioctl_obj) }, - #ifdef DT_FLASH_AREA_STORAGE_ID - { MP_ROM_QSTR(MP_QSTR_STORAGE), MP_ROM_INT(DT_FLASH_AREA_STORAGE_ID) }, + #if FLASH_AREA_LABEL_EXISTS(storage) + { MP_ROM_QSTR(MP_QSTR_STORAGE), MP_ROM_INT(FLASH_AREA_ID(storage)) }, #endif }; STATIC MP_DEFINE_CONST_DICT(zephyr_flash_area_locals_dict, zephyr_flash_area_locals_dict_table); From b1651ff092e86099b39cba6fe5abfb3ad7514daf Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Tue, 9 Jun 2020 18:22:04 -0500 Subject: [PATCH 068/352] zephyr: Increase minimum required cmake version to 3.13.1. The minimum required cmake version has been 3.13.1 since zephyr 1.14.0. --- ports/zephyr/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 017b0689cef58..2cc19a93f1a4d 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.13.1) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(NONE) From 1ae861819dbe8a57cdfda882b093d8d564d8f1c9 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Tue, 9 Jun 2020 18:28:09 -0500 Subject: [PATCH 069/352] zephyr: Use cmake find_package to locate zephyr. Updates the zephyr port to use the ZEPHYR_BASE environment variable only to locate the zephyr cmake package, allowing cmake to cache the variable. --- ports/zephyr/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 2cc19a93f1a4d..50cd031d4dd08 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13.1) -include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(NONE) target_sources(app PRIVATE src/zephyr_start.c src/zephyr_getchar.c) From 2b9900380ad2d0ab232e5e3a93c64aaddf90c55a Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 12 Jun 2020 10:15:25 +1000 Subject: [PATCH 070/352] stm32/boards/STM32F769DISC: Use macro instead of const for flash size. So that the flash size can be changed in just one place. Also remove the duplicate cache entry. --- ports/stm32/boards/STM32F769DISC/board_init.c | 1 - ports/stm32/boards/STM32F769DISC/mpconfigboard.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/stm32/boards/STM32F769DISC/board_init.c b/ports/stm32/boards/STM32F769DISC/board_init.c index 67fad407a0e28..6bb3dfb1f6178 100644 --- a/ports/stm32/boards/STM32F769DISC/board_init.c +++ b/ports/stm32/boards/STM32F769DISC/board_init.c @@ -9,7 +9,6 @@ const mp_spiflash_config_t spiflash_config = { .bus_kind = MP_SPIFLASH_BUS_QSPI, .bus.u_qspi.data = NULL, .bus.u_qspi.proto = &qspi_proto, - .cache = NULL, .cache = &spi_bdev_cache, }; diff --git a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h index 8dbac2d01574a..e1fe93bb0ed44 100644 --- a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h @@ -41,7 +41,7 @@ extern struct _spi_bdev_t spi_bdev; #if !USE_QSPI_XIP #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) #define MICROPY_HW_BDEV_IOCTL(op, arg) ( \ - (op) == BDEV_IOCTL_NUM_BLOCKS ? (64 * 1024 * 1024 / FLASH_BLOCK_SIZE) : \ + (op) == BDEV_IOCTL_NUM_BLOCKS ? ((1 << MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) / 8 / FLASH_BLOCK_SIZE) : \ (op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \ spi_bdev_ioctl(&spi_bdev, (op), (arg)) \ ) From 5093597542de86714a1eb5a8a4463d4234c2f2cd Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 12 Jun 2020 11:38:24 +1000 Subject: [PATCH 071/352] top: Update contribution and commit guide to include optional sign-off. MicroPython already requires contributors to implicitly sign-off on a set of points, which are listed in CODECONVENTIONS.md. This commit adjusts this wording to allow explicit sign-off using the git "Signed-off-by:" feature. There is no reference made to https://developercertificate.org/ because the project already has its own version of this. Signed-off-by: Damien George --- CODECONVENTIONS.md | 16 +++++++++++----- CONTRIBUTING.md | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CODECONVENTIONS.md b/CODECONVENTIONS.md index b18c9818ade25..78fb912a6ab75 100644 --- a/CODECONVENTIONS.md +++ b/CODECONVENTIONS.md @@ -27,9 +27,12 @@ change beyond 5 lines would likely require such detailed description. To get good practical examples of good commits and their messages, browse the `git log` of the project. -MicroPython doesn't require explicit sign-off for patches ("Signed-off-by" -lines and similar). Instead, the commit message, and your name and email -address on it construes your sign-off of the following: +When committing you are encouraged to sign-off your commit by adding +"Signed-off-by" lines and similar, eg using "git commit -s". If you don't +explicitly sign-off in this way then the commit message, which includes your +name and email address in the "Author" line, implies your sign-off. In either +case, of explicit or implicit sign-off, you are certifying and signing off +against the following: * That you wrote the change yourself, or took it from a project with a compatible license (in the latter case the commit message, and possibly @@ -43,8 +46,11 @@ address on it construes your sign-off of the following: copyright for your changes (for smaller changes, the commit message conveys your copyright; if you make significant changes to a particular source module, you're welcome to add your name to the file header). -* Your signature for all of the above, which is the 'Author' line in - the commit message, and which should include your full real name and +* Your contribution including commit message will be publicly and + indefinitely available for anyone to access, including redistribution + under the terms of the project's license. +* Your signature for all of the above, which is the "Signed-off-by" line + or the "Author" line in the commit message, includes your full real name and a valid and active email address by which you can be contacted in the foreseeable future. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06f970607b9bb..73004ac5181ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,6 @@ make sure that you are acquainted with Contributor Guidelines: https://github.com/micropython/micropython/wiki/ContributorGuidelines -and Code Conventions: +as well as the Code Conventions, which includes details of how to commit: https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md From bd7c92e17d7544ad840038ef74b1998854060090 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 13 Jun 2020 13:03:00 -0500 Subject: [PATCH 072/352] tools/uncrustify.cfg: Remove deprecated sp_word_brace option. This option was removed in uncrustify v0.71. Signed-off-by: David Lechner --- tools/uncrustify.cfg | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/uncrustify.cfg b/tools/uncrustify.cfg index b0b1239b1a1fb..da422ceb0f4d7 100644 --- a/tools/uncrustify.cfg +++ b/tools/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.70.1 +# Uncrustify-0.71.0 # # General options @@ -669,12 +669,6 @@ sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line. sp_getset_brace = ignore # ignore/add/remove/force -# Add or remove space between a variable and '{' for C++ uniform -# initialization. -# -# Default: add -sp_word_brace = add # ignore/add/remove/force - # Add or remove space between a variable and '{' for a namespace. # # Default: add From ecd782631608636f300d6b885b4f310e546a2b3f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 13 Jun 2020 13:05:48 -0500 Subject: [PATCH 073/352] tools/codeformat.py: Remove sizeof fixup. Formatting for `* sizeof` was fixed in uncrustify v0.71, so we no longer need the fixups for it. Also, there was one file where the updated uncrustify caught a problem that the regex didn't pick up, which is updated in this commit. Signed-off-by: David Lechner --- py/objfun.c | 4 ++-- tools/codeformat.py | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/py/objfun.c b/py/objfun.c index 3a63d8f439d2b..f9a6b4ebc2727 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -197,8 +197,8 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \ \ /* state size in bytes */ \ - state_size_out_var = n_state_out_var *sizeof(mp_obj_t) \ - + n_exc_stack *sizeof(mp_exc_stack_t); \ + state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \ + + n_exc_stack * sizeof(mp_exc_stack_t); \ } #define INIT_CODESTATE(code_state, _fun_bc, _n_state, n_args, n_kw, args) \ diff --git a/tools/codeformat.py b/tools/codeformat.py index 5aef4ccfbf242..364037d2fac86 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -76,9 +76,6 @@ PY_EXTS = (".py",) -FIXUP_REPLACEMENTS = ((re.compile("sizeof\(([a-z_]+)\) \*\(([a-z_]+)\)"), r"sizeof(\1) * (\2)"),) - - def list_files(paths, exclusions=None, prefix=""): files = set() for pattern in paths: @@ -124,10 +121,6 @@ def fixup_c(filename): if directive == "endif": dedent_stack.pop() - # Apply general regex-based fixups. - for regex, replacement in FIXUP_REPLACEMENTS: - l = regex.sub(replacement, l) - # Write out line. f.write(l) From b4d0d7bf03a787237740b80d2ecdeb0a3ce1d4ff Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 14 Jun 2020 11:02:19 -0500 Subject: [PATCH 074/352] tools/uncrustify: Update config for v0.71.0. This is the result of running... uncrustify -c tools/uncrustify.cfg --update-config-with-doc -o tools/uncrustify.cfg ...with some manual fixups to correct places where it changed things it should not have. Essentially it just adds new config parameters introduced in v0.71.0 with their default values. Signed-off-by: David Lechner --- tools/uncrustify.cfg | 118 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/tools/uncrustify.cfg b/tools/uncrustify.cfg index da422ceb0f4d7..96b471742cc51 100644 --- a/tools/uncrustify.cfg +++ b/tools/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.71.0 +# Uncrustify-0.71.0_f # # General options @@ -37,13 +37,18 @@ string_replace_tab_chars = false # true/false # Improvements to template detection may make this option obsolete. tok_split_gte = false # true/false +# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multiline macros) +disable_processing_nl_cont = false # true/false + # Specify the marker used in comments to disable processing of part of the # file. +# The comment should be used alone in one line. # # Default: *INDENT-OFF* disable_processing_cmt = " *FORMAT-OFF*" # string # Specify the marker used in comments to (re)enable processing in a file. +# The comment should be used alone in one line. # # Default: *INDENT-ON* enable_processing_cmt = " *FORMAT-ON*" # string @@ -604,6 +609,10 @@ sp_catch_paren = ignore # ignore/add/remove/force # in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. sp_oc_catch_paren = ignore # ignore/add/remove/force +# (OC) Add or remove space before Objective-C protocol list +# as in '@protocol Protocol' or '@interface MyClass : NSObject'. +sp_before_oc_proto_list = ignore # ignore/add/remove/force + # (OC) Add or remove space between class name and '(' # in '@interface className(categoryName):BaseClass' sp_oc_classname_paren = ignore # ignore/add/remove/force @@ -669,6 +678,10 @@ sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line. sp_getset_brace = ignore # ignore/add/remove/force +# Add or remove space between a variable and '{' for C++ uniform +# initialization. +sp_word_brace_init_lst = ignore # ignore/add/remove/force + # Add or remove space between a variable and '{' for a namespace. # # Default: add @@ -1107,6 +1120,10 @@ indent_member_single = false # true/false # Spaces to indent single line ('//') comments on lines before code. indent_sing_line_comments = 0 # unsigned number +# When opening a paren for a control statement (if, for, while, etc), increase +# the indent level by this value. Negative values decrease the indent level. +indent_sparen_extra = 0 # number + # Whether to indent trailing single line ('//') comments relative to the code # instead of trying to keep the same absolute column. indent_relative_single_line_comments = false # true/false @@ -1213,12 +1230,19 @@ indent_preserve_sql = false # true/false # Default: true indent_align_assign = false # true/false +# If true, the indentation of the chunks after a '=' sequence will be set at +# LHS token indentation column before '='. +indent_off_after_assign = false # true/false + # Whether to align continued statements at the '('. If false or the '(' is # followed by a newline, the next line indent is one tab. # # Default: true indent_align_paren = false # true/false +# (OC) Whether to indent Objective-C code inside message selectors. +indent_oc_inside_msg_sel = false # true/false + # (OC) Whether to indent Objective-C blocks at brace level instead of usual # rules. indent_oc_block = false # true/false @@ -1278,6 +1302,15 @@ indent_token_after_brace = true # true/false # Whether to indent the body of a C++11 lambda. indent_cpp_lambda_body = false # true/false +# How to indent compound literals that are being returned. +# true: add both the indent from return & the compound literal open brace (ie: +# 2 indent levels) +# false: only indent 1 level, don't add the indent for the open brace, only add +# the indent for the return. +# +# Default: true +indent_compound_literal_return = true # true/false + # (C#) Whether to indent a 'using' block if no braces are used. # # Default: true @@ -1290,6 +1323,12 @@ indent_using_block = true # true/false # 2: When the `:` is a continuation, indent it under `?` indent_ternary_operator = 0 # unsigned number +# Whether to indent the statments inside ternary operator. +indent_inside_ternary_operator = false # true/false + +# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. +indent_off_after_return = false # true/false + # If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. indent_off_after_return_new = false # true/false @@ -1323,7 +1362,7 @@ nl_getset_leave_one_liners = false # true/false nl_cs_property_leave_one_liners = false # true/false # Don't split one-line function definitions, as in 'int foo() { return 0; }'. -# night modify nl_func_type_name +# might modify nl_func_type_name nl_func_leave_one_liners = false # true/false # Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. @@ -1430,6 +1469,9 @@ nl_else_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and 'if'. nl_else_if = ignore # ignore/add/remove/force +# Add or remove newline before '{' opening brace +nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force + # Add or remove newline before 'if'/'else if' closing parenthesis. nl_before_if_closing_paren = ignore # ignore/add/remove/force @@ -1678,6 +1720,9 @@ nl_func_decl_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function definition. nl_func_def_args = ignore # ignore/add/remove/force +# Add or remove newline after each ',' in a function call. +nl_func_call_args = ignore # ignore/add/remove/force + # Whether to add a newline after each ',' in a function declaration if '(' # and ')' are in different lines. If false, nl_func_decl_args is used instead. nl_func_decl_args_multi_line = false # true/false @@ -1719,6 +1764,9 @@ nl_func_call_empty = ignore # ignore/add/remove/force # has preference over nl_func_call_start_multi_line. nl_func_call_start = ignore # ignore/add/remove/force +# Whether to add a newline before ')' in a function call. +nl_func_call_end = ignore # ignore/add/remove/force + # Whether to add a newline after '(' in a function call if '(' and ')' are in # different lines. nl_func_call_start_multi_line = false # true/false @@ -1731,6 +1779,9 @@ nl_func_call_args_multi_line = false # true/false # different lines. nl_func_call_end_multi_line = false # true/false +# Whether to respect nl_func_call_XXX option incase of closure args. +nl_func_call_args_multi_line_ignore_closures = false # true/false + # Whether to add a newline after '<' of a template parameter list. nl_template_start = false # true/false @@ -1865,7 +1916,7 @@ nl_before_return = false # true/false # close brace. nl_after_return = false # true/false -# (Java) Whether to put a blank line before a member '.' or '->' operators. +# Whether to put a blank line before a member '.' or '->' operators. nl_before_member = ignore # ignore/add/remove/force # (Java) Whether to put a blank line after a member '.' or '->' operators. @@ -2128,6 +2179,26 @@ nl_after_annotation = ignore # ignore/add/remove/force # (Java) Add or remove newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force +# The number of newlines before a whole-file #ifdef. +# +# 0: No change (default). +nl_before_whole_file_ifdef = 0 # unsigned number + +# The number of newlines after a whole-file #ifdef. +# +# 0: No change (default). +nl_after_whole_file_ifdef = 0 # unsigned number + +# The number of newlines before a whole-file #endif. +# +# 0: No change (default). +nl_before_whole_file_endif = 0 # unsigned number + +# The number of newlines after a whole-file #endif. +# +# 0: No change (default). +nl_after_whole_file_endif = 0 # unsigned number + # # Positioning options # @@ -2497,6 +2568,11 @@ align_oc_msg_colon_first = false # true/false # on the ':'. align_oc_decl_colon = false # true/false +# (OC) Whether to not align parameters in an Objectve-C message call if first +# colon is not on next line of the message call (the same way Xcode does +# aligment) +align_oc_msg_colon_xcode_like = false # true/false + # # Comment modification options # @@ -2728,6 +2804,25 @@ mod_sort_using = false # true/false # break your code if your includes/imports have ordering dependencies. mod_sort_include = false # true/false +# Whether to prioritize '#include' and '#import' statements that contain +# filename without extension when sorting is enabled. +mod_sort_incl_import_prioritize_filename = false # true/false + +# Whether to prioritize '#include' and '#import' statements that does not +# contain extensions when sorting is enabled. +mod_sort_incl_import_prioritize_extensionless = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# angle over quotes when sorting is enabled. +mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false + +# Whether to ignore file extension in '#include' and '#import' statements +# for sorting comparison. +mod_sort_incl_import_ignore_extension = false # true/false + +# Whether to group '#include' and '#import' statements when sorting is enabled. +mod_sort_incl_import_grouping_enabled = false # true/false + # Whether to move a 'break' that appears after a fully braced 'case' before # the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. mod_move_case_break = false # true/false @@ -2914,6 +3009,11 @@ use_sp_after_angle_always = false # true/false # Default: true use_options_overriding_for_qt_macros = true # true/false +# If true: the form feed character is removed from the list +# of whitespace characters. +# See https://en.cppreference.com/w/cpp/string/byte/isspace +use_form_feed_no_more_as_whitespace_character = false # true/false + # # Warn levels - 1: error, 2: warning (default), 3: note # @@ -2924,6 +3024,16 @@ use_options_overriding_for_qt_macros = true # true/false # Default: 2 warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number +# Limit the number of loops. +# Used by uncrustify.cpp to exit from infinite loop. +# 0: no limit. +debug_max_number_of_loops = 0 # number + +# Set the number of the line to protocol; +# Used in the function prot_the_line if the 2. parameter is zero. +# 0: nothing protocol. +debug_line_number_to_protocol = 0 # number + # Meaning of the settings: # Ignore - do not do any changes # Add - makes sure there is 1 or more space/brace/newline/etc @@ -2976,7 +3086,7 @@ warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number # `macro-close END_MESSAGE_MAP` # # -# option(s) with 'not default' value: 0 +# option(s) with 'not default' value: 67 # # Custom types for MicroPython From 0fd91e39b1711772c88cfe4e0aaf817fe3387ba6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 21:42:37 +1000 Subject: [PATCH 075/352] py/compile: Convert scope test to SCOPE_IS_COMP_LIKE macro. This macro can be used elsewhere. --- py/compile.c | 2 +- py/scope.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/py/compile.c b/py/compile.c index 22f47ee0d295f..8835ec2f8d06d 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3083,7 +3083,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); } EMIT(return_value); - } else if (scope->kind == SCOPE_LIST_COMP || scope->kind == SCOPE_DICT_COMP || scope->kind == SCOPE_SET_COMP || scope->kind == SCOPE_GEN_EXPR) { + } else if (SCOPE_IS_COMP_LIKE(scope->kind)) { // a bit of a hack at the moment assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); diff --git a/py/scope.h b/py/scope.h index ba07c39498b5a..b52d98ea1c72d 100644 --- a/py/scope.h +++ b/py/scope.h @@ -55,6 +55,7 @@ typedef struct _id_info_t { } id_info_t; #define SCOPE_IS_FUNC_LIKE(s) ((s) >= SCOPE_LAMBDA) +#define SCOPE_IS_COMP_LIKE(s) (SCOPE_LIST_COMP <= (s) && (s) <= SCOPE_GEN_EXPR) // scope is a "block" in Python parlance typedef enum { From 178395031157f61af5426add2de1d76d91440b21 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 21:42:44 +1000 Subject: [PATCH 076/352] py/compile: Implement PEP 572, assignment expressions with := operator. The syntax matches CPython and the semantics are equivalent except that, unlike CPython, MicroPython allows using := to assign to comprehension iteration variables, because disallowing this would take a lot of code to check for it. The new compile-time option MICROPY_PY_ASSIGN_EXPR selects this feature and is enabled by default, following MICROPY_PY_ASYNC_AWAIT. --- py/compile.c | 29 ++++++++++++++++++++++++++++- py/grammar.h | 21 ++++++++++++++++----- py/lexer.c | 6 ++++-- py/lexer.h | 1 + py/mpconfig.h | 5 +++++ tests/cmdline/cmd_parsetree.py.exp | 2 +- 6 files changed, 55 insertions(+), 9 deletions(-) diff --git a/py/compile.c b/py/compile.c index 8835ec2f8d06d..da5c9190a3ef4 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2013-2020 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 @@ -2108,6 +2108,27 @@ STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) { compile_funcdef_lambdef(comp, this_scope, pns->nodes[0], PN_varargslist); } +#if MICROPY_PY_ASSIGN_EXPR +STATIC void compile_namedexpr_helper(compiler_t *comp, mp_parse_node_t pn_name, mp_parse_node_t pn_expr) { + if (!MP_PARSE_NODE_IS_ID(pn_name)) { + compile_syntax_error(comp, (mp_parse_node_t)pn_name, MP_ERROR_TEXT("can't assign to expression")); + } + compile_node(comp, pn_expr); + EMIT(dup_top); + scope_t *old_scope = comp->scope_cur; + if (SCOPE_IS_COMP_LIKE(comp->scope_cur->kind)) { + // Use parent's scope for assigned value so it can "escape" + comp->scope_cur = comp->scope_cur->parent; + } + compile_store_id(comp, MP_PARSE_NODE_LEAF_ARG(pn_name)); + comp->scope_cur = old_scope; +} + +STATIC void compile_namedexpr(compiler_t *comp, mp_parse_node_struct_t *pns) { + compile_namedexpr_helper(comp, pns->nodes[0], pns->nodes[1]); +} +#endif + STATIC void compile_or_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) { bool cond = MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test; uint l_end = comp_next_label(comp); @@ -2353,6 +2374,12 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; dblstar_args_node = pns_arg; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) { + #if MICROPY_PY_ASSIGN_EXPR + if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_4)) { + compile_namedexpr_helper(comp, pns_arg->nodes[0], ((mp_parse_node_struct_t *)pns_arg->nodes[1])->nodes[0]); + n_positional++; + } else + #endif if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_comp_for)) { if (!MP_PARSE_NODE_IS_ID(pns_arg->nodes[0])) { compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("LHS of keyword arg must be an id")); diff --git a/py/grammar.h b/py/grammar.h index c3d30cdf7c467..f5f1ff666d9e3 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2013-2020 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 @@ -184,10 +184,10 @@ DEF_RULE_NC(async_stmt_2, or(3), rule(funcdef), rule(with_stmt), rule(for_stmt)) #else DEF_RULE_NC(compound_stmt, or(8), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated)) #endif -DEF_RULE(if_stmt, c(if_stmt), and(6), tok(KW_IF), rule(test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt)) +DEF_RULE(if_stmt, c(if_stmt), and(6), tok(KW_IF), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt)) DEF_RULE_NC(if_stmt_elif_list, one_or_more, rule(if_stmt_elif)) -DEF_RULE_NC(if_stmt_elif, and(4), tok(KW_ELIF), rule(test), tok(DEL_COLON), rule(suite)) -DEF_RULE(while_stmt, c(while_stmt), and(5), tok(KW_WHILE), rule(test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) +DEF_RULE_NC(if_stmt_elif, and(4), tok(KW_ELIF), rule(namedexpr_test), tok(DEL_COLON), rule(suite)) +DEF_RULE(while_stmt, c(while_stmt), and(5), tok(KW_WHILE), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) DEF_RULE(for_stmt, c(for_stmt), and(7), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(testlist), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) DEF_RULE(try_stmt, c(try_stmt), and(4), tok(KW_TRY), tok(DEL_COLON), rule(suite), rule(try_stmt_2)) DEF_RULE_NC(try_stmt_2, or(2), rule(try_stmt_except_and_more), rule(try_stmt_finally)) @@ -210,6 +210,12 @@ DEF_RULE(suite_block_stmts, c(generic_all_nodes), one_or_more, rule(stmt)) // lambdef: 'lambda' [varargslist] ':' test // lambdef_nocond: 'lambda' [varargslist] ':' test_nocond +#if MICROPY_PY_ASSIGN_EXPR +DEF_RULE(namedexpr_test, c(namedexpr), and_ident(2), rule(test), opt_rule(namedexpr_test_2)) +DEF_RULE_NC(namedexpr_test_2, and_ident(2), tok(OP_ASSIGN), rule(test)) +#else +DEF_RULE_NC(namedexpr_test, or(1), rule(test)) +#endif DEF_RULE_NC(test, or(2), rule(lambdef), rule(test_if_expr)) DEF_RULE(test_if_expr, c(test_if_expr), and_ident(2), rule(or_test), opt_rule(test_if_else)) DEF_RULE_NC(test_if_else, and(4), tok(KW_IF), rule(or_test), tok(KW_ELSE), rule(test)) @@ -276,7 +282,7 @@ DEF_RULE_NC(atom_2b, or(2), rule(yield_expr), rule(testlist_comp)) DEF_RULE(atom_bracket, c(atom_bracket), and(3), tok(DEL_BRACKET_OPEN), opt_rule(testlist_comp), tok(DEL_BRACKET_CLOSE)) DEF_RULE(atom_brace, c(atom_brace), and(3), tok(DEL_BRACE_OPEN), opt_rule(dictorsetmaker), tok(DEL_BRACE_CLOSE)) DEF_RULE_NC(testlist_comp, and_ident(2), rule(testlist_comp_2), opt_rule(testlist_comp_3)) -DEF_RULE_NC(testlist_comp_2, or(2), rule(star_expr), rule(test)) +DEF_RULE_NC(testlist_comp_2, or(2), rule(star_expr), rule(namedexpr_test)) DEF_RULE_NC(testlist_comp_3, or(2), rule(comp_for), rule(testlist_comp_3b)) DEF_RULE_NC(testlist_comp_3b, and_ident(2), tok(DEL_COMMA), opt_rule(testlist_comp_3c)) DEF_RULE_NC(testlist_comp_3c, list_with_end, rule(testlist_comp_2), tok(DEL_COMMA)) @@ -342,7 +348,12 @@ DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test)) // comp_if: 'if' test_nocond [comp_iter] DEF_RULE_NC(argument, and_ident(2), rule(test), opt_rule(argument_2)) +#if MICROPY_PY_ASSIGN_EXPR +DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(argument_3), rule(argument_4)) +DEF_RULE_NC(argument_4, and(2), tok(OP_ASSIGN), rule(test)) +#else DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(argument_3)) +#endif DEF_RULE_NC(argument_3, and_ident(2), tok(DEL_EQUAL), rule(test)) DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if)) DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter)) diff --git a/py/lexer.c b/py/lexer.c index 10bb999af6b70..7d2a251d41d75 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -174,7 +174,8 @@ STATIC void indent_pop(mp_lexer_t *lex) { // this means if the start of two ops are the same then they are equal til the last char STATIC const char *const tok_enc = - "()[]{},:;~" // singles + "()[]{},;~" // singles + ":e=" // : := " >= >> >>= "*e=c*e=" // * *= ** **= @@ -194,8 +195,9 @@ STATIC const uint8_t tok_enc_kind[] = { MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE, MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE, MP_TOKEN_DEL_BRACE_OPEN, MP_TOKEN_DEL_BRACE_CLOSE, - MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_COLON, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_OP_TILDE, + MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_OP_TILDE, + MP_TOKEN_DEL_COLON, MP_TOKEN_OP_ASSIGN, MP_TOKEN_OP_LESS, MP_TOKEN_OP_LESS_EQUAL, MP_TOKEN_OP_DBL_LESS, MP_TOKEN_DEL_DBL_LESS_EQUAL, MP_TOKEN_OP_MORE, MP_TOKEN_OP_MORE_EQUAL, MP_TOKEN_OP_DBL_MORE, MP_TOKEN_DEL_DBL_MORE_EQUAL, MP_TOKEN_OP_STAR, MP_TOKEN_DEL_STAR_EQUAL, MP_TOKEN_OP_DBL_STAR, MP_TOKEN_DEL_DBL_STAR_EQUAL, diff --git a/py/lexer.h b/py/lexer.h index b9f97013a1bd9..91767a44bf991 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -96,6 +96,7 @@ typedef enum _mp_token_kind_t { MP_TOKEN_KW_WITH, MP_TOKEN_KW_YIELD, + MP_TOKEN_OP_ASSIGN, MP_TOKEN_OP_TILDE, // Order of these 6 matches corresponding mp_binary_op_t operator diff --git a/py/mpconfig.h b/py/mpconfig.h index 27df3f483a0e1..287b15aaef32f 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -835,6 +835,11 @@ typedef double mp_float_t; #define MICROPY_PY_ASYNC_AWAIT (1) #endif +// Support for assignment expressions with := (see PEP 572, Python 3.8+) +#ifndef MICROPY_PY_ASSIGN_EXPR +#define MICROPY_PY_ASSIGN_EXPR (1) +#endif + // Non-standard .pend_throw() method for generators, allowing for // Future-like behavior with respect to exception handling: an // exception set with .pend_throw() will activate on the next call diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 18986318a02b4..42a8228fb77e1 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -3,7 +3,7 @@ tok(4) [ 4] rule(22) (n=4) id(i) -[ 4] rule(44) (n=1) +[ 4] rule(45) (n=1) NULL [ 5] rule(8) (n=0) NULL From 2c5993c59e083d11ba8b85e82eeea9c5020ac553 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 21:42:48 +1000 Subject: [PATCH 077/352] ports: Disable MICROPY_PY_ASSIGN_EXPR in bare-arm and minimal ports. To keep these ports as minimal as possible. --- ports/bare-arm/mpconfigport.h | 1 + ports/minimal/mpconfigport.h | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/bare-arm/mpconfigport.h b/ports/bare-arm/mpconfigport.h index 4567676580324..7fd236bfba9e4 100644 --- a/ports/bare-arm/mpconfigport.h +++ b/ports/bare-arm/mpconfigport.h @@ -21,6 +21,7 @@ #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) #define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_ASSIGN_EXPR (0) #define MICROPY_PY_BUILTINS_BYTEARRAY (0) #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) #define MICROPY_PY_BUILTINS_MEMORYVIEW (0) diff --git a/ports/minimal/mpconfigport.h b/ports/minimal/mpconfigport.h index c3bdf66c10ae2..b34217f68019a 100644 --- a/ports/minimal/mpconfigport.h +++ b/ports/minimal/mpconfigport.h @@ -19,6 +19,7 @@ #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) #define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_ASSIGN_EXPR (0) #define MICROPY_PY_BUILTINS_BYTEARRAY (0) #define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) #define MICROPY_PY_BUILTINS_ENUMERATE (0) From e0fe8ea644b54406ca82cefdc73c98cc2e9cbe9f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 21:42:50 +1000 Subject: [PATCH 078/352] tests/basics: Add tests for assignment operator :=. --- tests/basics/assign_expr.py | 29 +++++++++++++++++++++ tests/basics/assign_expr.py.exp | 14 ++++++++++ tests/basics/assign_expr_syntaxerror.py | 16 ++++++++++++ tests/basics/assign_expr_syntaxerror.py.exp | 6 +++++ 4 files changed, 65 insertions(+) create mode 100644 tests/basics/assign_expr.py create mode 100644 tests/basics/assign_expr.py.exp create mode 100644 tests/basics/assign_expr_syntaxerror.py create mode 100644 tests/basics/assign_expr_syntaxerror.py.exp diff --git a/tests/basics/assign_expr.py b/tests/basics/assign_expr.py new file mode 100644 index 0000000000000..f243905dc2a63 --- /dev/null +++ b/tests/basics/assign_expr.py @@ -0,0 +1,29 @@ +(x := 4) +print(x) + +if x := 2: + print(True) +print(x) + +print(4, x := 5) +print(x) + +x = 1 +print(x, x := 5, x) +print(x) + + +def foo(): + print("any", any((hit := i) % 5 == 3 and (hit % 2) == 0 for i in range(10))) + return hit + + +hit = 123 +print(foo()) +print(hit) # shouldn't be changed by foo + +print("any", any((hit := i) % 5 == 3 and (hit % 2) == 0 for i in range(10))) +print(hit) # should be changed by above + +print([((m := k + 1), k * m) for k in range(4)]) +print(m) diff --git a/tests/basics/assign_expr.py.exp b/tests/basics/assign_expr.py.exp new file mode 100644 index 0000000000000..e38e1ae7a626c --- /dev/null +++ b/tests/basics/assign_expr.py.exp @@ -0,0 +1,14 @@ +4 +True +2 +4 5 +5 +1 5 5 +5 +any True +8 +123 +any True +8 +[(1, 0), (2, 2), (3, 6), (4, 12)] +4 diff --git a/tests/basics/assign_expr_syntaxerror.py b/tests/basics/assign_expr_syntaxerror.py new file mode 100644 index 0000000000000..11b3501292546 --- /dev/null +++ b/tests/basics/assign_expr_syntaxerror.py @@ -0,0 +1,16 @@ +# test SyntaxError with := operator + +def test(code): + try: + print(eval(code)) + except SyntaxError: + print('SyntaxError') + +test("x := 1") +test("((x, y) := 1)") + +# these are currently all allowed in MicroPython, but not in CPython +test("([i := i + 1 for i in range(4)])") +test("([i := -1 for i, j in [(1, 2)]])") +test("([[(i := j) for i in range(2)] for j in range(2)])") +test("([[(j := i) for i in range(2)] for j in range(2)])") diff --git a/tests/basics/assign_expr_syntaxerror.py.exp b/tests/basics/assign_expr_syntaxerror.py.exp new file mode 100644 index 0000000000000..2ba7d7df869eb --- /dev/null +++ b/tests/basics/assign_expr_syntaxerror.py.exp @@ -0,0 +1,6 @@ +SyntaxError +SyntaxError +[1, 2, 3, 4] +[-1] +[[0, 0], [1, 1]] +[[0, 1], [0, 1]] From a3c89cf907a3c2b7235ea86e9a229335212b9020 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 21:42:52 +1000 Subject: [PATCH 079/352] tests/cpydiff: Add CPy diff test for assignment expression behaviour. --- tests/cpydiff/syntax_assign_expr.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/cpydiff/syntax_assign_expr.py diff --git a/tests/cpydiff/syntax_assign_expr.py b/tests/cpydiff/syntax_assign_expr.py new file mode 100644 index 0000000000000..d4ed063b39ae7 --- /dev/null +++ b/tests/cpydiff/syntax_assign_expr.py @@ -0,0 +1,7 @@ +""" +categories: Syntax,Operators +description: MicroPython allows using := to assign to the variable of a comprehension, CPython raises a SyntaxError. +cause: MicroPython is optimised for code size and doesn't check this case. +workaround: Do not rely on this behaviour if writing CPython compatible code. +""" +print([i := -1 for i in range(4)]) From 131b0de70a47861c266d2ac7c26ae643f679d2f7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 22:48:46 +1000 Subject: [PATCH 080/352] py/grammar.h: Consolidate duplicate sub-rules for :test and =test. --- py/compile.c | 2 +- py/grammar.h | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/py/compile.c b/py/compile.c index da5c9190a3ef4..614e851a3d8ac 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2375,7 +2375,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar dblstar_args_node = pns_arg; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) { #if MICROPY_PY_ASSIGN_EXPR - if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_4)) { + if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_3)) { compile_namedexpr_helper(comp, pns_arg->nodes[0], ((mp_parse_node_struct_t *)pns_arg->nodes[1])->nodes[0]); n_positional++; } else diff --git a/py/grammar.h b/py/grammar.h index f5f1ff666d9e3..ff8e61828e862 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -30,6 +30,11 @@ // - zero_or_more is implemented using opt_rule around a one_or_more rule // - don't put opt_rule in arguments of or rule; instead, wrap the call to this or rule in opt_rule +// Generic sub-rules used by multiple rules below. + +DEF_RULE_NC(generic_colon_test, and_ident(2), tok(DEL_COLON), rule(test)) +DEF_RULE_NC(generic_equal_test, and_ident(2), tok(DEL_EQUAL), rule(test)) + // # Start symbols for the grammar: // # single_input is a single interactive statement; // # file_input is a module or sequence of commands read from an input file; @@ -71,19 +76,16 @@ DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test)) // note: typedargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA)) DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star)) -DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(typedargslist_colon), opt_rule(typedargslist_equal)) +DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test)) DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef)) -DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(typedargslist_colon)) -DEF_RULE_NC(typedargslist_colon, and_ident(2), tok(DEL_COLON), rule(test)) -DEF_RULE_NC(typedargslist_equal, and_ident(2), tok(DEL_EQUAL), rule(test)) -DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(typedargslist_colon)) +DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test)) +DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test)) // note: varargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA)) DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star)) -DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(varargslist_equal)) +DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test)) DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef)) DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME)) -DEF_RULE_NC(varargslist_equal, and_ident(2), tok(DEL_EQUAL), rule(test)) DEF_RULE_NC(vfpdef, and_ident(1), tok(NAME)) // stmt: compound_stmt | simple_stmt @@ -318,8 +320,7 @@ DEF_RULE(testlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA)) // TODO dictorsetmaker lets through more than is allowed DEF_RULE_NC(dictorsetmaker, and_ident(2), rule(dictorsetmaker_item), opt_rule(dictorsetmaker_tail)) #if MICROPY_PY_BUILTINS_SET -DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(dictorsetmaker_colon)) -DEF_RULE_NC(dictorsetmaker_colon, and_ident(2), tok(DEL_COLON), rule(test)) +DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(generic_colon_test)) #else DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and(3), rule(test), tok(DEL_COLON), rule(test)) #endif @@ -349,12 +350,11 @@ DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test)) DEF_RULE_NC(argument, and_ident(2), rule(test), opt_rule(argument_2)) #if MICROPY_PY_ASSIGN_EXPR -DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(argument_3), rule(argument_4)) -DEF_RULE_NC(argument_4, and(2), tok(OP_ASSIGN), rule(test)) +DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3)) +DEF_RULE_NC(argument_3, and(2), tok(OP_ASSIGN), rule(test)) #else -DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(argument_3)) +DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(generic_equal_test)) #endif -DEF_RULE_NC(argument_3, and_ident(2), tok(DEL_EQUAL), rule(test)) DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if)) DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter)) DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter)) From f2e267da68bf85f3f12fa77155e469352bdc6c09 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 22:49:25 +1000 Subject: [PATCH 081/352] py/compile: Implement PEP 526, syntax for variable annotations. This addition to the grammar was introduced in Python 3.6. It allows annotating the type of a varilable, like: x: int = 123 s: str The implementation in this commit is quite simple and just ignores the annotation (the int and str bits above). The reason to implement this is to allow Python 3.6+ code that uses this feature to compile under MicroPython without change, and for users to use type checkers. In the future viper could use this syntax as a way to give types to variables, which is currently done in a bit of an ad-hoc way, eg x = int(123). And this syntax could potentially be used in the inline assembler to define labels in an way that's easier to read. --- py/compile.c | 31 ++++++++++++++++++++++++------- py/grammar.h | 8 +++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/py/compile.c b/py/compile.c index 614e851a3d8ac..53108b7062b78 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1981,7 +1981,8 @@ STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { #endif STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { - if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) { + mp_parse_node_t pn_rhs = pns->nodes[1]; + if (MP_PARSE_NODE_IS_NULL(pn_rhs)) { if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) { // for REPL, evaluate then print the expression compile_load_id(comp, MP_QSTR___repl_print__); @@ -1999,10 +2000,26 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack } } - } else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) { - mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + } else if (MP_PARSE_NODE_IS_STRUCT(pn_rhs)) { + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn_rhs; int kind = MP_PARSE_NODE_STRUCT_KIND(pns1); - if (kind == PN_expr_stmt_augassign) { + if (kind == PN_annassign) { + // the annotation is in pns1->nodes[0] and is ignored + if (MP_PARSE_NODE_IS_NULL(pns1->nodes[1])) { + // an annotation of the form "x: y" + // inside a function this declares "x" as a local + if (comp->scope_cur->kind == SCOPE_FUNCTION) { + if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) { + qstr lhs = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + scope_find_or_add_id(comp->scope_cur, lhs, ID_INFO_KIND_LOCAL); + } + } + } else { + // an assigned annotation of the form "x: y = z" + pn_rhs = pns1->nodes[1]; + goto plain_assign; + } + } else if (kind == PN_expr_stmt_augassign) { c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign compile_node(comp, pns1->nodes[1]); // rhs assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0])); @@ -2027,10 +2044,10 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { } else { plain_assign: #if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN - if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr) + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_rhs, PN_testlist_star_expr) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) { mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0]; - pns1 = (mp_parse_node_struct_t *)pns->nodes[1]; + pns1 = (mp_parse_node_struct_t *)pn_rhs; uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0); // Can only optimise a tuple-to-tuple assignment when all of the following hold: // - equal number of items in LHS and RHS tuples @@ -2070,7 +2087,7 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { } #endif - compile_node(comp, pns->nodes[1]); // rhs + compile_node(comp, pn_rhs); // rhs c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store } } else { diff --git a/py/grammar.h b/py/grammar.h index ff8e61828e862..285fbded2fdae 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -98,20 +98,22 @@ DEF_RULE_NC(simple_stmt, and_ident(2), rule(simple_stmt_2), tok(NEWLINE)) DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), tok(DEL_SEMICOLON)) // small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt -// expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) +// expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) // testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] +// annassign: ':' test ['=' (yield_expr|testlist_star_expr)] // augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' -// # For normal assignments, additional restrictions enforced by the interpreter +// # For normal and annotated assignments, additional restrictions enforced by the interpreter DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt)) DEF_RULE(expr_stmt, c(expr_stmt), and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2)) -DEF_RULE_NC(expr_stmt_2, or(2), rule(expr_stmt_augassign), rule(expr_stmt_assign_list)) +DEF_RULE_NC(expr_stmt_2, or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list)) DEF_RULE_NC(expr_stmt_augassign, and_ident(2), rule(augassign), rule(expr_stmt_6)) DEF_RULE_NC(expr_stmt_assign_list, one_or_more, rule(expr_stmt_assign)) DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6)) DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr)) DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA)) DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test)) +DEF_RULE_NC(annassign, and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign)) DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) // del_stmt: 'del' exprlist From a51eef4471e01ee60d53ee184bc4feabf6ebd855 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 22:49:38 +1000 Subject: [PATCH 082/352] tests/basics: Add tests for variable annotations. --- tests/basics/annotate_var.py | 25 +++++++++++++++++++++++++ tests/basics/annotate_var.py.exp | 5 +++++ tests/run-tests | 1 + 3 files changed, 31 insertions(+) create mode 100644 tests/basics/annotate_var.py create mode 100644 tests/basics/annotate_var.py.exp diff --git a/tests/basics/annotate_var.py b/tests/basics/annotate_var.py new file mode 100644 index 0000000000000..3f767e4a73505 --- /dev/null +++ b/tests/basics/annotate_var.py @@ -0,0 +1,25 @@ +# test PEP 526, varible annotations + +x: int +print("x" in globals()) + +x: int = 1 +print(x) + +t: tuple = 1, 2 +print(t) + +# a pure annotation in a function makes that variable local +def f(): + x: int + try: + print(x) + except NameError: + print("NameError") +f() + +# here, "x" should remain a global +def f(): + x.y: int + print(x) +f() diff --git a/tests/basics/annotate_var.py.exp b/tests/basics/annotate_var.py.exp new file mode 100644 index 0000000000000..9b6536e966b0e --- /dev/null +++ b/tests/basics/annotate_var.py.exp @@ -0,0 +1,5 @@ +False +1 +(1, 2) +NameError +1 diff --git a/tests/run-tests b/tests/run-tests index 74e2f71ac6480..f9e4de4b34a20 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -414,6 +414,7 @@ def run_tests(pyb, tests, args, base_path="."): skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from_close generator_name'.split()}) # require raise_varargs, generator name skip_tests.update({'basics/async_%s.py' % t for t in 'with with2 with_break with_return'.split()}) # require async_with skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs + skip_tests.add('basics/annotate_var.py') # requires checking for unbound local skip_tests.add('basics/del_deref.py') # requires checking for unbound local skip_tests.add('basics/del_local.py') # requires checking for unbound local skip_tests.add('basics/exception_chain.py') # raise from is not supported From 1678f417445703750e187b1b8a83c8c3e0e3796f Mon Sep 17 00:00:00 2001 From: Jon Rob Date: Mon, 8 Jun 2020 06:29:43 +0000 Subject: [PATCH 083/352] esp32/esp32_rmt: Extend RMT to support carrier feature. The ESP32 RMT peripheral has hardware support for a carrier frequency, and this commit exposes it to Python with the keyword arguments carrier_freq and carrier_duty_percent in the constructor. Example usage: r = esp32.RMT(0, pin=Pin(2), clock_div=80, carrier_freq=38000, carrier_duty_percent=50) --- docs/library/esp32.rst | 17 ++++++++++++++--- ports/esp32/esp32_rmt.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 33b779a214336..715afdddeb169 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -151,6 +151,11 @@ used to transmit or receive many other types of digital signals:: r = esp32.RMT(0, pin=Pin(18), clock_div=8) r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8) + + # To use carrier frequency + r = esp32.RMT(0, pin=Pin(18), clock_div=8, carrier_freq=38000) + r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, carrier_freq=38000, carrier_duty_percent=50) + # The channel resolution is 100ns (1/(source_freq/clock_div)). r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns @@ -164,6 +169,9 @@ define the pulses. multiplying the resolution by a 15-bit (0-32,768) number. There are eight channels (0-7) and each can have a different clock divider. +To enable the carrier frequency feature of the esp32 hardware, specify the +``carrier_freq`` as something like 38000, a typical IR carrier frequency. + So, in the example above, the 80MHz clock is divided by 8. Thus the resolution is (1/(80Mhz/8)) 100ns. Since the ``start`` level is 0 and toggles with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns, @@ -174,17 +182,20 @@ For more details see Espressif's `ESP-IDF RMT documentation. .. Warning:: The current MicroPython RMT implementation lacks some features, most notably - receiving pulses and carrier transmit. RMT should be considered a + receiving pulses. RMT should be considered a *beta feature* and the interface may change in the future. -.. class:: RMT(channel, \*, pin=None, clock_div=8) +.. class:: RMT(channel, \*, pin=None, clock_div=8, carrier_freq=0, carrier_duty_percent=50) This class provides access to one of the eight RMT channels. *channel* is required and identifies which RMT channel (0-7) will be configured. *pin*, also required, configures which Pin is bound to the RMT channel. *clock_div* is an 8-bit clock divider that divides the source clock (80MHz) to the RMT - channel allowing the resolution to be specified. + channel allowing the resolution to be specified. *carrier_freq* is used to + enable the carrier feature and specify its frequency, default value is ``0`` + (not enabled). To enable, specify a positive integer. *carrier_duty_percent* + defaults to 50. .. method:: RMT.source_freq() diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 25b7b3808981c..846c80d478f0b 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -52,6 +52,8 @@ typedef struct _esp32_rmt_obj_t { uint8_t channel_id; gpio_num_t pin; uint8_t clock_div; + uint16_t carrier_duty_percent; + uint32_t carrier_freq; mp_uint_t num_items; rmt_item32_t *items; } esp32_rmt_obj_t; @@ -61,6 +63,8 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_clock_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, // 100ns resolution + { MP_QSTR_carrier_duty_percent, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 50} }, + { MP_QSTR_carrier_freq, MP_ARG_KW_ONLY | 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); @@ -68,6 +72,16 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj); mp_uint_t clock_div = args[2].u_int; + bool carrier_en = false; + mp_uint_t carrier_duty_percent = 0; + mp_uint_t carrier_freq = 0; + + if (args[4].u_int > 0) { + carrier_en = true; + carrier_duty_percent = args[3].u_int; + carrier_freq = args[4].u_int; + } + if (clock_div < 1 || clock_div > 255) { mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255")); } @@ -77,6 +91,8 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz self->channel_id = channel_id; self->pin = pin_id; self->clock_div = clock_div; + self->carrier_duty_percent = carrier_duty_percent; + self->carrier_freq = carrier_freq; rmt_config_t config; config.rmt_mode = RMT_MODE_TX; @@ -85,11 +101,11 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz config.mem_block_num = 1; config.tx_config.loop_en = 0; - config.tx_config.carrier_en = 0; + config.tx_config.carrier_en = carrier_en; config.tx_config.idle_output_en = 1; config.tx_config.idle_level = 0; - config.tx_config.carrier_duty_percent = 0; - config.tx_config.carrier_freq_hz = 0; + config.tx_config.carrier_duty_percent = self->carrier_duty_percent; + config.tx_config.carrier_freq_hz = self->carrier_freq; config.tx_config.carrier_level = 1; config.clk_div = self->clock_div; @@ -103,8 +119,14 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz STATIC void esp32_rmt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->pin != -1) { - mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u)", + mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u", self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div); + if (self->carrier_freq > 0) { + mp_printf(print, ", carrier_freq=%u, carrier_duty_percent=%u)", + self->carrier_freq, self->carrier_duty_percent); + } else { + mp_printf(print, ")"); + } } else { mp_printf(print, "RMT()"); } From da99e0f97911864d1803b804249b38344385de11 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 12:23:29 +1000 Subject: [PATCH 084/352] stm32/factoryreset: Provide empty create-FS function when FAT disabled. Signed-off-by: Damien George --- ports/stm32/factoryreset.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ports/stm32/factoryreset.c b/ports/stm32/factoryreset.c index 04591101e87df..725ecd12a871c 100644 --- a/ports/stm32/factoryreset.c +++ b/ports/stm32/factoryreset.c @@ -34,6 +34,8 @@ #if MICROPY_HW_ENABLE_STORAGE +#if MICROPY_VFS_FAT + static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n" "# can run arbitrary Python, but best to keep it minimal\r\n" @@ -128,4 +130,13 @@ MP_WEAK int factory_reset_create_filesystem(void) { return 0; // success } +#else + +// If FAT is not enabled then it's up to the board to create a fresh filesystem. +MP_WEAK int factory_reset_create_filesystem(void) { + return 0; // success +} + +#endif + #endif // MICROPY_HW_ENABLE_STORAGE From 289be6b352e30e97363c1db98a582eb84c9dd12e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 12:25:41 +1000 Subject: [PATCH 085/352] stm32/usb: Add support for 2xVCP on L0, L432 and WB MCUs. There are a maximum of 8 USB endpoints and each has 2 buffer slots (in/out). This commit add support for up to 8 endpoints and adds FIFO configuration for USB profiles with 2xVCP on MCUs that have device-only USB peripherals. Tested on NUCLEO_WB55 in 2xVCP, 2xVCP+MSC and 2xVCP+MSC+HID mode. Signed-off-by: Damien George --- ports/stm32/usb.c | 25 +++++++++++++++++++++++-- ports/stm32/usbd_conf.h | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index cd5238172188e..657d4cb6e7402 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -93,8 +93,29 @@ pyb_usb_storage_medium_t pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_NONE; // Units of FIFO size arrays below are 4x 16-bit words = 8 bytes // There are 512x 16-bit words it total to use here (when using PCD_SNG_BUF) -// EP0(out), EP0(in), MSC/HID(out), MSC/HID(in), unused, CDC_CMD(in), CDC_DATA(out), CDC_DATA(in) -STATIC const uint8_t usbd_fifo_size_cdc1[] = {16, 16, 16, 16, 0, 16, 16, 16}; +STATIC const uint8_t usbd_fifo_size_cdc1[USBD_PMA_NUM_FIFO] = { + 16, 16, 16, 16, // EP0(out), EP0(in), MSC/HID(out), MSC/HID(in) + 0, 16, 16, 16, // unused, CDC_CMD(in), CDC_DATA(out), CDC_DATA(in) + 0, 0, 0, 0, 0, 0, 0, 0, // 8x unused +}; + +#if MICROPY_HW_USB_CDC_NUM >= 2 +STATIC const uint8_t usbd_fifo_size_cdc2[USBD_PMA_NUM_FIFO] = { + 8, 8, 16, 16, // EP0(out), EP0(in), MSC/HID(out), MSC/HID(in) + 0, 8, 12, 12, // unused, CDC_CMD(in), CDC_DATA(out), CDC_DATA(in) + 0, 8, 12, 12, // unused, CDC2_CMD(in), CDC2_DATA(out), CDC2_DATA(in) + 0, 0, 0, 0, // 4x unused +}; + +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, CDC2_CMD/HID, CDC2_DATA, HID +STATIC const uint8_t usbd_fifo_size_cdc2_msc_hid[USBD_PMA_NUM_FIFO] = { + 8, 8, 16, 16, // EP0(out), EP0(in), MSC/HID(out), MSC/HID(in) + 0, 8, 8, 8, // unused, CDC_CMD(in), CDC_DATA(out), CDC_DATA(in) + 0, 8, 8, 8, // unused, CDC2_CMD(in), CDC2_DATA(out), CDC2_DATA(in) + 8, 8, // HID(out), HID(in) + 0, 0, // 2x unused +}; +#endif #else diff --git a/ports/stm32/usbd_conf.h b/ports/stm32/usbd_conf.h index 5237ba3a96e72..83805a6e4d801 100644 --- a/ports/stm32/usbd_conf.h +++ b/ports/stm32/usbd_conf.h @@ -51,7 +51,7 @@ // For MCUs with a device-only USB peripheral #define USBD_PMA_RESERVE (64) -#define USBD_PMA_NUM_FIFO (8) +#define USBD_PMA_NUM_FIFO (16) // Maximum 8 endpoints, 2 FIFOs each // For MCUs with multiple OTG USB peripherals #define USBD_FS_NUM_TX_FIFO (6) From 4b5dd012e0fe615001bbd1edf2f85ea30371a1d6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 16 Jun 2020 12:29:02 +1000 Subject: [PATCH 086/352] stm32/rfcore: Leave txpower level as default when initialising rfcore. And provide a convenient API function to change it (currently unused). Fixes issue #5985. Signed-off-by: Damien George --- ports/stm32/rfcore.c | 7 ++++++- ports/stm32/rfcore.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 602ef974b262b..54b3393435e1e 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -416,8 +416,13 @@ void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env) SWAP_UINT8(buf[3], buf[6]); SWAP_UINT8(buf[4], buf[5]); tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_WRITE_CONFIG), 8, buf); // set BDADDR - tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_SET_TX_POWER), 2, (const uint8_t *)"\x00\x06"); // 0 dBm } } +// "level" is 0x00-0x1f, ranging from -40 dBm to +6 dBm (not linear). +void rfcore_ble_set_txpower(uint8_t level) { + uint8_t buf[2] = { 0x00, level }; + tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_SET_TX_POWER), 2, buf); +} + #endif // defined(STM32WB) diff --git a/ports/stm32/rfcore.h b/ports/stm32/rfcore.h index 138c438f12eae..fbe111e1ebdcd 100644 --- a/ports/stm32/rfcore.h +++ b/ports/stm32/rfcore.h @@ -33,5 +33,6 @@ void rfcore_init(void); void rfcore_ble_init(void); void rfcore_ble_hci_cmd(size_t len, const uint8_t *src); void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env); +void rfcore_ble_set_txpower(uint8_t level); #endif // MICROPY_INCLUDED_STM32_RFCORE_H From 8d71cc2e7da7a77aeca66bc8fac0bb0e28690344 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 11 Jun 2020 20:40:04 +0200 Subject: [PATCH 087/352] nrf/bluetooth: Use MP_ERROR_TEXT for all error messages. This follows up on commit def76fe4d9bbc2c342594dc05861b24d7165d274. Fixes issue #6152. --- ports/nrf/drivers/bluetooth/ble_drv.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 7619dc0399455..47f7f98885a91 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -607,12 +607,12 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { #if (BLUETOOTH_SD == 110) if ((err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not apply advertisment data. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not apply advertisment data. status: 0x" HEX2_FMT), (uint16_t)err_code); } #else if ((err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not apply advertisment data. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not apply advertisment data. status: 0x" HEX2_FMT), (uint16_t)err_code); } #endif BLE_DRIVER_LOG("Set Adv data size: " UINT_FMT "\n", byte_pos); @@ -627,7 +627,7 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { #endif if (err_code != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not start advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not start advertisment. status: 0x" HEX2_FMT), (uint16_t)err_code); } m_adv_in_progress = true; @@ -642,12 +642,12 @@ void ble_drv_advertise_stop(void) { #if (BLUETOOTH_SD == 110) if ((err_code = sd_ble_gap_adv_stop()) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not stop advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not stop advertisment. status: 0x" HEX2_FMT), (uint16_t)err_code); } #else if ((err_code = sd_ble_gap_adv_stop(m_adv_handle)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not stop advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not stop advertisment. status: 0x" HEX2_FMT), (uint16_t)err_code); } #endif } @@ -667,7 +667,7 @@ void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, ui &gatts_value); if (err_code != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not read attribute value. status: 0x" HEX2_FMT), (uint16_t)err_code); } } @@ -684,7 +684,7 @@ void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, u if (err_code != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not write attribute value. status: 0x" HEX2_FMT), (uint16_t)err_code); } } @@ -708,7 +708,7 @@ void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint32_t err_code; if ((err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not notify attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not notify attribute value. status: 0x" HEX2_FMT), (uint16_t)err_code); } m_tx_in_progress++; BLE_DRIVER_LOG("Queued TX, m_tx_in_progress: %u\n", m_tx_in_progress); @@ -747,7 +747,7 @@ void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, bl 0); if (err_code != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not read attribute value. status: 0x" HEX2_FMT), (uint16_t)err_code); } while (gattc_char_data_handle != NULL) { @@ -777,7 +777,7 @@ void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, u if (err_code != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not write attribute value. status: 0x" HEX2_FMT), (uint16_t)err_code); } while (m_write_done != true) { @@ -808,7 +808,7 @@ void ble_drv_scan_start(bool cont) { } if ((err_code = sd_ble_gap_scan_start(p_scan_params, &scan_buffer)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not start scanning. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not start scanning. status: 0x" HEX2_FMT), (uint16_t)err_code); } } @@ -854,7 +854,7 @@ void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type) { &conn_params, conn_tag)) != 0) { mp_raise_msg_varg(&mp_type_OSError, - "Can not connect. status: 0x" HEX2_FMT, (uint16_t)err_code); + MP_ERROR_TEXT("Can not connect. status: 0x" HEX2_FMT), (uint16_t)err_code); } } From cba3e25cb33007e4c0b1a0c476ecce7166fb089f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Jun 2020 11:39:52 +1000 Subject: [PATCH 088/352] travis: Change nrf pca10056 board to build with soft-device enabled. To test building with SD which enables a lot of additional code. Signed-off-by: Damien George --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 414fe2b59d108..1cb5575c73636 100644 --- a/.travis.yml +++ b/.travis.yml @@ -292,10 +292,11 @@ jobs: - sudo apt-get install libnewlib-arm-none-eabi - arm-none-eabi-gcc --version script: + - ports/nrf/drivers/bluetooth/download_ble_stack.sh s132_nrf52_6_1_1 - make ${MAKEOPTS} -C ports/nrf submodules - make ${MAKEOPTS} -C ports/nrf BOARD=pca10040 - make ${MAKEOPTS} -C ports/nrf BOARD=microbit - - make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 + - make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 SD=s132 # bare-arm and minimal ports, with size-diff check - stage: test From c521c178e9d113a27c50f6e0265c75d87fd7c99c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Jun 2020 23:48:33 +1000 Subject: [PATCH 089/352] github: Add FUNDING.yml file pointing to micropython GitHub sponsorship. Signed-off-by: Damien George --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000..15186fd5c1916 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: micropython From ce02d5e3486ca0665e175e9690be910a06392edf Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 10:48:24 +1000 Subject: [PATCH 090/352] stm32/boards/NUCLEO_WB55: Add more CPU pins and aliases to SW1/2/3. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_WB55/pins.csv | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ports/stm32/boards/NUCLEO_WB55/pins.csv b/ports/stm32/boards/NUCLEO_WB55/pins.csv index 49fdab0c28c58..d7e0babf36d25 100644 --- a/ports/stm32/boards/NUCLEO_WB55/pins.csv +++ b/ports/stm32/boards/NUCLEO_WB55/pins.csv @@ -6,6 +6,14 @@ ,PA5 ,PA6 ,PA7 +,PA8 +,PA9 +,PA10 +,PA11 +,PA12 +,PA13 +,PA14 +,PA15 ,PB0 ,PB1 ,PB2 @@ -26,7 +34,20 @@ ,PC1 ,PC2 ,PC3 +,PC4 +,PC5 +,PC6 +,PC10 +,PC11 +,PC12 +,PC13 +,PD0 +,PD1 +,PE4 SW,PC4 +SW1,PC4 +SW2,PD0 +SW3,PD1 LED_GREEN,PB0 LED_RED,PB1 LED_BLUE,PB5 From 026fda605e03113d6e753290d65fed774418bc53 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 22:20:20 +1000 Subject: [PATCH 091/352] tools/codeformat.py: Include extmod/{btstack,nimble} in code formatting. Signed-off-by: Damien George --- extmod/btstack/modbluetooth_btstack.c | 2 +- extmod/nimble/modbluetooth_nimble.c | 2 +- tools/codeformat.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 31b2825758434..206bc98f9ff71 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -35,7 +35,7 @@ #include "lib/btstack/src/btstack.h" -#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__) +#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) #ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY BTSTACK" diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 035ec3d26fc6d..4e0ca88efa468 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -45,7 +45,7 @@ #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE" #endif -#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__) +#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV diff --git a/tools/codeformat.py b/tools/codeformat.py index 364037d2fac86..653529460c079 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -36,6 +36,8 @@ PATHS = [ # C "extmod/*.[ch]", + "extmod/btstack/*.[ch]", + "extmod/nimble/*.[ch]", "lib/netutils/*.[ch]", "lib/timeutils/*.[ch]", "lib/utils/*.[ch]", From 77ed6f69ac35c1663a5633a8ee1d8a2446542204 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 16 Jun 2020 09:34:51 -0500 Subject: [PATCH 092/352] tools/uncrustify: Enable more opts to remove space between func and '('. With only `sp_func_proto_paren = remove` set there are some cases where uncrustify misses removing a space between the function name and the opening '('. This sets all of the related options to `force` as well. --- extmod/vfs_fat_diskio.c | 6 +++--- lib/utils/printf.c | 2 +- ports/cc3200/main.c | 14 +++++++------- ports/cc3200/mptask.c | 18 +++++++++--------- ports/cc3200/serverstask.c | 30 +++++++++++++++--------------- tools/uncrustify.cfg | 20 ++++++++++---------- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index ae1bef57d5e07..1bcd471f2162e 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -52,7 +52,7 @@ STATIC fs_user_mount_t *disk_get_device(void *bdev) { /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ -DRESULT disk_read ( +DRESULT disk_read( bdev_t pdrv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ @@ -72,7 +72,7 @@ DRESULT disk_read ( /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ -DRESULT disk_write ( +DRESULT disk_write( bdev_t pdrv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ @@ -98,7 +98,7 @@ DRESULT disk_write ( /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ -DRESULT disk_ioctl ( +DRESULT disk_ioctl( bdev_t pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ diff --git a/lib/utils/printf.c b/lib/utils/printf.c index 6266ab66d3cdc..e8db2b999c123 100644 --- a/lib/utils/printf.c +++ b/lib/utils/printf.c @@ -104,7 +104,7 @@ STATIC void strn_print_strn(void *data, const char *str, size_t len) { // when linkings against it statically. // GCC 9 gives a warning about missing attributes so it's excluded until // uClibc+GCC9 support is needed. -int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias ("vsnprintf"))); +int __GI_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) __attribute__((weak, alias("vsnprintf"))); #endif int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { diff --git a/ports/cc3200/main.c b/ports/cc3200/main.c index b9f67194b6fc0..bdd73e7bf9ba7 100644 --- a/ports/cc3200/main.c +++ b/ports/cc3200/main.c @@ -51,8 +51,8 @@ ******************************************************************************/ // This is the static memory (TCB and stack) for the idle task -static StaticTask_t xIdleTaskTCB __attribute__ ((section (".rtos_heap"))); -static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE] __attribute__ ((section (".rtos_heap"))) __attribute__((aligned (8))); +static StaticTask_t xIdleTaskTCB __attribute__ ((section(".rtos_heap"))); +static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE] __attribute__ ((section(".rtos_heap"))) __attribute__((aligned(8))); /****************************************************************************** DECLARE PUBLIC DATA @@ -62,18 +62,18 @@ OsiTaskHandle mpTaskHandle; #endif // This is the FreeRTOS heap, defined here so we can put it in a special segment -uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ((section (".rtos_heap"))) __attribute__((aligned (8))); +uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ((section(".rtos_heap"))) __attribute__((aligned(8))); // This is the static memory (TCB and stack) for the main MicroPython task -StaticTask_t mpTaskTCB __attribute__ ((section (".rtos_heap"))); -StackType_t mpTaskStack[MICROPY_TASK_STACK_LEN] __attribute__ ((section (".rtos_heap"))) __attribute__((aligned (8))); +StaticTask_t mpTaskTCB __attribute__ ((section(".rtos_heap"))); +StackType_t mpTaskStack[MICROPY_TASK_STACK_LEN] __attribute__ ((section(".rtos_heap"))) __attribute__((aligned(8))); /****************************************************************************** DEFINE PUBLIC FUNCTIONS ******************************************************************************/ -__attribute__ ((section (".boot"))) -int main (void) { +__attribute__ ((section(".boot"))) +int main(void) { // Initialize the clocks and the interrupt system HAL_SystemInit(); diff --git a/ports/cc3200/mptask.c b/ports/cc3200/mptask.c index f06a502776087..71b650ff858c8 100644 --- a/ports/cc3200/mptask.c +++ b/ports/cc3200/mptask.c @@ -113,7 +113,7 @@ static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n" uintptr_t cortex_m3_get_sp(void); -void TASK_MicroPython (void *pvParameters) { +void TASK_MicroPython(void *pvParameters) { // get the top of the stack to initialize the garbage collector uint32_t sp = cortex_m3_get_sp(); @@ -262,16 +262,16 @@ void TASK_MicroPython (void *pvParameters) { /****************************************************************************** DEFINE PRIVATE FUNCTIONS ******************************************************************************/ -__attribute__ ((section (".boot"))) -STATIC void mptask_pre_init (void) { +__attribute__ ((section(".boot"))) +STATIC void mptask_pre_init(void) { // this one only makes sense after a poweron reset pyb_rtc_pre_init(); // Create the simple link spawn task - ASSERT (OSI_OK == VStartSimpleLinkSpawnTask(SIMPLELINK_SPAWN_TASK_PRIORITY)); + ASSERT(OSI_OK == VStartSimpleLinkSpawnTask(SIMPLELINK_SPAWN_TASK_PRIORITY)); // Allocate memory for the flash file system - ASSERT ((sflash_vfs_fat = mem_Malloc(sizeof(*sflash_vfs_fat))) != NULL); + ASSERT((sflash_vfs_fat = mem_Malloc(sizeof(*sflash_vfs_fat))) != NULL); // this one allocates memory for the nvic vault pyb_sleep_pre_init(); @@ -295,7 +295,7 @@ STATIC void mptask_pre_init (void) { ASSERT(svTaskHandle != NULL); } -STATIC void mptask_init_sflash_filesystem (void) { +STATIC void mptask_init_sflash_filesystem(void) { FILINFO fno; // Initialise the local flash filesystem. @@ -378,16 +378,16 @@ STATIC void mptask_init_sflash_filesystem (void) { } } -STATIC void mptask_enter_ap_mode (void) { +STATIC void mptask_enter_ap_mode(void) { // append the mac only if it's not the first boot bool add_mac = !PRCMGetSpecialBit(PRCM_FIRST_BOOT_BIT); // enable simplelink in ap mode (use the MAC address to make the ssid unique) - wlan_sl_init (ROLE_AP, MICROPY_PORT_WLAN_AP_SSID, strlen(MICROPY_PORT_WLAN_AP_SSID), + wlan_sl_init(ROLE_AP, MICROPY_PORT_WLAN_AP_SSID, strlen(MICROPY_PORT_WLAN_AP_SSID), MICROPY_PORT_WLAN_AP_SECURITY, MICROPY_PORT_WLAN_AP_KEY, strlen(MICROPY_PORT_WLAN_AP_KEY), MICROPY_PORT_WLAN_AP_CHANNEL, ANTENNA_TYPE_INTERNAL, add_mac); } -STATIC void mptask_create_main_py (void) { +STATIC void mptask_create_main_py(void) { // create empty main.py FIL fp; f_open(&sflash_vfs_fat->fatfs, &fp, "/main.py", FA_WRITE | FA_CREATE_ALWAYS); diff --git a/ports/cc3200/serverstask.c b/ports/cc3200/serverstask.c index 517a7a2281bdb..03eed8eeb0cf4 100644 --- a/ports/cc3200/serverstask.c +++ b/ports/cc3200/serverstask.c @@ -68,8 +68,8 @@ static volatile bool sleep_sockets = false; ******************************************************************************/ // This is the static memory (TCB and stack) for the servers task -StaticTask_t svTaskTCB __attribute__ ((section (".rtos_heap"))); -StackType_t svTaskStack[SERVERS_STACK_LEN] __attribute__ ((section (".rtos_heap"))) __attribute__((aligned (8))); +StaticTask_t svTaskTCB __attribute__ ((section(".rtos_heap"))); +StackType_t svTaskStack[SERVERS_STACK_LEN] __attribute__ ((section(".rtos_heap"))) __attribute__((aligned(8))); char servers_user[SERVERS_USER_PASS_LEN_MAX + 1]; char servers_pass[SERVERS_USER_PASS_LEN_MAX + 1]; @@ -77,12 +77,12 @@ char servers_pass[SERVERS_USER_PASS_LEN_MAX + 1]; /****************************************************************************** DECLARE PUBLIC FUNCTIONS ******************************************************************************/ -void TASK_Servers (void *pvParameters) { +void TASK_Servers(void *pvParameters) { bool cycle = false; - strcpy (servers_user, SERVERS_DEF_USER); - strcpy (servers_pass, SERVERS_DEF_PASS); + strcpy(servers_user, SERVERS_DEF_USER); + strcpy(servers_pass, SERVERS_DEF_PASS); telnet_init(); ftp_init(); @@ -143,12 +143,12 @@ void TASK_Servers (void *pvParameters) { } } -void servers_start (void) { +void servers_start(void) { servers_data.do_enable = true; mp_hal_delay_ms(SERVERS_CYCLE_TIME_MS * 3); } -void servers_stop (void) { +void servers_stop(void) { servers_data.do_disable = true; do { mp_hal_delay_ms(SERVERS_CYCLE_TIME_MS); @@ -156,24 +156,24 @@ void servers_stop (void) { mp_hal_delay_ms(SERVERS_CYCLE_TIME_MS * 3); } -void servers_reset (void) { +void servers_reset(void) { servers_data.do_reset = true; } -void servers_wlan_cycle_power (void) { +void servers_wlan_cycle_power(void) { servers_data.do_wlan_cycle_power = true; } -bool servers_are_enabled (void) { +bool servers_are_enabled(void) { return servers_data.enabled; } -void server_sleep_sockets (void) { +void server_sleep_sockets(void) { sleep_sockets = true; mp_hal_delay_ms(SERVERS_CYCLE_TIME_MS + 1); } -void servers_close_socket (int16_t *sd) { +void servers_close_socket(int16_t *sd) { if (*sd > 0) { modusocket_socket_delete(*sd); sl_Close(*sd); @@ -181,7 +181,7 @@ void servers_close_socket (int16_t *sd) { } } -void servers_set_login (char *user, char *pass) { +void servers_set_login(char *user, char *pass) { if (strlen(user) > SERVERS_USER_PASS_LEN_MAX || strlen(pass) > SERVERS_USER_PASS_LEN_MAX) { mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s) value")); } @@ -189,7 +189,7 @@ void servers_set_login (char *user, char *pass) { memcpy(servers_pass, pass, SERVERS_USER_PASS_LEN_MAX); } -void servers_set_timeout (uint32_t timeout) { +void servers_set_timeout(uint32_t timeout) { if (timeout < SERVERS_MIN_TIMEOUT_MS) { // timeout is too low mp_raise_ValueError(MP_ERROR_TEXT("invalid argument(s) value")); @@ -197,7 +197,7 @@ void servers_set_timeout (uint32_t timeout) { servers_data.timeout = timeout; } -uint32_t servers_get_timeout (void) { +uint32_t servers_get_timeout(void) { return servers_data.timeout; } diff --git a/tools/uncrustify.cfg b/tools/uncrustify.cfg index 96b471742cc51..80542b903e79e 100644 --- a/tools/uncrustify.cfg +++ b/tools/uncrustify.cfg @@ -512,35 +512,35 @@ sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function declaration # without parameters. -sp_func_proto_paren_empty = ignore # ignore/add/remove/force +sp_func_proto_paren_empty = remove # ignore/add/remove/force # Add or remove space between function name and '(' with a typedef specifier. -sp_func_type_paren = ignore # ignore/add/remove/force +sp_func_type_paren = remove # ignore/add/remove/force # Add or remove space between alias name and '(' of a non-pointer function type typedef. -sp_func_def_paren = ignore # ignore/add/remove/force +sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function definition # without parameters. -sp_func_def_paren_empty = ignore # ignore/add/remove/force +sp_func_def_paren_empty = remove # ignore/add/remove/force # Add or remove space inside empty function '()'. # Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_fparens = ignore # ignore/add/remove/force +sp_inside_fparens = remove # ignore/add/remove/force # Add or remove space inside function '(' and ')'. sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space inside the first parentheses in a function type, as in # 'void (*x)(...)'. -sp_inside_tparen = ignore # ignore/add/remove/force +sp_inside_tparen = remove # ignore/add/remove/force # Add or remove space between the ')' and '(' in a function type, as in # 'void (*x)(...)'. -sp_after_tparen_close = ignore # ignore/add/remove/force +sp_after_tparen_close = remove # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. -sp_square_fparen = ignore # ignore/add/remove/force +sp_square_fparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{' of function. sp_fparen_brace = force # ignore/add/remove/force @@ -555,11 +555,11 @@ sp_fparen_brace_initializer = ignore # ignore/add/remove/force sp_fparen_dbrace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls. -sp_func_call_paren = ignore # ignore/add/remove/force +sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without # parameters. If set to ignore (the default), sp_func_call_paren is used. -sp_func_call_paren_empty = ignore # ignore/add/remove/force +sp_func_call_paren_empty = remove # ignore/add/remove/force # Add or remove space between the user function name and '(' on function # calls. You need to set a keyword to be a user function in the config file, From 3a9d948032e27f690e1fb09084c36bd47b1a75a0 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Thu, 18 Jun 2020 12:44:54 +0100 Subject: [PATCH 093/352] esp32/esp32_rmt: Call rmt_driver_install before rmt_config. Otherwise the RMT will repeat pulses when using loop(True). This repeating is due to a bug in the IDF which will be fixed in an upcoming release, but for now the accepted workaround is to swap these calls, which should still work in the fixed version of the IDF. Fixes issue #6167. --- ports/esp32/esp32_rmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 846c80d478f0b..2ed4c9f6968c4 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -110,8 +110,8 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz config.clk_div = self->clock_div; - check_esp_err(rmt_config(&config)); check_esp_err(rmt_driver_install(config.channel, 0, 0)); + check_esp_err(rmt_config(&config)); return MP_OBJ_FROM_PTR(self); } From 6164c7e666fdd0aba232191768a66ab4434a7222 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 10:58:51 +1000 Subject: [PATCH 094/352] py/misc.h: Add missing semi-colon in mp_float_union_t for big-endian. Fixes issue #6161. Signed-off-by: Damien George --- py/misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/misc.h b/py/misc.h index c7060ddf9d223..2dd20365c934c 100644 --- a/py/misc.h +++ b/py/misc.h @@ -247,7 +247,7 @@ typedef union _mp_float_union_t { } p; #else struct { - mp_float_uint_t sgn : 1 + mp_float_uint_t sgn : 1; mp_float_uint_t exp : MP_FLOAT_EXP_BITS; mp_float_uint_t frc : MP_FLOAT_FRAC_BITS; } p; From f1ba2c9d88078df8758ee9072b02d48e70c0b305 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 10:59:29 +1000 Subject: [PATCH 095/352] qemu-arm/Makefile: Add CFLAGS_EXTRA to CFLAGS. Signed-off-by: Damien George --- ports/qemu-arm/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index e7b6523f88c2f..aebc5afaeb773 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -42,6 +42,7 @@ INC += -I$(BUILD) CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \ -ffunction-sections -fdata-sections +CFLAGS += $(CFLAGS_EXTRA) # Debugging/Optimization ifeq ($(DEBUG), 1) From ac15be9365808919dc18c6fbfee55e074671c2aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 10:59:53 +1000 Subject: [PATCH 096/352] travis: Build qemu-arm with MP_ENDIANNESS_BIG=1 to test bigendian build. Eventually it would be good to run the full test suite on a big-endian system, but for now this will help to catch build errors with the big-endian configuration. Signed-off-by: Damien George --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1cb5575c73636..09da9598e2018 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,6 +90,8 @@ jobs: - qemu-system-arm --version script: - make ${MAKEOPTS} -C mpy-cross + - make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 + - make ${MAKEOPTS} -C ports/qemu-arm clean - make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test after_failure: - grep --text "FAIL" ports/qemu-arm/build/console.out From 5f3c2f1fa8e7a41ff85aaaea98bfb3d98459e136 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 12:19:04 +1000 Subject: [PATCH 097/352] stm32/irq: Clean up irq.h so it does not depend on core uPy defines. The irq.h file now just provides low-level IRQ definitions and priorities. All Python binding definitions are moved to modmachine.h, with some renaming of pyb -> machine, and also the machine_idle definition (was pyb_wfi) is moved to modmachine.c. The cc3200 and teensy ports are updated to build with these changes. Signed-off-by: Damien George --- ports/cc3200/mods/modmachine.c | 8 ++++--- ports/stm32/irq.c | 38 ++++++++++------------------------ ports/stm32/irq.h | 7 +------ ports/stm32/modmachine.c | 15 +++++++++++--- ports/stm32/modmachine.h | 7 +++++++ ports/stm32/modpyb.c | 6 +++--- ports/teensy/modpyb.c | 12 ++++++++--- 7 files changed, 48 insertions(+), 45 deletions(-) diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c index f70b399c07ebd..ddecd47f2b013 100644 --- a/ports/cc3200/mods/modmachine.c +++ b/ports/cc3200/mods/modmachine.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "irq.h" #include "inc/hw_types.h" #include "inc/hw_gpio.h" #include "inc/hw_ints.h" @@ -69,6 +68,9 @@ extern OsiTaskHandle xSimpleLinkSpawnTaskHndl; /// \module machine - functions related to the SoC /// +MP_DECLARE_CONST_FUN_OBJ_0(machine_disable_irq_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_enable_irq_obj); + /******************************************************************************/ // MicroPython bindings; @@ -176,8 +178,8 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, { MP_ROM_QSTR(MP_QSTR_wake_reason), MP_ROM_PTR(&machine_wake_reason_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, diff --git a/ports/stm32/irq.c b/ports/stm32/irq.c index 0100899733098..fdaf2385cc9d1 100644 --- a/ports/stm32/irq.c +++ b/ports/stm32/irq.c @@ -27,44 +27,28 @@ #include "py/obj.h" #include "py/mphal.h" #include "irq.h" - -/// \moduleref pyb +#include "modmachine.h" #if IRQ_ENABLE_STATS uint32_t irq_stats[IRQ_STATS_MAX] = {0}; #endif -/// \function wfi() -/// Wait for an interrupt. -/// This executies a `wfi` instruction which reduces power consumption -/// of the MCU until an interrupt occurs, at which point execution continues. -STATIC mp_obj_t pyb_wfi(void) { - __WFI(); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi); - -/// \function disable_irq() -/// Disable interrupt requests. -/// Returns the previous IRQ state: `False`/`True` for disabled/enabled IRQs -/// respectively. This return value can be passed to enable_irq to restore -/// the IRQ to its original state. -STATIC mp_obj_t pyb_disable_irq(void) { +// disable_irq() +// Disable interrupt requests. +// Returns the previous IRQ state which can be passed to enable_irq. +STATIC mp_obj_t machine_disable_irq(void) { return mp_obj_new_bool(disable_irq() == IRQ_STATE_ENABLED); } -MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq); +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); -/// \function enable_irq(state=True) -/// Enable interrupt requests. -/// If `state` is `True` (the default value) then IRQs are enabled. -/// If `state` is `False` then IRQs are disabled. The most common use of -/// this function is to pass it the value returned by `disable_irq` to -/// exit a critical section. -STATIC mp_obj_t pyb_enable_irq(uint n_args, const mp_obj_t *arg) { +// enable_irq(state=True) +// Enable interrupt requests, based on the argument, which is usually the +// value returned by a previous call to disable_irq. +STATIC mp_obj_t machine_enable_irq(uint n_args, const mp_obj_t *arg) { enable_irq((n_args == 0 || mp_obj_is_true(arg[0])) ? IRQ_STATE_ENABLED : IRQ_STATE_DISABLED); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj, 0, 1, pyb_enable_irq); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_enable_irq_obj, 0, 1, machine_enable_irq); #if IRQ_ENABLE_STATS // return a memoryview of the irq statistics array diff --git a/ports/stm32/irq.h b/ports/stm32/irq.h index 4b1251666c202..6c10f5e1b07f5 100644 --- a/ports/stm32/irq.h +++ b/ports/stm32/irq.h @@ -52,7 +52,7 @@ extern uint32_t irq_stats[IRQ_STATS_MAX]; #define IRQ_EXIT(irq) #endif -static inline mp_uint_t query_irq(void) { +static inline uint32_t query_irq(void) { return __get_PRIMASK(); } @@ -92,11 +92,6 @@ static inline void restore_irq_pri(uint32_t state) { #endif -MP_DECLARE_CONST_FUN_OBJ_0(pyb_wfi_obj); -MP_DECLARE_CONST_FUN_OBJ_0(pyb_disable_irq_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_enable_irq_obj); -MP_DECLARE_CONST_FUN_OBJ_0(pyb_irq_stats_obj); - // IRQ priority definitions. // // Lower number implies higher interrupt priority. diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 6fd2488a5ae7b..ac4d8712397e4 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -346,6 +346,15 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 4, machine_freq); +// idle() +// This executies a wfi machine instruction which reduces power consumption +// of the MCU until an interrupt occurs, at which point execution continues. +STATIC mp_obj_t machine_idle(void) { + __WFI(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + STATIC mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) { if (n_args != 0) { mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]}; @@ -382,7 +391,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_HW_ENABLE_RNG { MP_ROM_QSTR(MP_QSTR_rng), MP_ROM_PTR(&pyb_rng_get_obj) }, #endif - { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&pyb_wfi_obj) }, + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_lightsleep), MP_ROM_PTR(&machine_lightsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, @@ -391,8 +400,8 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_wake_reason), MP_ROM_PTR(&machine_wake_reason_obj) }, #endif - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, diff --git a/ports/stm32/modmachine.h b/ports/stm32/modmachine.h index f04bfb486bb81..e4ccf638855e8 100644 --- a/ports/stm32/modmachine.h +++ b/ports/stm32/modmachine.h @@ -39,7 +39,14 @@ MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_reset_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj); + +MP_DECLARE_CONST_FUN_OBJ_0(machine_idle_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj); +MP_DECLARE_CONST_FUN_OBJ_0(machine_disable_irq_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_enable_irq_obj); + +MP_DECLARE_CONST_FUN_OBJ_0(pyb_irq_stats_obj); + #endif // MICROPY_INCLUDED_STM32_MODMACHINE_H diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 321fb62e9a162..c92090699108e 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -146,9 +146,9 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_repl_info), MP_ROM_PTR(&pyb_set_repl_info_obj) }, #endif - { MP_ROM_QSTR(MP_QSTR_wfi), MP_ROM_PTR(&pyb_wfi_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_wfi), MP_ROM_PTR(&machine_idle_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, #if IRQ_ENABLE_STATS { MP_ROM_QSTR(MP_QSTR_irq_stats), MP_ROM_PTR(&pyb_irq_stats_obj) }, #endif diff --git a/ports/teensy/modpyb.c b/ports/teensy/modpyb.c index 26e3f4c43a0dc..f4384a885426e 100644 --- a/ports/teensy/modpyb.c +++ b/ports/teensy/modpyb.c @@ -36,7 +36,6 @@ #include "lib/utils/pyexec.h" #include "gccollect.h" -#include "irq.h" #include "systick.h" #include "led.h" #include "pin.h" @@ -53,6 +52,7 @@ #include "dac.h" #include "usb.h" #include "portmodules.h" +#include "modmachine.h" /// \module pyb - functions related to the pyboard /// @@ -230,6 +230,12 @@ STATIC mp_obj_t pyb_udelay(mp_obj_t usec_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay); +STATIC mp_obj_t pyb_wfi(void) { + __WFI(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi); + STATIC mp_obj_t pyb_stop(void) { printf("stop not currently implemented\n"); return mp_const_none; @@ -285,8 +291,8 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { #endif { MP_ROM_QSTR(MP_QSTR_wfi), MP_ROM_PTR(&pyb_wfi_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&pyb_disable_irq_obj) }, - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&pyb_enable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&pyb_stop_obj) }, { MP_ROM_QSTR(MP_QSTR_standby), MP_ROM_PTR(&pyb_standby_obj) }, From ce326699d7bb4e618600475c856f2ee1fe333442 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 13:33:31 +1000 Subject: [PATCH 098/352] stm32/powerctrlboot: Include irq.h to get definitions of IRQ priorities. irq.h is included by py/mphal.h but it's better to be explicit, eg if mboot uses powerctrlboot.c. Signed-off-by: Damien George --- ports/stm32/powerctrlboot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 749cb5505caaa..206b19b75067a 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -25,6 +25,7 @@ */ #include "py/mphal.h" +#include "irq.h" #include "powerctrl.h" static inline void powerctrl_config_systick(void) { From afd47d58ac25b292bb7ff5ba3b1c53dbf3750d81 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 13:35:53 +1000 Subject: [PATCH 099/352] stm32/flash: Make flash C-API reusable, and funcs return an error code. This commit makes the low-level flash C functions usable by code other than flashbdev.c (eg by mboot). Changes in this commit are: - flash_erase() and flash_write() now return an errno error code, a negative value on error. - flash_erase() now automatically locks the flash, as well as unlocking it. - flash_write() now automatically unlocks the flash, as well as locking it. - flashbdev.c is modified for the above changes. Signed-off-by: Damien George --- ports/stm32/flash.c | 68 +++++++++++++++++++++-------------------- ports/stm32/flash.h | 4 +-- ports/stm32/mphalport.h | 4 +++ 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 99c95f7d993ed..7c7d2f7b6e0dd 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -26,6 +26,7 @@ #include "py/mpconfig.h" #include "py/misc.h" +#include "py/mphal.h" #include "flash.h" typedef struct { @@ -162,17 +163,18 @@ uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *si return 0; } -void flash_erase(uint32_t flash_dest, uint32_t num_word32) { +int flash_erase(uint32_t flash_dest, uint32_t num_word32) { // check there is something to write if (num_word32 == 0) { - return; + return 0; } - // unlock + // Unlock the flash for erase. HAL_FLASH_Unlock(); - FLASH_EraseInitTypeDef EraseInitStruct; + // Clear pending flags (if any) and set up EraseInitStruct. + FLASH_EraseInitTypeDef EraseInitStruct; #if defined(STM32F0) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; @@ -190,17 +192,14 @@ void flash_erase(uint32_t flash_dest, uint32_t num_word32) { EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; #elif defined(STM32L4) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); - - // erase the sector(s) // The sector returned by flash_get_sector_info can not be used // as the flash has on each bank 0/1 pages 0..255 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.Banks = get_bank(flash_dest); EraseInitStruct.Page = get_page(flash_dest); EraseInitStruct.NbPages = get_page(flash_dest + 4 * num_word32 - 1) - EraseInitStruct.Page + 1; - ; #else - // Clear pending flags (if any) + #if defined(STM32H7) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS_BANK1 | FLASH_FLAG_ALL_ERRORS_BANK2); #else @@ -208,7 +207,6 @@ void flash_erase(uint32_t flash_dest, uint32_t num_word32) { FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); #endif - // erase the sector(s) EraseInitStruct.TypeErase = TYPEERASE_SECTORS; EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3; // voltage range needs to be 2.7V to 3.6V #if defined(STM32H7) @@ -216,14 +214,17 @@ void flash_erase(uint32_t flash_dest, uint32_t num_word32) { #endif EraseInitStruct.Sector = flash_get_sector_info(flash_dest, NULL, NULL); EraseInitStruct.NbSectors = flash_get_sector_info(flash_dest + 4 * num_word32 - 1, NULL, NULL) - EraseInitStruct.Sector + 1; + #endif + // Erase the sectors. uint32_t SectorError = 0; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - // error occurred during sector erase - HAL_FLASH_Lock(); // lock the flash - return; - } + HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); + + // Lock the flash after erase. + HAL_FLASH_Lock(); + + return mp_hal_status_to_neg_errno(status); } /* @@ -255,16 +256,21 @@ void flash_erase_it(uint32_t flash_dest, uint32_t num_word32) { } */ -void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) { +int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) { + // Unlock the flash for write. + HAL_FLASH_Unlock(); + + HAL_StatusTypeDef status = HAL_OK; + #if defined(STM32L4) || defined(STM32WB) // program the flash uint64 by uint64 for (int i = 0; i < num_word32 / 2; i++) { uint64_t val = *(uint64_t *)src; - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, flash_dest, val) != HAL_OK) { - // error occurred during flash write - HAL_FLASH_Lock(); // lock the flash - return; + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, flash_dest, val); + if (status != HAL_OK) { + num_word32 = 0; // don't write any odd word after this loop + break; } flash_dest += 8; src += 2; @@ -272,21 +278,16 @@ void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) if ((num_word32 & 0x01) == 1) { uint64_t val = *(uint64_t *)flash_dest; val = (val & 0xffffffff00000000uL) | (*src); - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, flash_dest, val) != HAL_OK) { - // error occurred during flash write - HAL_FLASH_Lock(); // lock the flash - return; - } + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, flash_dest, val); } #elif defined(STM32H7) // program the flash 256 bits at a time for (int i = 0; i < num_word32 / 8; i++) { - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, flash_dest, (uint64_t)(uint32_t)src) != HAL_OK) { - // error occurred during flash write - HAL_FLASH_Lock(); // lock the flash - return; + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, flash_dest, (uint64_t)(uint32_t)src); + if (status != HAL_OK) { + break; } flash_dest += 32; src += 8; @@ -296,10 +297,9 @@ void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) // program the flash word by word for (int i = 0; i < num_word32; i++) { - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flash_dest, *src) != HAL_OK) { - // error occurred during flash write - HAL_FLASH_Lock(); // lock the flash - return; + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flash_dest, *src); + if (status != HAL_OK) { + break; } flash_dest += 4; src += 1; @@ -307,8 +307,10 @@ void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) #endif - // lock the flash + // Lock the flash after write. HAL_FLASH_Lock(); + + return mp_hal_status_to_neg_errno(status); } /* diff --git a/ports/stm32/flash.h b/ports/stm32/flash.h index b9edf610610cb..048914e501e22 100644 --- a/ports/stm32/flash.h +++ b/ports/stm32/flash.h @@ -27,7 +27,7 @@ #define MICROPY_INCLUDED_STM32_FLASH_H uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size); -void flash_erase(uint32_t flash_dest, uint32_t num_word32); -void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32); +int flash_erase(uint32_t flash_dest, uint32_t num_word32); +int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32); #endif // MICROPY_INCLUDED_STM32_FLASH_H diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index fe9378a1c7252..a76945db5fea7 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -4,6 +4,10 @@ extern const unsigned char mp_hal_status_to_errno_table[4]; +static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { + return -mp_hal_status_to_errno_table[status]; +} + NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable From 4c8a68df6f25ed98d097b0defd3180739c813654 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 13:40:30 +1000 Subject: [PATCH 100/352] stm32/i2cslave: Add support for WB MCUs. Signed-off-by: Damien George --- ports/stm32/i2cslave.c | 2 +- ports/stm32/i2cslave.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/stm32/i2cslave.c b/ports/stm32/i2cslave.c index cacc00e0c4141..149e105ee8207 100644 --- a/ports/stm32/i2cslave.c +++ b/ports/stm32/i2cslave.c @@ -60,7 +60,7 @@ void i2c_slave_ev_irq_handler(i2c_slave_t *i2c) { } } -#elif defined(STM32F7) || defined(STM32H7) +#elif defined(STM32F7) || defined(STM32H7) || defined(STM32WB) void i2c_slave_init_helper(i2c_slave_t *i2c, int addr) { i2c->CR1 = I2C_CR1_STOPIE | I2C_CR1_ADDRIE | I2C_CR1_RXIE | I2C_CR1_TXIE; diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h index 55882acd8eb2e..f335c9291226c 100644 --- a/ports/stm32/i2cslave.h +++ b/ports/stm32/i2cslave.h @@ -28,6 +28,11 @@ #include STM32_HAL_H +#if !defined(I2C2_BASE) +// This MCU doesn't have I2C2_BASE, define it so that the i2c_idx calculation works. +#define I2C2_BASE (I2C1_BASE + ((I2C3_BASE - I2C1_BASE) / 2)) +#endif + typedef I2C_TypeDef i2c_slave_t; void i2c_slave_init_helper(i2c_slave_t *i2c, int addr); @@ -42,6 +47,10 @@ static inline void i2c_slave_init(i2c_slave_t *i2c, int irqn, int irq_pri, int a RCC->APB1LENR |= 1 << (RCC_APB1LENR_I2C1EN_Pos + i2c_idx); volatile uint32_t tmp = RCC->APB1LENR; // Delay after enabling clock (void)tmp; + #elif defined(STM32WB) + RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx); + volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock + (void)tmp; #endif i2c_slave_init_helper(i2c, addr); From 736daebfc8c7dce2fab10ee311519034d702c8b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 15:12:25 +1000 Subject: [PATCH 101/352] stm32/flash: Add flash_is_valid_addr, and extend sectors for 2MB F7. Signed-off-by: Damien George --- ports/stm32/flash.c | 12 ++++++++++++ ports/stm32/flash.h | 1 + 2 files changed, 13 insertions(+) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 7c7d2f7b6e0dd..5f96696a9ffd7 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -70,10 +70,15 @@ static const flash_layout_t flash_layout[] = { { 0x08020000, 0x20000, 3 }, }; #else +// This is for dual-bank mode disabled static const flash_layout_t flash_layout[] = { { 0x08000000, 0x08000, 4 }, { 0x08020000, 0x20000, 1 }, + #if FLASH_SECTOR_TOTAL == 8 { 0x08040000, 0x40000, 3 }, + #else + { 0x08040000, 0x40000, 7 }, + #endif }; #endif @@ -139,6 +144,13 @@ static uint32_t get_page(uint32_t addr) { #endif +bool flash_is_valid_addr(uint32_t addr) { + uint8_t last = MP_ARRAY_SIZE(flash_layout) - 1; + uint32_t end_of_flash = flash_layout[last].base_address + + flash_layout[last].sector_count * flash_layout[last].sector_size; + return flash_layout[0].base_address <= addr && addr < end_of_flash; +} + uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size) { if (addr >= flash_layout[0].base_address) { uint32_t sector_index = 0; diff --git a/ports/stm32/flash.h b/ports/stm32/flash.h index 048914e501e22..12cdaca55aab9 100644 --- a/ports/stm32/flash.h +++ b/ports/stm32/flash.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_FLASH_H #define MICROPY_INCLUDED_STM32_FLASH_H +bool flash_is_valid_addr(uint32_t addr); uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size); int flash_erase(uint32_t flash_dest, uint32_t num_word32); int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32); From a8778c8dc8849dfba14fb17ba148e4d0f78f0467 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 17:27:52 +1000 Subject: [PATCH 102/352] stm32/mboot: Use flash routines from main stm32 code rather than custom. The flash functions in ports/stm32/flash.c are almost identical to those in ports/stm32/mboot/main.c, so remove the duplicated code in mboot and use instead the main stm32 code. This also allows supporting other MCU series. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 3 +- ports/stm32/mboot/main.c | 162 ++++------------------------------ ports/stm32/mboot/mphalport.h | 5 ++ 3 files changed, 25 insertions(+), 145 deletions(-) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 43aae2a6785f3..a5db1666dd313 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -99,9 +99,10 @@ SRC_C = \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ + ports/stm32/flash.c \ + ports/stm32/flashbdev.c \ ports/stm32/i2cslave.c \ ports/stm32/qspi.c \ - ports/stm32/flashbdev.c \ ports/stm32/spibdev.c \ ports/stm32/usbd_conf.c \ $(wildcard $(BOARD_DIR)/*.c) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 4ae575f3ddf41..c53920ccf0244 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -31,6 +31,7 @@ #include "extmod/crypto-algorithms/sha256.c" #include "usbd_core.h" #include "storage.h" +#include "flash.h" #include "i2cslave.h" #include "mboot.h" #include "dfu.h" @@ -500,115 +501,26 @@ static int usrbtn_state(void) { #define MBOOT_SPIFLASH2_LAYOUT "" #endif -typedef struct { - uint32_t base_address; - uint32_t sector_size; - uint32_t sector_count; -} flash_layout_t; - -#if defined(STM32F7) -// FLASH_FLAG_PGSERR (Programming Sequence Error) was renamed to -// FLASH_FLAG_ERSERR (Erasing Sequence Error) in STM32F7 -#define FLASH_FLAG_PGSERR FLASH_FLAG_ERSERR -#endif - #if defined(STM32F4) \ || defined(STM32F722xx) \ || defined(STM32F723xx) \ || defined(STM32F732xx) \ || defined(STM32F733xx) - #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT - -static const flash_layout_t flash_layout[] = { - { 0x08000000, 0x04000, 4 }, - { 0x08010000, 0x10000, 1 }, - { 0x08020000, 0x20000, 3 }, - #if defined(FLASH_SECTOR_8) - { 0x08080000, 0x20000, 4 }, - #endif - #if defined(FLASH_SECTOR_12) - { 0x08100000, 0x04000, 4 }, - { 0x08110000, 0x10000, 1 }, - { 0x08120000, 0x20000, 7 }, - #endif -}; - #elif defined(STM32F765xx) || defined(STM32F767xx) || defined(STM32F769xx) - #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT - -// This is for dual-bank mode disabled -static const flash_layout_t flash_layout[] = { - { 0x08000000, 0x08000, 4 }, - { 0x08020000, 0x20000, 1 }, - { 0x08040000, 0x40000, 7 }, -}; - #elif defined(STM32H743xx) - #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/16*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT - -static const flash_layout_t flash_layout[] = { - { 0x08000000, 0x20000, 16 }, -}; - -#endif - -static inline bool flash_is_valid_addr(uint32_t addr) { - uint8_t last = MP_ARRAY_SIZE(flash_layout) - 1; - uint32_t end_of_flash = flash_layout[last].base_address + - flash_layout[last].sector_count * flash_layout[last].sector_size; - return flash_layout[0].base_address <= addr && addr < end_of_flash; -} - -static uint32_t flash_get_sector_index(uint32_t addr, uint32_t *sector_size) { - if (addr >= flash_layout[0].base_address) { - uint32_t sector_index = 0; - for (int i = 0; i < MP_ARRAY_SIZE(flash_layout); ++i) { - for (int j = 0; j < flash_layout[i].sector_count; ++j) { - uint32_t sector_start_next = flash_layout[i].base_address - + (j + 1) * flash_layout[i].sector_size; - if (addr < sector_start_next) { - *sector_size = flash_layout[i].sector_size; - return sector_index; - } - ++sector_index; - } - } - } - return 0; -} - -#if defined(STM32H7) -// get the bank of a given flash address -static uint32_t get_bank(uint32_t addr) { - if (READ_BIT(FLASH->OPTCR, FLASH_OPTCR_SWAP_BANK) == 0) { - // no bank swap - if (addr < (FLASH_BASE + FLASH_BANK_SIZE)) { - return FLASH_BANK_1; - } else { - return FLASH_BANK_2; - } - } else { - // bank swap - if (addr < (FLASH_BASE + FLASH_BANK_SIZE)) { - return FLASH_BANK_2; - } else { - return FLASH_BANK_1; - } - } -} #endif -static int flash_mass_erase(void) { +static int mboot_flash_mass_erase(void) { // TODO return -1; } -static int flash_page_erase(uint32_t addr, uint32_t *next_addr) { +static int mboot_flash_page_erase(uint32_t addr, uint32_t *next_addr) { uint32_t sector_size = 0; - uint32_t sector = flash_get_sector_index(addr, §or_size); + uint32_t sector = flash_get_sector_info(addr, NULL, §or_size); if (sector == 0) { // Don't allow to erase the sector with this bootloader in it dfu_context.status = DFU_STATUS_ERROR_ADDRESS; @@ -618,30 +530,10 @@ static int flash_page_erase(uint32_t addr, uint32_t *next_addr) { *next_addr = addr + sector_size; - HAL_FLASH_Unlock(); - - // Clear pending flags (if any) - #if defined(STM32H7) - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS_BANK1 | FLASH_FLAG_ALL_ERRORS_BANK2); - #else - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - #endif - - // erase the sector(s) - FLASH_EraseInitTypeDef EraseInitStruct; - EraseInitStruct.TypeErase = TYPEERASE_SECTORS; - EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3; // voltage range needs to be 2.7V to 3.6V - #if defined(STM32H7) - EraseInitStruct.Banks = get_bank(addr); - #endif - EraseInitStruct.Sector = sector; - EraseInitStruct.NbSectors = 1; - - uint32_t SectorError = 0; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - // error occurred during sector erase - return -1; + // Erase the flash page. + int ret = flash_erase(addr, sector_size / sizeof(uint32_t)); + if (ret != 0) { + return ret; } // Check the erase set bits to 1, at least for the first 256 bytes @@ -654,8 +546,9 @@ static int flash_page_erase(uint32_t addr, uint32_t *next_addr) { return 0; } -static int flash_write(uint32_t addr, const uint8_t *src8, size_t len) { - if (addr >= flash_layout[0].base_address && addr < flash_layout[0].base_address + flash_layout[0].sector_size) { +static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) { + uint32_t sector = flash_get_sector_info(addr, NULL, NULL); + if (sector == 0) { // Don't allow to write the sector with this bootloader in it dfu_context.status = DFU_STATUS_ERROR_ADDRESS; dfu_context.error = MBOOT_ERROR_STR_OVERWRITE_BOOTLOADER_IDX; @@ -664,32 +557,13 @@ static int flash_write(uint32_t addr, const uint8_t *src8, size_t len) { const uint32_t *src = (const uint32_t*)src8; size_t num_word32 = (len + 3) / 4; - HAL_FLASH_Unlock(); - #if defined(STM32H7) - - // program the flash 256 bits at a time - for (int i = 0; i < num_word32 / 8; ++i) { - if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr, (uint64_t)(uint32_t)src) != HAL_OK) { - return - 1; - } - addr += 32; - src += 8; + // Write the data to flash. + int ret = flash_write(addr, src, num_word32); + if (ret != 0) { + return ret; } - #else - - // program the flash word by word - for (size_t i = 0; i < num_word32; i++) { - if (HAL_FLASH_Program(TYPEPROGRAM_WORD, addr, *src) != HAL_OK) { - return -1; - } - addr += 4; - src += 1; - } - - #endif - // TODO verify data return 0; @@ -700,7 +574,7 @@ static int flash_write(uint32_t addr, const uint8_t *src8, size_t len) { static int do_mass_erase(void) { // TODO - return flash_mass_erase(); + return mboot_flash_mass_erase(); } #if defined(MBOOT_SPIFLASH_ADDR) || defined(MBOOT_SPIFLASH2_ADDR) @@ -735,7 +609,7 @@ int do_page_erase(uint32_t addr, uint32_t *next_addr) { } else #endif { - ret = flash_page_erase(addr, next_addr); + ret = mboot_flash_page_erase(addr, next_addr); } led0_state((ret == 0) ? LED0_STATE_SLOW_FLASH : LED0_STATE_SLOW_INVERTED_FLASH); @@ -775,7 +649,7 @@ int do_write(uint32_t addr, const uint8_t *src8, size_t len) { } else #endif if (flash_is_valid_addr(addr)) { - ret = flash_write(addr, src8, len); + ret = mboot_flash_write(addr, src8, len); } else { dfu_context.status = DFU_STATUS_ERROR_ADDRESS; dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; diff --git a/ports/stm32/mboot/mphalport.h b/ports/stm32/mboot/mphalport.h index 0c8cb91a6883a..56d18a9b05d1d 100644 --- a/ports/stm32/mboot/mphalport.h +++ b/ports/stm32/mboot/mphalport.h @@ -28,6 +28,11 @@ #include "genhdr/pins.h" +// For simplicity just convert all HAL errors to one errno. +static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { + return status == HAL_OK ? 0 : -1; +} + #define mp_hal_delay_us_fast(us) mp_hal_delay_us(us) #define MP_HAL_PIN_MODE_INPUT (0) From 8675858465f3d83aab709170eab6dc141570acf4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 18:18:30 +1000 Subject: [PATCH 103/352] stm32/mboot: Use CMSIS system source code for SystemInit function. There's no need to duplicate this functionality in mboot, the code provided in stm32lib/CMSIS does the same thing and makes it easier to support other MCU series. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 2 + ports/stm32/mboot/main.c | 87 +++++++------------------------------- 2 files changed, 18 insertions(+), 71 deletions(-) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index a5db1666dd313..a504c2314d3f9 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -34,6 +34,7 @@ STFLASH ?= st-flash OPENOCD ?= openocd OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg STARTUP_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).o +SYSTEM_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/system_stm32$(MCU_SERIES)xx.o CROSS_COMPILE ?= arm-none-eabi- @@ -109,6 +110,7 @@ SRC_C = \ SRC_O = \ $(STARTUP_FILE) \ + $(SYSTEM_FILE) \ ports/stm32/resethandler.o \ $(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index c53920ccf0244..c8cd7ba60e673 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -140,72 +140,6 @@ static void __fatal_error(const char *msg) { /******************************************************************************/ // CLOCK -#if defined(STM32F4) || defined(STM32F7) - -#define CONFIG_RCC_CR_1ST (RCC_CR_HSION) -#define CONFIG_RCC_CR_2ND (RCC_CR_HSEON || RCC_CR_CSSON || RCC_CR_PLLON) -#define CONFIG_RCC_PLLCFGR (0x24003010) - -#elif defined(STM32H7) - -#define CONFIG_RCC_CR_1ST (RCC_CR_HSION) -#define CONFIG_RCC_CR_2ND (RCC_CR_PLL3ON | RCC_CR_PLL2ON | RCC_CR_PLL1ON | RCC_CR_CSSHSEON \ - | RCC_CR_HSEON | RCC_CR_HSI48ON | RCC_CR_CSIKERON | RCC_CR_CSION) -#define CONFIG_RCC_PLLCFGR (0x00000000) - -#else -#error Unknown processor -#endif - -void SystemInit(void) { - #if defined(STM32H7) - // Configure write-once power options, and wait for voltage levels to be ready - PWR->CR3 = PWR_CR3_LDOEN; - while (!(PWR->CSR1 & PWR_CSR1_ACTVOSRDY)) { - } - #endif - - // Set HSION bit - RCC->CR |= CONFIG_RCC_CR_1ST; - - // Reset CFGR register - RCC->CFGR = 0x00000000; - - // Reset HSEON, CSSON and PLLON bits - RCC->CR &= ~CONFIG_RCC_CR_2ND; - - // Reset PLLCFGR register - RCC->PLLCFGR = CONFIG_RCC_PLLCFGR; - - #if defined(STM32H7) - // Reset PLL and clock configuration registers - RCC->D1CFGR = 0x00000000; - RCC->D2CFGR = 0x00000000; - RCC->D3CFGR = 0x00000000; - RCC->PLLCKSELR = 0x00000000; - RCC->D1CCIPR = 0x00000000; - RCC->D2CCIP1R = 0x00000000; - RCC->D2CCIP2R = 0x00000000; - RCC->D3CCIPR = 0x00000000; - #endif - - // Reset HSEBYP bit - RCC->CR &= (uint32_t)0xFFFBFFFF; - - // Disable all interrupts - #if defined(STM32F4) || defined(STM32F7) - RCC->CIR = 0x00000000; - #elif defined(STM32H7) - RCC->CIER = 0x00000000; - #endif - - // Set location of vector table - SCB->VTOR = FLASH_BASE; - - // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI - SCB->CCR |= SCB_CCR_STKALIGN_Msk; -} - void systick_init(void) { // Configure SysTick as 1ms ticker SysTick_Config(SystemCoreClock / 1000); @@ -1311,12 +1245,26 @@ static void do_reset(void) { NVIC_SystemReset(); } -uint32_t SystemCoreClock; - extern PCD_HandleTypeDef pcd_fs_handle; extern PCD_HandleTypeDef pcd_hs_handle; void stm32_main(int initial_r0) { + #if defined(STM32H7) + // Configure write-once power options, and wait for voltage levels to be ready + PWR->CR3 = PWR_CR3_LDOEN; + while (!(PWR->CSR1 & PWR_CSR1_ACTVOSRDY)) { + } + + // Reset the kernel clock configuration registers for all domains. + RCC->D1CCIPR = 0x00000000; + RCC->D2CCIP1R = 0x00000000; + RCC->D2CCIP2R = 0x00000000; + RCC->D3CCIPR = 0x00000000; + #endif + + // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI + SCB->CCR |= SCB_CCR_STKALIGN_Msk; + #if defined(STM32F4) #if INSTRUCTION_CACHE_ENABLE __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); @@ -1355,9 +1303,6 @@ void stm32_main(int initial_r0) { goto enter_bootloader; } - // MCU starts up with HSI - SystemCoreClock = HSI_VALUE; - int reset_mode = get_reset_mode(); uint32_t msp = *(volatile uint32_t*)APPLICATION_ADDR; if (reset_mode != 4 && (msp & APP_VALIDITY_BITS) == 0) { From 705728369d7422905825e8e4d4c8001524b990e7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 19:35:52 +1000 Subject: [PATCH 104/352] stm32/mboot: Add support for using mboot with WB MCUs. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 2 ++ ports/stm32/mboot/main.c | 44 +++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index a504c2314d3f9..79aa3316f9b65 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -55,6 +55,7 @@ CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS = $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 -nostdlib $(CFLAGS_MOD) $(CFLAGS_EXTRA) CFLAGS += -D$(CMSIS_MCU) @@ -103,6 +104,7 @@ SRC_C = \ ports/stm32/flash.c \ ports/stm32/flashbdev.c \ ports/stm32/i2cslave.c \ + ports/stm32/powerctrlboot.c \ ports/stm32/qspi.c \ ports/stm32/spibdev.c \ ports/stm32/usbd_conf.c \ diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index c8cd7ba60e673..b3af495261a14 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -33,29 +33,36 @@ #include "storage.h" #include "flash.h" #include "i2cslave.h" +#include "irq.h" #include "mboot.h" +#include "powerctrl.h" #include "dfu.h" // Using polling is about 10% faster than not using it (and using IRQ instead) // This DFU code with polling runs in about 70% of the time of the ST bootloader +// With STM32WB MCUs only non-polling/IRQ mode is supported. +#if defined(STM32WB) +#define USE_USB_POLLING (0) +#else #define USE_USB_POLLING (1) +#endif // Using cache probably won't make it faster because we run at a low frequency, and best // to keep the MCU config as minimal as possible. #define USE_CACHE (0) // IRQ priorities (encoded values suitable for NVIC_SetPriority) -#define IRQ_PRI_SYSTICK (NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 0, 0)) +// Most values are defined in irq.h. #define IRQ_PRI_I2C (NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 1, 0)) // Configure PLL to give the desired CPU freq #undef MICROPY_HW_FLASH_LATENCY -#if defined(STM32H7) -#define CORE_PLL_FREQ (96000000) -#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_2 -#else +#if defined(STM32F4) || defined(STM32F7) #define CORE_PLL_FREQ (48000000) #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_1 +#elif defined(STM32H7) +#define CORE_PLL_FREQ (96000000) +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_2 #endif #undef MICROPY_HW_CLK_PLLM #undef MICROPY_HW_CLK_PLLN @@ -93,7 +100,11 @@ uint32_t get_le32(const uint8_t *b) { void mp_hal_delay_us(mp_uint_t usec) { // use a busy loop for the delay // sys freq is always a multiple of 2MHz, so division here won't lose precision + #if defined(CORE_PLL_FREQ) const uint32_t ucount = CORE_PLL_FREQ / 2000000 * usec / 2; + #else + const uint32_t ucount = SystemCoreClock / 2000000 * usec / 2; + #endif for (uint32_t count = 0; ++count <= ucount;) { } } @@ -313,6 +324,9 @@ uint32_t HAL_RCC_GetHCLKFreq(void) { #elif defined(STM32H7) #define AHBxENR AHB4ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos +#elif defined(STM32WB) +#define AHBxENR AHB2ENR +#define AHBxENR_GPIOAEN_Pos RCC_AHB2ENR_GPIOAEN_Pos #endif void mp_hal_pin_config(mp_hal_pin_obj_t port_pin, uint32_t mode, uint32_t pull, uint32_t alt) { @@ -445,6 +459,8 @@ static int usrbtn_state(void) { #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT #elif defined(STM32H743xx) #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/16*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#elif defined(STM32WB) +#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/256*04Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT #endif static int mboot_flash_mass_erase(void) { @@ -924,12 +940,19 @@ typedef struct _pyb_usbdd_obj_t { #define MBOOT_USB_PID BOOTLOADER_DFU_USB_PID #endif +#if !MICROPY_HW_USB_IS_MULTI_OTG +STATIC const uint8_t usbd_fifo_size[USBD_PMA_NUM_FIFO] = { + 32, 32, // EP0(out), EP0(in) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x unused +}; +#else static const uint8_t usbd_fifo_size[] = { 32, 8, 16, 8, 16, 0, 0, // FS: RX, EP0(in), 5x IN endpoints #if MICROPY_HW_USB_HS 116, 8, 64, 4, 64, 0, 0, 0, 0, 0, // HS: RX, EP0(in), 8x IN endpoints #endif }; +#endif __ALIGN_BEGIN static const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { USB_LEN_LANGID_STR_DESC, @@ -1455,6 +1478,15 @@ void I2Cx_EV_IRQHandler(void) { #endif #if !USE_USB_POLLING + +#if defined(STM32WB) + +void USB_LP_IRQHandler(void) { + HAL_PCD_IRQHandler(&pcd_fs_handle); +} + +#else + #if MBOOT_USB_AUTODETECT_PORT || MICROPY_HW_USB_MAIN_DEV == USB_PHY_FS_ID void OTG_FS_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd_fs_handle); @@ -1467,3 +1499,5 @@ void OTG_HS_IRQHandler(void) { } #endif #endif + +#endif From 6f40e6e131b666b373ff3245a095c7e710578c16 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 19:36:16 +1000 Subject: [PATCH 105/352] stm32/boards: Add build-time option for NUCLEO_WB55 to use mboot. As an example of how to use mboot on a WB series MCU. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk | 11 ++++++++++- ports/stm32/boards/stm32wb55xg.ld | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk index 416364df9ef6d..dcec788ed443f 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk @@ -1,9 +1,18 @@ MCU_SERIES = wb CMSIS_MCU = STM32WB55xx AF_FILE = boards/stm32wb55_af.csv -LD_FILES = boards/stm32wb55xg.ld boards/common_basic.ld STARTUP_FILE = lib/stm32lib/CMSIS/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.o +ifeq ($(USE_MBOOT),1) +# When using Mboot all the text goes together after the bootloader +LD_FILES = boards/stm32wb55xg.ld boards/common_bl.ld +TEXT0_ADDR = 0x08004000 +else +# When not using Mboot the text goes at the start of flash +LD_FILES = boards/stm32wb55xg.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 +endif + # MicroPython settings MICROPY_PY_BLUETOOTH = 1 MICROPY_BLUETOOTH_NIMBLE = 1 diff --git a/ports/stm32/boards/stm32wb55xg.ld b/ports/stm32/boards/stm32wb55xg.ld index 77596d7759689..dbeccc1895775 100644 --- a/ports/stm32/boards/stm32wb55xg.ld +++ b/ports/stm32/boards/stm32wb55xg.ld @@ -6,6 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* sectors 0-127 */ + FLASH_APP (rx) : ORIGIN = 0x08004000, LENGTH = 496K /* sectors 4-127 */ FLASH_FS (r) : ORIGIN = 0x08080000, LENGTH = 256K /* sectors 128-191 */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K /* SRAM1 */ RAM2A (xrw) : ORIGIN = 0x20030020, LENGTH = 8K /* SRAM2A */ From 13ad1a4f06f2d114c821279f58fa7462e8bd0d41 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Jun 2020 19:59:54 +1000 Subject: [PATCH 106/352] travis: In stm32 job, build mboot for NUCLEO_WB55. Signed-off-by: Damien George --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 09da9598e2018..b7aac0bd0d0fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,6 +79,7 @@ jobs: - make ${MAKEOPTS} -C ports/stm32 BOARD=STM32L476DISC - make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WB55 - make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 + - make ${MAKEOPTS} -C ports/stm32/mboot BOARD=NUCLEO_WB55 # qemu-arm port - stage: test From 81a7293ed63bb39e05e4303f7ad297807748c7ea Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 13:56:20 +1000 Subject: [PATCH 107/352] stm32/mboot: Set VTOR on start up to ensure it has the correct value. Commit 8675858465f3d83aab709170eab6dc141570acf4 switched to using the CMSIS provided SystemInit function which sets VTOR to 0x00000000 (previously it was 0x08000000). A VTOR of 0x00000000 will be correct on some MCUs but not on others where the built-in bootloader is remapped to this address, via __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(). To make sure mboot has the correct vector table, this commit explicitly sets VTOR to the correct value of 0x08000000. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index b3af495261a14..b3e63ccc26626 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -1285,6 +1285,9 @@ void stm32_main(int initial_r0) { RCC->D3CCIPR = 0x00000000; #endif + // Make sure IRQ vector table points to flash where this bootloader lives. + SCB->VTOR = FLASH_BASE; + // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI SCB->CCR |= SCB_CCR_STKALIGN_Msk; From b4dc4c5b9a1e6df620a14d85f18721a0bcb17d83 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 14:00:16 +1000 Subject: [PATCH 108/352] stm32/mboot: Use additional CFLAGS to compile string0.c. This is the same as a902b69dd51de0e3fe3bb6955296591d6a93abab but applied to mboot's Makefile. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 79aa3316f9b65..a72b752ac9430 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -57,6 +57,10 @@ CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +# Standard C functions like memset need to be compiled with special flags so +# the compiler does not optimise these functions in terms of themselves. +CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto + CFLAGS = $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 -nostdlib $(CFLAGS_MOD) $(CFLAGS_EXTRA) CFLAGS += -D$(CMSIS_MCU) CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) @@ -84,6 +88,7 @@ else COPT += -Os -DNDEBUG endif +$(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) LIB_SRC_C = \ lib/libc/string0.c \ lib/oofatfs/ff.c \ From 456a3abe8dbcd8ee822d81d6780db72d55e89d61 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 21 Jun 2020 17:10:20 +1000 Subject: [PATCH 109/352] py/obj.h: Add public mp_obj_is_dict_or_ordereddict() helper macro. And use it in py/objdict.c instead of mp_obj_is_dict_type. Signed-off-by: Damien George --- py/obj.h | 2 ++ py/objdict.c | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/py/obj.h b/py/obj.h index f80f00031dc77..590b9c4b6a4a1 100644 --- a/py/obj.h +++ b/py/obj.h @@ -708,6 +708,7 @@ extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; #define mp_obj_is_int(o) (mp_obj_is_small_int(o) || mp_obj_is_type(o, &mp_type_int)) #define mp_obj_is_str(o) (mp_obj_is_qstr(o) || mp_obj_is_type(o, &mp_type_str)) #define mp_obj_is_str_or_bytes(o) (mp_obj_is_qstr(o) || (mp_obj_is_obj(o) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->binary_op == mp_obj_str_binary_op)) +#define mp_obj_is_dict_or_ordereddict(o) (mp_obj_is_obj(o) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->make_new == mp_obj_dict_make_new) #define mp_obj_is_fun(o) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_function)) mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); @@ -890,6 +891,7 @@ typedef struct _mp_obj_dict_t { mp_obj_base_t base; mp_map_t map; } mp_obj_dict_t; +mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_obj_dict_init(mp_obj_dict_t *dict, size_t n_args); size_t mp_obj_dict_len(mp_obj_t self_in); mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index); diff --git a/py/objdict.c b/py/objdict.c index 69eda99738eec..4fa59f4634cb0 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -33,8 +33,6 @@ #include "py/objtype.h" #include "py/objstr.h" -#define mp_obj_is_dict_type(o) (mp_obj_is_obj(o) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->make_new == dict_make_new) - STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); // This is a helper function to iterate through a dictionary. The state of @@ -90,7 +88,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ } } -STATIC mp_obj_t dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { +mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_t dict_out = mp_obj_new_dict(0); mp_obj_dict_t *dict = MP_OBJ_TO_PTR(dict_out); dict->base.type = type; @@ -217,7 +215,7 @@ STATIC void mp_ensure_not_fixed(const mp_obj_dict_t *dict) { } STATIC mp_obj_t dict_clear(mp_obj_t self_in) { - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_ensure_not_fixed(self); @@ -228,7 +226,7 @@ STATIC mp_obj_t dict_clear(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); mp_obj_t mp_obj_dict_copy(mp_obj_t self_in) { - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); mp_obj_dict_t *other = MP_OBJ_TO_PTR(other_out); @@ -275,7 +273,7 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, MP_ROM_PTR(&dict_fromk #endif STATIC mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_lookup_kind_t lookup_kind) { - mp_check_self(mp_obj_is_dict_type(args[0])); + mp_check_self(mp_obj_is_dict_or_ordereddict(args[0])); mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); if (lookup_kind != MP_MAP_LOOKUP) { mp_ensure_not_fixed(self); @@ -320,7 +318,7 @@ STATIC mp_obj_t dict_setdefault(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault); STATIC mp_obj_t dict_popitem(mp_obj_t self_in) { - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_ensure_not_fixed(self); if (self->map.used == 0) { @@ -345,7 +343,7 @@ STATIC mp_obj_t dict_popitem(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - mp_check_self(mp_obj_is_dict_type(args[0])); + mp_check_self(mp_obj_is_dict_or_ordereddict(args[0])); mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); mp_ensure_not_fixed(self); @@ -354,7 +352,7 @@ STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwarg if (n_args == 2) { // given a positional argument - if (mp_obj_is_dict_type(args[1])) { + if (mp_obj_is_dict_or_ordereddict(args[1])) { // update from other dictionary (make sure other is not self) if (args[1] != args[0]) { size_t cur = 0; @@ -512,7 +510,7 @@ STATIC mp_obj_t mp_obj_new_dict_view(mp_obj_t dict, mp_dict_view_kind_t kind) { } STATIC mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) { - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); return mp_obj_new_dict_view(self_in, kind); } @@ -536,7 +534,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values); STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t *)iter_buf; o->base.type = &dict_view_it_type; o->kind = MP_DICT_VIEW_KEYS; @@ -573,7 +571,7 @@ const mp_obj_type_t mp_type_dict = { { &mp_type_type }, .name = MP_QSTR_dict, .print = dict_print, - .make_new = dict_make_new, + .make_new = mp_obj_dict_make_new, .unary_op = dict_unary_op, .binary_op = dict_binary_op, .subscr = dict_subscr, @@ -586,7 +584,7 @@ const mp_obj_type_t mp_type_ordereddict = { { &mp_type_type }, .name = MP_QSTR_OrderedDict, .print = dict_print, - .make_new = dict_make_new, + .make_new = mp_obj_dict_make_new, .unary_op = dict_unary_op, .binary_op = dict_binary_op, .subscr = dict_subscr, @@ -613,7 +611,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in) { } mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { - mp_check_self(mp_obj_is_dict_type(self_in)); + mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_ensure_not_fixed(self); mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; From 7dd480ad5505a40bb33ee52d01c5fa78b1273083 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 21 Jun 2020 17:14:18 +1000 Subject: [PATCH 110/352] extmod/moductypes: Use mp_obj_is_dict_or_ordereddict to simplify code. Signed-off-by: Damien George --- extmod/moductypes.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/extmod/moductypes.c b/extmod/moductypes.c index f7d2be1bc1487..811258424a3bf 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -137,11 +137,7 @@ STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_p (void)kind; mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); const char *typen = "unk"; - if (mp_obj_is_type(self->desc, &mp_type_dict) - #if MICROPY_PY_COLLECTIONS_ORDEREDDICT - || mp_obj_is_type(self->desc, &mp_type_ordereddict) - #endif - ) { + if (mp_obj_is_dict_or_ordereddict(self->desc)) { typen = "STRUCT"; } else if (mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); @@ -214,11 +210,7 @@ STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_ } STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) { - if (!mp_obj_is_type(desc_in, &mp_type_dict) - #if MICROPY_PY_COLLECTIONS_ORDEREDDICT - && !mp_obj_is_type(desc_in, &mp_type_ordereddict) - #endif - ) { + if (!mp_obj_is_dict_or_ordereddict(desc_in)) { if (mp_obj_is_type(desc_in, &mp_type_tuple)) { return uctypes_struct_agg_size((mp_obj_tuple_t *)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size); } else if (mp_obj_is_small_int(desc_in)) { @@ -418,11 +410,7 @@ STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) { STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) { mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); - if (!mp_obj_is_type(self->desc, &mp_type_dict) - #if MICROPY_PY_COLLECTIONS_ORDEREDDICT - && !mp_obj_is_type(self->desc, &mp_type_ordereddict) - #endif - ) { + if (!mp_obj_is_dict_or_ordereddict(self->desc)) { mp_raise_TypeError(MP_ERROR_TEXT("struct: no fields")); } From 457fdf61c3bca550b4ad3fb2c6822c838984dac0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 21 Jun 2020 17:25:44 +1000 Subject: [PATCH 111/352] py/objtype: Support passing in an OrderedDict to type() as the locals. An OrderedDict can now be used for the locals when creating a type explicitly via type(name, bases, locals). Signed-off-by: Damien George --- py/objtype.c | 6 +++--- tests/basics/class_ordereddict.py | 18 ++++++++++++++++++ tests/basics/class_ordereddict.py.exp | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/basics/class_ordereddict.py create mode 100644 tests/basics/class_ordereddict.py.exp diff --git a/py/objtype.c b/py/objtype.c index a539fcab29414..40900dc050358 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -153,7 +153,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t if (type->locals_dict != NULL) { // search locals_dict (the set of methods/attributes) - assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(type->locals_dict))); // MicroPython restriction, for now mp_map_t *locals_map = &type->locals_dict->map; mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP); if (elem != NULL) { @@ -1053,7 +1053,7 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // delete/store attribute if (self->locals_dict != NULL) { - assert(self->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(self->locals_dict))); // MicroPython restriction, for now mp_map_t *locals_map = &self->locals_dict->map; if (locals_map->is_fixed) { // can't apply delete/store to a fixed map @@ -1103,7 +1103,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) if (!mp_obj_is_type(bases_tuple, &mp_type_tuple)) { mp_raise_TypeError(NULL); } - if (!mp_obj_is_type(locals_dict, &mp_type_dict)) { + if (!mp_obj_is_dict_or_ordereddict(locals_dict)) { mp_raise_TypeError(NULL); } diff --git a/tests/basics/class_ordereddict.py b/tests/basics/class_ordereddict.py new file mode 100644 index 0000000000000..4dd25eb6e15b4 --- /dev/null +++ b/tests/basics/class_ordereddict.py @@ -0,0 +1,18 @@ +# test using an OrderedDict as the locals to construct a class + +try: + from ucollections import OrderedDict +except ImportError: + try: + from collections import OrderedDict + except ImportError: + print("SKIP") + raise SystemExit + +if not hasattr(int, "__dict__"): + print("SKIP") + raise SystemExit + + +A = type("A", (), OrderedDict(a=1, b=2, c=3, d=4, e=5)) +print([k for k in A.__dict__.keys() if not k.startswith("_")]) diff --git a/tests/basics/class_ordereddict.py.exp b/tests/basics/class_ordereddict.py.exp new file mode 100644 index 0000000000000..b723e327515c7 --- /dev/null +++ b/tests/basics/class_ordereddict.py.exp @@ -0,0 +1 @@ +['a', 'b', 'c', 'd', 'e'] From 76faeed098ec4ee70202202cb11e64b78a77f14e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 17:20:59 +1000 Subject: [PATCH 112/352] tools/makemanifest.py: Support freezing a subdirectory recursively. This adds support for freezing an entire directory while keeping the directory as part of the import path. For example freeze("path/to/library", "module") will recursively freeze all scripts in "path/to/library/module" and have them importable as "from module import ...". Signed-off-by: Damien George --- tools/makemanifest.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 2cee6aebc796a..babce7fa73f4b 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -76,9 +76,10 @@ def freeze(path, script=None, opt=0): If `script` is an iterable then freeze() is called on all items of the iterable (with the same `path` and `opt` passed through). - If `script` is a string then it specifies the filename to freeze, and - can include extra directories before the file. The file will be - searched for in `path`. + If `script` is a string then it specifies the file or directory to + freeze, and can include extra directories before the file or last + directory. The file or directory will be searched for in `path`. If + `script` is a directory then all files in that directory will be frozen. `opt` is the optimisation level to pass to mpy-cross when compiling .py to .mpy. @@ -182,14 +183,22 @@ def freeze_internal(kind, path, script, opt): if any(f[0] == KIND_AS_STR for f in manifest_list): raise FreezeError("can only freeze one str directory") manifest_list.append((KIND_AS_STR, path, script, opt)) - elif script is None: - for dirpath, dirnames, filenames in os.walk(path, followlinks=True): + elif script is None or isinstance(script, str) and script.find(".") == -1: + # Recursively search `path` for files to freeze, optionally restricted + # to a subdirectory specified by `script` + if script is None: + subdir = "" + else: + subdir = "/" + script + for dirpath, dirnames, filenames in os.walk(path + subdir, followlinks=True): for f in filenames: freeze_internal(kind, path, (dirpath + "/" + f)[len(path) + 1 :], opt) elif not isinstance(script, str): + # `script` is an iterable of items to freeze for s in script: freeze_internal(kind, path, s, opt) else: + # `script` should specify an individual file to be frozen extension_kind = {KIND_AS_MPY: ".py", KIND_MPY: ".mpy"} if kind == KIND_AUTO: for k, ext in extension_kind.items(): From 0c77668d11a0b8db7af57311828169e3fdc9678f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jun 2020 16:31:33 +1000 Subject: [PATCH 113/352] extmod/vfs_lfs: Fix littlefs bindings to build in nan-box mode. Signed-off-by: Damien George --- extmod/vfs_lfsx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index c6240967822aa..24816433be015 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -442,7 +442,7 @@ STATIC mp_import_stat_t MP_VFS_LFSx(import_stat)(void *self_in, const char *path MP_OBJ_VFS_LFSx *self = self_in; struct LFSx_API (info) info; mp_obj_str_t path_obj = { { &mp_type_str }, 0, 0, (const byte *)path }; - path = MP_VFS_LFSx(make_path)(self, &path_obj); + path = MP_VFS_LFSx(make_path)(self, MP_OBJ_FROM_PTR(&path_obj)); int ret = LFSx_API(stat)(&self->lfs, path, &info); if (ret == 0) { if (info.type == LFSx_MACRO(_TYPE_REG)) { From eb9850ef6cbb1d5a05d18a1e050f7a78b106c502 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jun 2020 16:08:05 +1000 Subject: [PATCH 114/352] stm32/mpconfigport.h: Enable PY_IO_FILEIO when any VFS is enabled. Previously, if FAT was not enabled but LFS1/2 was then MICROPY_PY_IO_FILEIO would be disabled and file binary-mode was not supported. Signed-off-by: Damien George --- ports/stm32/mpconfigport.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 13dce6e96d334..6f38d90e8d800 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -120,7 +120,7 @@ #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO (1) #define MICROPY_PY_IO_IOBASE (1) -#define MICROPY_PY_IO_FILEIO (MICROPY_VFS_FAT) // because mp_type_fileio/textio point to fatfs impl +#define MICROPY_PY_IO_FILEIO (MICROPY_VFS_FAT || MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) #define MICROPY_PY_SYS_MAXSIZE (1) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_STDFILES (1) @@ -210,9 +210,17 @@ #define MICROPY_FATFS_RPATH (2) #define MICROPY_FATFS_MULTI_PARTITION (1) -// TODO these should be generic, not bound to fatfs +// TODO these should be generic, not bound to a particular FS implementation +#if MICROPY_VFS_FAT #define mp_type_fileio mp_type_vfs_fat_fileio #define mp_type_textio mp_type_vfs_fat_textio +#elif MICROPY_VFS_LFS1 +#define mp_type_fileio mp_type_vfs_lfs1_fileio +#define mp_type_textio mp_type_vfs_lfs1_textio +#elif MICROPY_VFS_LFS2 +#define mp_type_fileio mp_type_vfs_lfs2_fileio +#define mp_type_textio mp_type_vfs_lfs2_textio +#endif // use vfs's functions for import stat and builtin open #define mp_import_stat mp_vfs_import_stat From c5af3217d985f517287c6b85bda16c5b7f9fa677 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jun 2020 16:10:00 +1000 Subject: [PATCH 115/352] stm32/timer: Support TIM1 on WB MCUs. Signed-off-by: Damien George --- ports/stm32/stm32_it.c | 2 +- ports/stm32/timer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index b84f4adfaeb81..8e96da177b583 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -602,7 +602,7 @@ void TIM1_UP_TIM10_IRQHandler(void) { IRQ_EXIT(TIM1_UP_TIM10_IRQn); } -#if defined(STM32L4) +#if defined(STM32L4) || defined(STM32WB) void TIM1_UP_TIM16_IRQHandler(void) { IRQ_ENTER(TIM1_UP_TIM16_IRQn); timer_irq_handler(1); diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 491221ec2a7de..c6f3d21f8671e 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -805,7 +805,7 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(1, TIM1_UP_TIM10_IRQn), #elif defined(STM32H7) TIM_ENTRY(1, TIM1_UP_IRQn), - #elif defined(STM32L4) + #elif defined(STM32L4) || defined(STM32WB) TIM_ENTRY(1, TIM1_UP_TIM16_IRQn), #endif #endif From 717b5073aa7295164f3c06a889b648071b435bc3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jun 2020 16:11:48 +1000 Subject: [PATCH 116/352] stm32/boards: Enable LFS2 on PYBD_SF3 and PYBD_SF6. This was missed in commit 120368ba1ab444b2f1c17d1eb69bc6f09072ec5d Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF3/mpconfigboard.mk | 1 + ports/stm32/boards/PYBD_SF6/mpconfigboard.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk b/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk index 119ec8f825b42..abe3dcd862373 100644 --- a/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk +++ b/ports/stm32/boards/PYBD_SF3/mpconfigboard.mk @@ -17,6 +17,7 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_VFS_LFS2 = 1 # PYBD-specific frozen modules FROZEN_MANIFEST = boards/PYBD_SF2/manifest.py diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk b/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk index ddc176e9cd422..bac516ab7949f 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.mk @@ -14,6 +14,7 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_VFS_LFS2 = 1 # PYBD-specific frozen modules FROZEN_MANIFEST = boards/PYBD_SF2/manifest.py From 763bd448a4348b36db08cbae41337507e47cb892 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 16:16:21 +1000 Subject: [PATCH 117/352] stm32/mboot: Don't search for firmware on FS, just attempt to open it. There's no need to do a directory listing to search for the given firmware filename, it just takes extra time and code size. Instead this commit changes it so that the requested firmware file is opened immediately and will abort if the file couldn't be opened. This also allows to specify files in a directory. Signed-off-by: Damien George --- ports/stm32/mboot/fsload.c | 43 ++++++++++++-------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 64eb2a3a5ba72..12f39c65b360d 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -204,7 +204,7 @@ static int fsload_program_file(FATFS *fatfs, const char *filename, bool write_to return 0; } -static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, size_t fname_len, const char *fname) { +static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, const char *fname) { fsload_bdev_t bdev = {base_addr, byte_len}; FATFS fatfs; fatfs.drv = &bdev; @@ -213,34 +213,14 @@ static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, size_t fn return -1; } - FF_DIR dp; - res = f_opendir(&fatfs, &dp, "/"); - if (res != FR_OK) { - return -1; - } + // Validate firmware + led_state_all(2); + int r = fsload_program_file(&fatfs, fname, false); - // Search for firmware file with correct name - int r; - for (;;) { - FILINFO fno; - res = f_readdir(&dp, &fno); - char *fn = fno.fname; - if (res != FR_OK || fn[0] == 0) { - // Finished listing dir, no firmware found - r = -1; - break; - } - if (memcmp(fn, fname, fname_len) == 0 && fn[fname_len] == '\0') { - // Found firmware - led_state_all(2); - r = fsload_program_file(&fatfs, fn, false); - if (r == 0) { - // Firmware is valid, program it - led_state_all(4); - r = fsload_program_file(&fatfs, fn, true); - } - break; - } + if (r == 0) { + // Firmware is valid, program it + led_state_all(4); + r = fsload_program_file(&fatfs, fname, true); } return r; @@ -252,9 +232,12 @@ int fsload_process(void) { return -1; } + // Get mount point id and create null-terminated filename uint8_t mount_point = elem[0]; uint8_t fname_len = elem[-1] - 1; - const char *fname = (const char*)&elem[1]; + char fname[256]; + memcpy(fname, &elem[1], fname_len); + fname[fname_len] = '\0'; elem = ELEM_DATA_START; for (;;) { @@ -267,7 +250,7 @@ int fsload_process(void) { uint32_t base_addr = get_le32(&elem[2]); uint32_t byte_len = get_le32(&elem[6]); if (elem[1] == ELEM_MOUNT_FAT) { - int ret = fsload_process_fatfs(base_addr, byte_len, fname_len, fname); + int ret = fsload_process_fatfs(base_addr, byte_len, fname); // Flash LEDs based on success/failure of update for (int i = 0; i < 4; ++i) { if (ret == 0) { From 390f32922d81b56495198d3d93b7258f450c03b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 22:57:16 +1000 Subject: [PATCH 118/352] stm32/mboot: Decouple stream, filesystem and top-level loading code. This commit factors the code for files and streaming to separate source files (vfs_fat.c and gzstream.c respectively) and introduces an abstract gzstream interface to make it easier to plug in different filesystems. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 3 +- ports/stm32/mboot/fsload.c | 110 +++++----------------- ports/stm32/mboot/gzstream.c | 94 ++++++++++++++++++ ports/stm32/mboot/gzstream.h | 45 +++++++++ ports/stm32/mboot/mboot.h | 5 - ports/stm32/mboot/vfs.h | 43 +++++++++ ports/stm32/mboot/{diskio.c => vfs_fat.c} | 54 +++++++++-- 7 files changed, 256 insertions(+), 98 deletions(-) create mode 100644 ports/stm32/mboot/gzstream.c create mode 100644 ports/stm32/mboot/gzstream.h create mode 100644 ports/stm32/mboot/vfs.h rename ports/stm32/mboot/{diskio.c => vfs_fat.c} (60%) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index a72b752ac9430..9ab186412ea93 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -102,7 +102,8 @@ SRC_C = \ main.c \ elem.c \ fsload.c \ - diskio.c \ + gzstream.c \ + vfs_fat.c \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 12f39c65b360d..dc40a28ea02d4 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -27,81 +27,18 @@ #include #include "py/mphal.h" -#include "lib/oofatfs/ff.h" -#include "extmod/uzlib/uzlib.h" #include "mboot.h" +#include "vfs.h" #if MBOOT_FSLOAD -#define DICT_SIZE (1 << 15) - -typedef struct _gz_stream_t { - FIL fp; - TINF_DATA tinf; - uint8_t buf[512]; - uint8_t dict[DICT_SIZE]; -} gz_stream_t; - -static gz_stream_t gz_stream SECTION_NOZERO_BSS; - -static int gz_stream_read_src(TINF_DATA *tinf) { - UINT n; - FRESULT res = f_read(&gz_stream.fp, gz_stream.buf, sizeof(gz_stream.buf), &n); - if (res != FR_OK) { - return -1; - } - if (n == 0) { - return -1; - } - tinf->source = gz_stream.buf + 1; - tinf->source_limit = gz_stream.buf + n; - return gz_stream.buf[0]; -} - -static int gz_stream_open(FATFS *fatfs, const char *filename) { - FRESULT res = f_open(fatfs, &gz_stream.fp, filename, FA_READ); - if (res != FR_OK) { - return -1; - } - memset(&gz_stream.tinf, 0, sizeof(gz_stream.tinf)); - gz_stream.tinf.readSource = gz_stream_read_src; - - int st = uzlib_gzip_parse_header(&gz_stream.tinf); - if (st != TINF_OK) { - f_close(&gz_stream.fp); - return -1; - } - - uzlib_uncompress_init(&gz_stream.tinf, gz_stream.dict, DICT_SIZE); - - return 0; -} - -static int gz_stream_read(size_t len, uint8_t *buf) { - gz_stream.tinf.dest = buf; - gz_stream.tinf.dest_limit = buf + len; - int st = uzlib_uncompress_chksum(&gz_stream.tinf); - if (st == TINF_DONE) { - return 0; - } - if (st < 0) { - return st; - } - return gz_stream.tinf.dest - buf; -} - -static int fsload_program_file(FATFS *fatfs, const char *filename, bool write_to_flash) { - int res = gz_stream_open(fatfs, filename); - if (res != 0) { - return res; - } - +static int fsload_program_file(bool write_to_flash) { // Parse DFU uint8_t buf[512]; size_t file_offset; // Read file header, <5sBIB - res = gz_stream_read(11, buf); + int res = gz_stream_read(11, buf); if (res != 11) { return -1; } @@ -204,26 +141,23 @@ static int fsload_program_file(FATFS *fatfs, const char *filename, bool write_to return 0; } -static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, const char *fname) { - fsload_bdev_t bdev = {base_addr, byte_len}; - FATFS fatfs; - fatfs.drv = &bdev; - FRESULT res = f_mount(&fatfs); - if (res != FR_OK) { - return -1; - } - - // Validate firmware - led_state_all(2); - int r = fsload_program_file(&fatfs, fname, false); - - if (r == 0) { - // Firmware is valid, program it - led_state_all(4); - r = fsload_program_file(&fatfs, fname, true); +static int fsload_validate_and_program_file(void *stream, const stream_methods_t *meth, const char *fname) { + // First pass verifies the file, second pass programs it + for (unsigned int pass = 0; pass <= 1; ++pass) { + led_state_all(pass == 0 ? 2 : 4); + int res = meth->open(stream, fname); + if (res == 0) { + res = gz_stream_init(stream, meth->read); + if (res == 0) { + res = fsload_program_file(pass == 0 ? false : true); + } + } + meth->close(stream); + if (res != 0) { + return res; + } } - - return r; + return 0; } int fsload_process(void) { @@ -250,7 +184,11 @@ int fsload_process(void) { uint32_t base_addr = get_le32(&elem[2]); uint32_t byte_len = get_le32(&elem[6]); if (elem[1] == ELEM_MOUNT_FAT) { - int ret = fsload_process_fatfs(base_addr, byte_len, fname); + vfs_fat_context_t ctx; + int ret = vfs_fat_mount(&ctx, base_addr, byte_len); + if (ret == 0) { + ret = fsload_validate_and_program_file(&ctx, &vfs_fat_stream_methods, fname); + } // Flash LEDs based on success/failure of update for (int i = 0; i < 4; ++i) { if (ret == 0) { diff --git a/ports/stm32/mboot/gzstream.c b/ports/stm32/mboot/gzstream.c new file mode 100644 index 0000000000000..20ba33045f8d8 --- /dev/null +++ b/ports/stm32/mboot/gzstream.c @@ -0,0 +1,94 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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 "py/mphal.h" +#include "extmod/uzlib/uzlib.h" +#include "gzstream.h" +#include "mboot.h" + +#if MBOOT_FSLOAD + +#define DICT_SIZE (1 << 15) + +typedef struct _gz_stream_t { + void *stream_data; + stream_read_t stream_read; + TINF_DATA tinf; + uint8_t buf[512]; + uint8_t dict[DICT_SIZE]; +} gz_stream_t; + +static gz_stream_t gz_stream SECTION_NOZERO_BSS; + +static int gz_stream_read_src(TINF_DATA *tinf) { + int n = gz_stream.stream_read(gz_stream.stream_data, gz_stream.buf, sizeof(gz_stream.buf)); + if (n < 0) { + // Stream error + return -1; + } + if (n == 0) { + // No data / EOF + return -1; + } + + tinf->source = gz_stream.buf + 1; + tinf->source_limit = gz_stream.buf + n; + return gz_stream.buf[0]; +} + +int gz_stream_init(void *stream_data, stream_read_t stream_read) { + gz_stream.stream_data = stream_data; + gz_stream.stream_read = stream_read; + + memset(&gz_stream.tinf, 0, sizeof(gz_stream.tinf)); + gz_stream.tinf.readSource = gz_stream_read_src; + + int st = uzlib_gzip_parse_header(&gz_stream.tinf); + if (st != TINF_OK) { + return -1; + } + + uzlib_uncompress_init(&gz_stream.tinf, gz_stream.dict, DICT_SIZE); + + return 0; +} + +int gz_stream_read(size_t len, uint8_t *buf) { + gz_stream.tinf.dest = buf; + gz_stream.tinf.dest_limit = buf + len; + int st = uzlib_uncompress_chksum(&gz_stream.tinf); + if (st == TINF_DONE) { + return 0; + } + if (st < 0) { + return st; + } + return gz_stream.tinf.dest - buf; +} + +#endif // MBOOT_FSLOAD diff --git a/ports/stm32/mboot/gzstream.h b/ports/stm32/mboot/gzstream.h new file mode 100644 index 0000000000000..ec11ba79bbb02 --- /dev/null +++ b/ports/stm32/mboot/gzstream.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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_STM32_MBOOT_GZSTREAM_H +#define MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H + +#include +#include + +typedef int (*stream_open_t)(void *stream, const char *fname); +typedef void (*stream_close_t)(void *stream); +typedef int (*stream_read_t)(void *stream, uint8_t *buf, size_t len); + +typedef struct _stream_methods_t { + stream_open_t open; + stream_close_t close; + stream_read_t read; +} stream_methods_t; + +int gz_stream_init(void *stream_data, stream_read_t stream_read); +int gz_stream_read(size_t len, uint8_t *buf); + +#endif // MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 7dc1ada0c32f6..ef9af7854b894 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -44,11 +44,6 @@ enum { ELEM_MOUNT_FAT = 1, }; -typedef struct _fsload_bdev_t { - uint32_t base_addr; - uint32_t byte_len; -} fsload_bdev_t; - extern uint8_t _estack[ELEM_DATA_SIZE]; uint32_t get_le32(const uint8_t *b); diff --git a/ports/stm32/mboot/vfs.h b/ports/stm32/mboot/vfs.h new file mode 100644 index 0000000000000..9ee1c5ff3cd42 --- /dev/null +++ b/ports/stm32/mboot/vfs.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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_STM32_MBOOT_VFS_H +#define MICROPY_INCLUDED_STM32_MBOOT_VFS_H + +#include "lib/oofatfs/ff.h" +#include "gzstream.h" + +typedef struct _vfs_fat_context_t { + uint32_t bdev_base_addr; + uint32_t bdev_byte_len; + FATFS fatfs; + FIL fp; +} vfs_fat_context_t; + +extern const stream_methods_t vfs_fat_stream_methods; + +int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len); + +#endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H diff --git a/ports/stm32/mboot/diskio.c b/ports/stm32/mboot/vfs_fat.c similarity index 60% rename from ports/stm32/mboot/diskio.c rename to ports/stm32/mboot/vfs_fat.c index 5f68f26a8e669..20994c8b4c00d 100644 --- a/ports/stm32/mboot/diskio.c +++ b/ports/stm32/mboot/vfs_fat.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -28,6 +28,7 @@ #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "mboot.h" +#include "vfs.h" #if MBOOT_FSLOAD @@ -38,10 +39,10 @@ #endif DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) { - fsload_bdev_t *bdev = pdrv; + vfs_fat_context_t *ctx = pdrv; - if (0 <= sector && sector < bdev->byte_len / 512) { - do_read(bdev->base_addr + sector * SECSIZE, count * SECSIZE, buf); + if (0 <= sector && sector < ctx->bdev_byte_len / 512) { + do_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf); return RES_OK; } @@ -49,14 +50,14 @@ DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) { } DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { - fsload_bdev_t *bdev = pdrv; + vfs_fat_context_t *ctx = pdrv; switch (cmd) { case CTRL_SYNC: return RES_OK; case GET_SECTOR_COUNT: - *((DWORD*)buf) = bdev->byte_len / SECSIZE; + *((DWORD*)buf) = ctx->bdev_byte_len / SECSIZE; return RES_OK; case GET_SECTOR_SIZE: @@ -77,4 +78,45 @@ DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { } } +int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len) { + ctx->bdev_base_addr = base_addr; + ctx->bdev_byte_len = byte_len; + ctx->fatfs.drv = ctx; + FRESULT res = f_mount(&ctx->fatfs); + if (res != FR_OK) { + return -1; + } + return 0; +} + +static int vfs_fat_stream_open(void *stream_in, const char *fname) { + vfs_fat_context_t *stream = stream_in; + FRESULT res = f_open(&stream->fatfs, &stream->fp, fname, FA_READ); + if (res != FR_OK) { + return -1; + } + return 0; +} + +static void vfs_fat_stream_close(void *stream_in) { + vfs_fat_context_t *stream = stream_in; + f_close(&stream->fp); +} + +static int vfs_fat_stream_read(void *stream_in, uint8_t *buf, size_t len) { + vfs_fat_context_t *stream = stream_in; + UINT n; + FRESULT res = f_read(&stream->fp, buf, len, &n); + if (res != FR_OK) { + return -1; + } + return n; +} + +const stream_methods_t vfs_fat_stream_methods = { + vfs_fat_stream_open, + vfs_fat_stream_close, + vfs_fat_stream_read, +}; + #endif // MBOOT_FSLOAD From 67fd58bbd212badff9cd66d0f5f215834844e6aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 23:58:26 +1000 Subject: [PATCH 119/352] stm32/mboot: Add support for littlefs. Mboot now supports FAT, LFS1 and LFS2 filesystems, to load firmware from. The filesystem needed by the board must be explicitly enabled by the configuration variables MBOOT_VFS_FAT, MBOOT_VFS_LFS1 and MBOOT_VFS_LFS2. Boards that previously used FAT implicitly (with MBOOT_FSLOAD enabled) must now add the following config to mpconfigboard.h: #define MBOOT_VFS_FAT (1) Signed-off-by: Damien George --- .../MIKROE_CLICKER2_STM32/mpconfigboard.h | 1 + ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 1 + ports/stm32/mboot/Makefile | 7 + ports/stm32/mboot/fsload.c | 71 +++++-- ports/stm32/mboot/fwupdate.py | 13 +- ports/stm32/mboot/mboot.h | 8 +- ports/stm32/mboot/vfs.h | 55 +++++- ports/stm32/mboot/vfs_fat.c | 4 +- ports/stm32/mboot/vfs_lfs.c | 177 ++++++++++++++++++ 9 files changed, 309 insertions(+), 28 deletions(-) create mode 100644 ports/stm32/mboot/vfs_lfs.c diff --git a/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h index eb622cd296a84..71af3ce03823d 100644 --- a/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h +++ b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h @@ -83,3 +83,4 @@ #define MBOOT_BOOTPIN_PULL (MP_HAL_PIN_PULL_NONE) #define MBOOT_BOOTPIN_ACTIVE (0) #define MBOOT_FSLOAD (1) +#define MBOOT_VFS_FAT (1) diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index 757dacf9a7c18..f6e130eb57eaf 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -188,6 +188,7 @@ extern struct _spi_bdev_t spi_bdev2; #define MBOOT_USB_AUTODETECT_PORT (1) #define MBOOT_FSLOAD (1) +#define MBOOT_VFS_FAT (1) #define MBOOT_I2C_PERIPH_ID 1 #define MBOOT_I2C_SCL (pin_B8) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 9ab186412ea93..c901dfb3344a4 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -70,6 +70,8 @@ CFLAGS += -DSTM32_HAL_H='' CFLAGS += -DBOARD_$(BOARD) CFLAGS += -DAPPLICATION_ADDR=$(TEXT0_ADDR) CFLAGS += -DFFCONF_H=\"ports/stm32/mboot/ffconf.h\" +CFLAGS += -DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT +CFLAGS += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT CFLAGS += -DBUILDING_MBOOT=1 CFLAGS += -DBOOTLOADER_DFU_USB_VID=$(BOOTLOADER_DFU_USB_VID) -DBOOTLOADER_DFU_USB_PID=$(BOOTLOADER_DFU_USB_PID) @@ -91,6 +93,10 @@ endif $(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) LIB_SRC_C = \ lib/libc/string0.c \ + lib/littlefs/lfs1.c \ + lib/littlefs/lfs1_util.c \ + lib/littlefs/lfs2.c \ + lib/littlefs/lfs2_util.c \ lib/oofatfs/ff.c \ lib/oofatfs/ffunicode.c \ extmod/uzlib/crc32.c \ @@ -104,6 +110,7 @@ SRC_C = \ fsload.c \ gzstream.c \ vfs_fat.c \ + vfs_lfs.c \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index dc40a28ea02d4..1e1ad7a04d015 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -32,6 +32,10 @@ #if MBOOT_FSLOAD +#if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2) +#error Must enable at least one VFS component +#endif + static int fsload_program_file(bool write_to_flash) { // Parse DFU uint8_t buf[512]; @@ -183,27 +187,58 @@ int fsload_process(void) { if (elem[0] == mount_point) { uint32_t base_addr = get_le32(&elem[2]); uint32_t byte_len = get_le32(&elem[6]); + int ret; + union { + #if MBOOT_VFS_FAT + vfs_fat_context_t fat; + #endif + #if MBOOT_VFS_LFS1 + vfs_lfs1_context_t lfs1; + #endif + #if MBOOT_VFS_LFS2 + vfs_lfs2_context_t lfs2; + #endif + } ctx; + const stream_methods_t *methods; + #if MBOOT_VFS_FAT if (elem[1] == ELEM_MOUNT_FAT) { - vfs_fat_context_t ctx; - int ret = vfs_fat_mount(&ctx, base_addr, byte_len); + ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len); + methods = &vfs_fat_stream_methods; + } else + #endif + #if MBOOT_VFS_LFS1 + if (elem[1] == ELEM_MOUNT_LFS1) { + ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len); + methods = &vfs_lfs1_stream_methods; + } else + #endif + #if MBOOT_VFS_LFS2 + if (elem[1] == ELEM_MOUNT_LFS2) { + ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len); + methods = &vfs_lfs2_stream_methods; + } else + #endif + { + // Unknown filesystem type + return -1; + } + + if (ret == 0) { + ret = fsload_validate_and_program_file(&ctx, methods, fname); + } + + // Flash LEDs based on success/failure of update + for (int i = 0; i < 4; ++i) { if (ret == 0) { - ret = fsload_validate_and_program_file(&ctx, &vfs_fat_stream_methods, fname); + led_state_all(7); + } else { + led_state_all(1); } - // Flash LEDs based on success/failure of update - for (int i = 0; i < 4; ++i) { - if (ret == 0) { - led_state_all(7); - } else { - led_state_all(1); - } - mp_hal_delay_ms(100); - led_state_all(0); - mp_hal_delay_ms(100); - } - return ret; + mp_hal_delay_ms(100); + led_state_all(0); + mp_hal_delay_ms(100); } - // Unknown filesystem type - return -1; + return ret; } elem += elem[-1]; } diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index b44ed772c3047..dab5fa6632e99 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -1,9 +1,13 @@ # Update Mboot or MicroPython from a .dfu.gz file on the board's filesystem -# MIT license; Copyright (c) 2019 Damien P. George +# MIT license; Copyright (c) 2019-2020 Damien P. George import struct, time import uzlib, machine, stm +# Constants to be used with update_mpy +VFS_FAT = 1 +VFS_LFS1 = 2 +VFS_LFS2 = 3 FLASH_KEY1 = 0x45670123 FLASH_KEY2 = 0xCDEF89AB @@ -152,7 +156,7 @@ def update_mboot(filename): print("Programming finished, can now reset or turn off.") -def update_mpy(filename, fs_base, fs_len): +def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT): # Check firmware is of .dfu.gz type try: with open(filename, "rb") as f: @@ -166,11 +170,8 @@ def update_mpy(filename, fs_base, fs_len): ELEM_TYPE_END = 1 ELEM_TYPE_MOUNT = 2 ELEM_TYPE_FSLOAD = 3 - ELEM_MOUNT_FAT = 1 mount_point = 1 - mount = struct.pack( - " #include @@ -42,6 +44,8 @@ enum { enum { ELEM_MOUNT_FAT = 1, + ELEM_MOUNT_LFS1, + ELEM_MOUNT_LFS2, }; extern uint8_t _estack[ELEM_DATA_SIZE]; @@ -55,3 +59,5 @@ int do_write(uint32_t addr, const uint8_t *src8, size_t len); const uint8_t *elem_search(const uint8_t *elem, uint8_t elem_id); int fsload_process(void); + +#endif // MICROPY_INCLUDED_STM32_MBOOT_MBOOT_H diff --git a/ports/stm32/mboot/vfs.h b/ports/stm32/mboot/vfs.h index 9ee1c5ff3cd42..6cf883a13975a 100644 --- a/ports/stm32/mboot/vfs.h +++ b/ports/stm32/mboot/vfs.h @@ -26,8 +26,12 @@ #ifndef MICROPY_INCLUDED_STM32_MBOOT_VFS_H #define MICROPY_INCLUDED_STM32_MBOOT_VFS_H -#include "lib/oofatfs/ff.h" #include "gzstream.h" +#include "mboot.h" + +#if MBOOT_VFS_FAT + +#include "lib/oofatfs/ff.h" typedef struct _vfs_fat_context_t { uint32_t bdev_base_addr; @@ -40,4 +44,53 @@ extern const stream_methods_t vfs_fat_stream_methods; int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len); +#endif + +#if MBOOT_VFS_LFS1 + +#include "lib/littlefs/lfs1.h" + +#define LFS_READ_SIZE (32) +#define LFS_PROG_SIZE (32) +#define LFS_LOOKAHEAD_SIZE (32) + +typedef struct _vfs_lfs1_context_t { + uint32_t bdev_base_addr; + struct lfs1_config config; + lfs1_t lfs; + struct lfs1_file_config filecfg; + uint8_t filebuf[LFS_PROG_SIZE]; + lfs1_file_t file; +} vfs_lfs1_context_t; + +extern const stream_methods_t vfs_lfs1_stream_methods; + +int vfs_lfs1_mount(vfs_lfs1_context_t *ctx, uint32_t base_addr, uint32_t byte_len); + +#endif + +#if MBOOT_VFS_LFS2 + +#include "lib/littlefs/lfs2.h" + +#define LFS_READ_SIZE (32) +#define LFS_PROG_SIZE (32) +#define LFS_CACHE_SIZE (4 * LFS_READ_SIZE) +#define LFS_LOOKAHEAD_SIZE (32) + +typedef struct _vfs_lfs2_context_t { + uint32_t bdev_base_addr; + struct lfs2_config config; + lfs2_t lfs; + struct lfs2_file_config filecfg; + uint8_t filebuf[LFS_CACHE_SIZE]; // lfs2 specific + lfs2_file_t file; +} vfs_lfs2_context_t; + +extern const stream_methods_t vfs_lfs2_stream_methods; + +int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, uint32_t base_addr, uint32_t byte_len); + +#endif + #endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H diff --git a/ports/stm32/mboot/vfs_fat.c b/ports/stm32/mboot/vfs_fat.c index 20994c8b4c00d..20de074f0dcf0 100644 --- a/ports/stm32/mboot/vfs_fat.c +++ b/ports/stm32/mboot/vfs_fat.c @@ -30,7 +30,7 @@ #include "mboot.h" #include "vfs.h" -#if MBOOT_FSLOAD +#if MBOOT_FSLOAD && MBOOT_VFS_FAT #if FF_MAX_SS == FF_MIN_SS #define SECSIZE (FF_MIN_SS) @@ -119,4 +119,4 @@ const stream_methods_t vfs_fat_stream_methods = { vfs_fat_stream_read, }; -#endif // MBOOT_FSLOAD +#endif // MBOOT_FSLOAD && MBOOT_VFS_FAT diff --git a/ports/stm32/mboot/vfs_lfs.c b/ports/stm32/mboot/vfs_lfs.c new file mode 100644 index 0000000000000..dec7c015fc087 --- /dev/null +++ b/ports/stm32/mboot/vfs_lfs.c @@ -0,0 +1,177 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/mphal.h" +#include "mboot.h" +#include "vfs.h" + +#if MBOOT_FSLOAD && (MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2) + +#if MBOOT_VFS_LFS1 +#if MBOOT_VFS_LFS2 +#error Unsupported +#endif + +#define LFSx_MACRO(s) LFS1##s +#define LFSx_API(x) lfs1_ ## x +#define VFS_LFSx_CONTEXT_T vfs_lfs1_context_t +#define VFS_LFSx_MOUNT vfs_lfs1_mount +#define VFS_LFSx_STREAM_METHODS vfs_lfs1_stream_methods + +#define SUPERBLOCK_MAGIC_OFFSET (40) +#define SUPERBLOCK_BLOCK_SIZE_OFFSET (28) +#define SUPERBLOCK_BLOCK_COUNT_OFFSET (32) + +static uint8_t lfs_read_buffer[LFS_READ_SIZE]; +static uint8_t lfs_prog_buffer[LFS_PROG_SIZE]; +static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE / 8]; + +#else + +#define LFSx_MACRO(s) LFS2##s +#define LFSx_API(x) lfs2_ ## x +#define VFS_LFSx_CONTEXT_T vfs_lfs2_context_t +#define VFS_LFSx_MOUNT vfs_lfs2_mount +#define VFS_LFSx_STREAM_METHODS vfs_lfs2_stream_methods + +#define SUPERBLOCK_MAGIC_OFFSET (8) +#define SUPERBLOCK_BLOCK_SIZE_OFFSET (24) +#define SUPERBLOCK_BLOCK_COUNT_OFFSET (28) + +static uint8_t lfs_read_buffer[LFS_CACHE_SIZE]; +static uint8_t lfs_prog_buffer[LFS_CACHE_SIZE]; +static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE]; + +#endif + +static int dev_read(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) { + VFS_LFSx_CONTEXT_T *ctx = c->context; + if (0 <= block && block < ctx->config.block_count) { + do_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer); + return LFSx_MACRO(_ERR_OK); + } + return LFSx_MACRO(_ERR_IO); +} + +static int dev_prog(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, const void *buffer, LFSx_API(size_t) size) { + return LFSx_MACRO(_ERR_IO); +} + +static int dev_erase(const struct LFSx_API (config) * c, LFSx_API(block_t) block) { + return LFSx_MACRO(_ERR_IO); +} + +static int dev_sync(const struct LFSx_API (config) * c) { + return LFSx_MACRO(_ERR_OK); +} + +int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_len) { + // Read start of superblock. + uint8_t buf[48]; + do_read(base_addr, sizeof(buf), buf); + + // Verify littlefs and detect block size. + if (memcmp(&buf[SUPERBLOCK_MAGIC_OFFSET], "littlefs", 8) != 0) { + return -1; + } + uint32_t block_size = get_le32(&buf[SUPERBLOCK_BLOCK_SIZE_OFFSET]); + uint32_t block_count = get_le32(&buf[SUPERBLOCK_BLOCK_COUNT_OFFSET]); + + // Verify size of volume. + if (block_size * block_count != byte_len) { + return -1; + } + + ctx->bdev_base_addr = base_addr; + + struct LFSx_API (config) *config = &ctx->config; + memset(config, 0, sizeof(*config)); + + config->context = ctx; + + config->read = dev_read; + config->prog = dev_prog; + config->erase = dev_erase; + config->sync = dev_sync; + + config->read_size = LFS_READ_SIZE; + config->prog_size = LFS_PROG_SIZE; + config->block_size = block_size; + config->block_count = byte_len / block_size; + + #if MBOOT_VFS_LFS1 + config->lookahead = LFS_LOOKAHEAD_SIZE; + config->read_buffer = lfs_read_buffer; + config->prog_buffer = lfs_prog_buffer; + config->lookahead_buffer = lfs_lookahead_buffer; + #else + config->block_cycles = 100; + config->cache_size = LFS_CACHE_SIZE; + config->lookahead_size = LFS_LOOKAHEAD_SIZE; + config->read_buffer = lfs_read_buffer; + config->prog_buffer = lfs_prog_buffer; + config->lookahead_buffer = lfs_lookahead_buffer; + #endif + + int ret = LFSx_API(mount)(&ctx->lfs, &ctx->config); + if (ret < 0) { + return -1; + } + return 0; +} + +static int vfs_lfs_stream_open(void *stream_in, const char *fname) { + VFS_LFSx_CONTEXT_T *ctx = stream_in; + memset(&ctx->file, 0, sizeof(ctx->file)); + memset(&ctx->filecfg, 0, sizeof(ctx->filecfg)); + ctx->filecfg.buffer = &ctx->filebuf[0]; + LFSx_API(file_opencfg)(&ctx->lfs, &ctx->file, fname, LFSx_MACRO(_O_RDONLY), &ctx->filecfg); + return 0; +} + +static void vfs_lfs_stream_close(void *stream_in) { + VFS_LFSx_CONTEXT_T *ctx = stream_in; + LFSx_API(file_close)(&ctx->lfs, &ctx->file); +} + +static int vfs_lfs_stream_read(void *stream_in, uint8_t *buf, size_t len) { + VFS_LFSx_CONTEXT_T *ctx = stream_in; + LFSx_API(ssize_t) sz = LFSx_API(file_read)(&ctx->lfs, &ctx->file, buf, len); + if (sz < 0) { + return -1; + } + return sz; +} + +const stream_methods_t VFS_LFSx_STREAM_METHODS = { + vfs_lfs_stream_open, + vfs_lfs_stream_close, + vfs_lfs_stream_read, +}; + +#endif // MBOOT_FSLOAD && (MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2) From 0a8ce0d56899b02a18ee4260446d31e732fe71e8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jun 2020 21:26:06 +1000 Subject: [PATCH 120/352] stm32/mboot: Update README to describe WB and littlefs support. Signed-off-by: Damien George --- ports/stm32/mboot/README.md | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index 52bbf55675414..d8aa6d456f19e 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -2,11 +2,11 @@ Mboot - MicroPython boot loader =============================== Mboot is a custom bootloader for STM32 MCUs, and currently supports the -STM32F4xx and STM32F7xx families. It can provide a standard USB DFU interface -on either the FS or HS peripherals, as well as a sophisticated, custom I2C +STM32F4xx, STM32F7xx and STM32WBxx families. It can provide a standard USB DFU +interface on either the FS or HS peripherals, as well as a sophisticated, custom I2C interface. It can also load and program firmware in .dfu.gz format from a -filesystem. It can fit in 16k of flash space, but all features enabled requires -32k. +filesystem, either FAT, littlefs 1 or littlfs 2. +It can fit in 16k of flash space, but all features enabled requires 32k. How to use ---------- @@ -63,6 +63,15 @@ How to use #define MBOOT_FSLOAD (1) + and then enable one or more of the following depending on what filesystem + support is required in Mboot (note that the FAT driver is read-only and + quite compact, but littlefs supports both read and write so is rather + large): + + #define MBOOT_VFS_FAT (1) + #define MBOOT_VFS_LFS1 (1) + #define MBOOT_VFS_LFS2 (1) + 2. Build the board's main application firmware as usual. 3. Build mboot via: @@ -133,10 +142,18 @@ are located and what filename to program. The elements to use are: `u32` means unsigned 32-bit little-endian integer. The firmware to load must be a gzip'd DfuSe file (.dfu.gz) and stored within a -FAT formatted partition. +FAT or littlefs formatted partition. The provided fwupdate.py script contains helper functions to call into Mboot -with the correct data, and also to update Mboot itself. +with the correct data, and also to update Mboot itself. For example on PYBD +the following will update the main MicroPython firmware from the file +firmware.dfu.gz stored on the default FAT filesystem: + + import fwupdate + fwupdate.update_mpy('firmware.dfu.gz', 0x80000000, 2 * 1024 * 1024) + +The 0x80000000 value is the address understood by Mboot as the location of +the external SPI flash, configured via `MBOOT_SPIFLASH_ADDR`. Example: Mboot on PYBv1.x ------------------------- From 6475cdb7d06cc1a7bf273e66d0bbfc9aef890622 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jun 2020 21:29:32 +1000 Subject: [PATCH 121/352] travis: Build mboot for PYBV10 with LFS2 enabled in stm32 job. Signed-off-by: Damien George --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index b7aac0bd0d0fc..f6fe3e1e4e39f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,6 +78,7 @@ jobs: - make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L073RZ - make ${MAKEOPTS} -C ports/stm32 BOARD=STM32L476DISC - make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WB55 + - make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' - make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 - make ${MAKEOPTS} -C ports/stm32/mboot BOARD=NUCLEO_WB55 From 137df817575e06b7bd765fb230a99d108f1d4f61 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jun 2020 23:56:45 +1000 Subject: [PATCH 122/352] stm32/i2cslave: Pass I2C instance to callbacks to support multi I2Cs. By passing through the I2C instance to the application callbacks, the application can implement multiple I2C slave devices on different peripherals (eg I2C1 and I2C2). This commit also adds a proper rw argument to i2c_slave_process_addr_match for F7/H7/WB MCUs, and enables the i2c_slave_process_tx_end callback. Mboot is also updated for these changes. Signed-off-by: Damien George --- ports/stm32/i2cslave.c | 18 +++++++++--------- ports/stm32/i2cslave.h | 9 +++++---- ports/stm32/mboot/main.c | 11 +++++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/ports/stm32/i2cslave.c b/ports/stm32/i2cslave.c index 149e105ee8207..a575c53085168 100644 --- a/ports/stm32/i2cslave.c +++ b/ports/stm32/i2cslave.c @@ -42,20 +42,20 @@ void i2c_slave_ev_irq_handler(i2c_slave_t *i2c) { // Read of SR1, SR2 needed to clear ADDR bit sr1 = i2c->SR1; uint32_t sr2 = i2c->SR2; - i2c_slave_process_addr_match((sr2 >> I2C_SR2_TRA_Pos) & 1); + i2c_slave_process_addr_match(i2c, (sr2 >> I2C_SR2_TRA_Pos) & 1); } if (sr1 & I2C_SR1_TXE) { - i2c->DR = i2c_slave_process_tx_byte(); + i2c->DR = i2c_slave_process_tx_byte(i2c); } if (sr1 & I2C_SR1_RXNE) { - i2c_slave_process_rx_byte(i2c->DR); + i2c_slave_process_rx_byte(i2c, i2c->DR); } if (sr1 & I2C_SR1_STOPF) { // STOPF only set at end of RX mode (in TX mode AF is set on NACK) // Read of SR1, write CR1 needed to clear STOPF bit sr1 = i2c->SR1; i2c->CR1 &= ~I2C_CR1_ACK; - i2c_slave_process_rx_end(); + i2c_slave_process_rx_end(i2c); i2c->CR1 |= I2C_CR1_ACK; } } @@ -77,22 +77,22 @@ void i2c_slave_ev_irq_handler(i2c_slave_t *i2c) { // Set TXE so that TXDR is flushed and ready for the first byte i2c->ISR = I2C_ISR_TXE; i2c->ICR = I2C_ICR_ADDRCF; - i2c_slave_process_addr_match(0); + i2c_slave_process_addr_match(i2c, (i2c->ISR >> I2C_ISR_DIR_Pos) & 1); } if (isr & I2C_ISR_TXIS) { - i2c->TXDR = i2c_slave_process_tx_byte(); + i2c->TXDR = i2c_slave_process_tx_byte(i2c); } if (isr & I2C_ISR_RXNE) { - i2c_slave_process_rx_byte(i2c->RXDR); + i2c_slave_process_rx_byte(i2c, i2c->RXDR); } if (isr & I2C_ISR_STOPF) { // STOPF only set for STOP condition, not a repeated START i2c->ICR = I2C_ICR_STOPCF; i2c->OAR1 &= ~I2C_OAR1_OA1EN; if (i2c->ISR & I2C_ISR_DIR) { - // i2c_slave_process_tx_end(); + i2c_slave_process_tx_end(i2c); } else { - i2c_slave_process_rx_end(); + i2c_slave_process_rx_end(i2c); } i2c->OAR1 |= I2C_OAR1_OA1EN; } diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h index f335c9291226c..4a51bf378e924 100644 --- a/ports/stm32/i2cslave.h +++ b/ports/stm32/i2cslave.h @@ -67,9 +67,10 @@ static inline void i2c_slave_shutdown(i2c_slave_t *i2c, int irqn) { void i2c_slave_ev_irq_handler(i2c_slave_t *i2c); // These should be provided externally -int i2c_slave_process_addr_match(int rw); -int i2c_slave_process_rx_byte(uint8_t val); -void i2c_slave_process_rx_end(void); -uint8_t i2c_slave_process_tx_byte(void); +int i2c_slave_process_addr_match(i2c_slave_t *i2c, int rw); +int i2c_slave_process_rx_byte(i2c_slave_t *i2c, uint8_t val); +void i2c_slave_process_rx_end(i2c_slave_t *i2c); +uint8_t i2c_slave_process_tx_byte(i2c_slave_t *i2c); +void i2c_slave_process_tx_end(i2c_slave_t *i2c); #endif // MICROPY_INCLUDED_STM32_I2CSLAVE_H diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index b3e63ccc26626..87618e7f410ad 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -664,7 +664,7 @@ void i2c_init(int addr) { i2c_slave_init(MBOOT_I2Cx, I2Cx_EV_IRQn, IRQ_PRI_I2C, addr); } -int i2c_slave_process_addr_match(int rw) { +int i2c_slave_process_addr_match(i2c_slave_t *i2c, int rw) { if (i2c_obj.cmd_arg_sent) { i2c_obj.cmd_send_arg = false; } @@ -672,14 +672,14 @@ int i2c_slave_process_addr_match(int rw) { return 0; // ACK } -int i2c_slave_process_rx_byte(uint8_t val) { +int i2c_slave_process_rx_byte(i2c_slave_t *i2c, uint8_t val) { if (i2c_obj.cmd_buf_pos < sizeof(i2c_obj.cmd_buf)) { i2c_obj.cmd_buf[i2c_obj.cmd_buf_pos++] = val; } return 0; // ACK } -void i2c_slave_process_rx_end(void) { +void i2c_slave_process_rx_end(i2c_slave_t *i2c) { if (i2c_obj.cmd_buf_pos == 0) { return; } @@ -764,7 +764,7 @@ void i2c_slave_process_rx_end(void) { i2c_obj.cmd_arg_sent = false; } -uint8_t i2c_slave_process_tx_byte(void) { +uint8_t i2c_slave_process_tx_byte(i2c_slave_t *i2c) { if (i2c_obj.cmd_send_arg) { i2c_obj.cmd_arg_sent = true; return i2c_obj.cmd_arg; @@ -775,6 +775,9 @@ uint8_t i2c_slave_process_tx_byte(void) { } } +void i2c_slave_process_tx_end(i2c_slave_t *i2c) { +} + #endif // defined(MBOOT_I2C_SCL) /******************************************************************************/ From aa26fe62d8728c95d447475ced9b4a03380025ea Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Jun 2020 13:09:07 +1000 Subject: [PATCH 123/352] py/asm: Add funcs/macros to emit machine code for logical-shift-right. Signed-off-by: Damien George --- py/asmarm.c | 5 +++++ py/asmarm.h | 2 ++ py/asmthumb.h | 1 + py/asmx64.c | 5 +++++ py/asmx64.h | 2 ++ py/asmx86.c | 5 +++++ py/asmx86.h | 2 ++ py/asmxtensa.h | 9 +++++++++ 8 files changed, 31 insertions(+) diff --git a/py/asmarm.c b/py/asmarm.c index 72b37f73a02f5..e91421578b274 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -303,6 +303,11 @@ void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs) { emit_al(as, 0x1a00010 | (rd << 12) | (rs << 8) | rd); } +void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs) { + // mov rd, rd, lsr rs + emit_al(as, 0x1a00030 | (rd << 12) | (rs << 8) | rd); +} + void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) { // mov rd, rd, asr rs emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd); diff --git a/py/asmarm.h b/py/asmarm.h index 825fd884005ff..46da661faa9ba 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -101,6 +101,7 @@ void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm); void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num); void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label); void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs); +void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs); void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs); // memory @@ -187,6 +188,7 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); #define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_arm_mov_reg_pcrel((as), (reg_dest), (label)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_arm_lsr_reg_reg((as), (reg_dest), (reg_shift)) #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift)) #define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) diff --git a/py/asmthumb.h b/py/asmthumb.h index 6d0ee4b76e50e..9e6f242ccaeb3 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -345,6 +345,7 @@ void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenien #define ASM_MOV_REG_PCREL(as, rlo_dest, label) asm_thumb_mov_reg_pcrel((as), (rlo_dest), (label)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift)) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSR, (reg_dest), (reg_shift)) #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift)) #define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src)) #define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src)) diff --git a/py/asmx64.c b/py/asmx64.c index 723671d5a3558..fd64eaf98b1c1 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -67,6 +67,7 @@ // #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */ // #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */ #define OPCODE_SHL_RM64_CL (0xd3) /* /4 */ +#define OPCODE_SHR_RM64_CL (0xd3) /* /5 */ #define OPCODE_SAR_RM64_CL (0xd3) /* /7 */ // #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */ // #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */ @@ -382,6 +383,10 @@ void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64) { asm_x64_generic_r64_r64(as, dest_r64, 4, OPCODE_SHL_RM64_CL); } +void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64) { + asm_x64_generic_r64_r64(as, dest_r64, 5, OPCODE_SHR_RM64_CL); +} + void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64) { asm_x64_generic_r64_r64(as, dest_r64, 7, OPCODE_SAR_RM64_CL); } diff --git a/py/asmx64.h b/py/asmx64.h index 28b1bd255fd21..e73e3c5c57b87 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -98,6 +98,7 @@ void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64); +void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64); void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64); void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64); @@ -190,6 +191,7 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); #define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x64_mov_reg_pcrel((as), (reg_dest), (label)) #define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg)) +#define ASM_LSR_REG(as, reg) asm_x64_shr_r64_cl((as), (reg)) #define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg)) #define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src)) #define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src)) diff --git a/py/asmx86.c b/py/asmx86.c index 96de372aec0c2..4b0f8047f6eb7 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -67,6 +67,7 @@ // #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */ // #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */ #define OPCODE_SHL_RM32_CL (0xd3) /* /4 */ +#define OPCODE_SHR_RM32_CL (0xd3) /* /5 */ #define OPCODE_SAR_RM32_CL (0xd3) /* /7 */ // #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */ // #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */ @@ -259,6 +260,10 @@ void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32) { asm_x86_generic_r32_r32(as, dest_r32, 4, OPCODE_SHL_RM32_CL); } +void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32) { + asm_x86_generic_r32_r32(as, dest_r32, 5, OPCODE_SHR_RM32_CL); +} + void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32) { asm_x86_generic_r32_r32(as, dest_r32, 7, OPCODE_SAR_RM32_CL); } diff --git a/py/asmx86.h b/py/asmx86.h index 4855cd7ee4e8a..f28040abf444c 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -93,6 +93,7 @@ void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32); +void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32); void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32); void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32); @@ -185,6 +186,7 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r #define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x86_mov_reg_pcrel((as), (reg_dest), (label)) #define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg)) +#define ASM_LSR_REG(as, reg) asm_x86_shr_r32_cl((as), (reg)) #define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg)) #define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src)) #define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src)) diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 5eb40daf7887c..43f1b608edd1c 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -243,6 +243,10 @@ static inline void asm_xtensa_op_sll(asm_xtensa_t *as, uint reg_dest, uint reg_s asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, reg_dest, reg_src, 0)); } +static inline void asm_xtensa_op_srl(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 9, reg_dest, 0, reg_src)); +} + static inline void asm_xtensa_op_sra(asm_xtensa_t *as, uint reg_dest, uint reg_src) { asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, reg_dest, 0, reg_src)); } @@ -372,6 +376,11 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); asm_xtensa_op_ssl((as), (reg_shift)); \ asm_xtensa_op_sll((as), (reg_dest), (reg_dest)); \ } while (0) +#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssr((as), (reg_shift)); \ + asm_xtensa_op_srl((as), (reg_dest), (reg_dest)); \ + } while (0) #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) \ do { \ asm_xtensa_op_ssr((as), (reg_shift)); \ From b3b8706d27cffbfc4cdd447b204ae7083283d13c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jun 2020 18:26:01 +1000 Subject: [PATCH 124/352] py/asm: Add condition codes for signed comparisons. Signed-off-by: Damien George --- py/asmthumb.h | 7 +++++++ py/asmx64.h | 3 +++ py/asmx86.h | 3 +++ 3 files changed, 13 insertions(+) diff --git a/py/asmthumb.h b/py/asmthumb.h index 9e6f242ccaeb3..17b694a74dfdd 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -80,12 +80,19 @@ void asm_thumb_exit(asm_thumb_t *as); #define ASM_THUMB_OP_IT (0xbf00) #define ASM_THUMB_OP_ITE_EQ (0xbf0c) +#define ASM_THUMB_OP_ITE_NE (0xbf14) #define ASM_THUMB_OP_ITE_CS (0xbf2c) +#define ASM_THUMB_OP_ITE_CC (0xbf34) #define ASM_THUMB_OP_ITE_MI (0xbf4c) +#define ASM_THUMB_OP_ITE_PL (0xbf54) #define ASM_THUMB_OP_ITE_VS (0xbf6c) +#define ASM_THUMB_OP_ITE_VC (0xbf74) #define ASM_THUMB_OP_ITE_HI (0xbf8c) +#define ASM_THUMB_OP_ITE_LS (0xbf94) #define ASM_THUMB_OP_ITE_GE (0xbfac) +#define ASM_THUMB_OP_ITE_LT (0xbfb4) #define ASM_THUMB_OP_ITE_GT (0xbfcc) +#define ASM_THUMB_OP_ITE_LE (0xbfd4) #define ASM_THUMB_OP_NOP (0xbf00) #define ASM_THUMB_OP_WFI (0xbf30) diff --git a/py/asmx64.h b/py/asmx64.h index e73e3c5c57b87..1a4987f5cbbaf 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -61,10 +61,13 @@ // condition codes, used for jcc and setcc (despite their j-name!) #define ASM_X64_CC_JB (0x2) // below, unsigned +#define ASM_X64_CC_JAE (0x3) // above or equal, unsigned #define ASM_X64_CC_JZ (0x4) #define ASM_X64_CC_JE (0x4) #define ASM_X64_CC_JNZ (0x5) #define ASM_X64_CC_JNE (0x5) +#define ASM_X64_CC_JBE (0x6) // below or equal, unsigned +#define ASM_X64_CC_JA (0x7) // above, unsigned #define ASM_X64_CC_JL (0xc) // less, signed #define ASM_X64_CC_JGE (0xd) // greater or equal, signed #define ASM_X64_CC_JLE (0xe) // less or equal, signed diff --git a/py/asmx86.h b/py/asmx86.h index f28040abf444c..8f1b06d220e83 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -63,10 +63,13 @@ // condition codes, used for jcc and setcc (despite their j-name!) #define ASM_X86_CC_JB (0x2) // below, unsigned +#define ASM_X86_CC_JAE (0x3) // above or equal, unsigned #define ASM_X86_CC_JZ (0x4) #define ASM_X86_CC_JE (0x4) #define ASM_X86_CC_JNZ (0x5) #define ASM_X86_CC_JNE (0x5) +#define ASM_X86_CC_JBE (0x6) // below or equal, unsigned +#define ASM_X86_CC_JA (0x7) // above, unsigned #define ASM_X86_CC_JL (0xc) // less, signed #define ASM_X86_CC_JGE (0xd) // greater or equal, signed #define ASM_X86_CC_JLE (0xe) // less or equal, signed From 41fa8b5482089bdd7fa5478fe24f32913b23967c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Jun 2020 18:26:39 +1000 Subject: [PATCH 125/352] py/emitnative: Implement binary operations for viper uint operands. uint types in viper mode can now be used for all binary operators except floor-divide and modulo. Fixes issue #1847 and issue #6177. Signed-off-by: Damien George --- py/emitnative.c | 113 +++++++++++++----- tests/micropython/viper_binop_arith_uint.py | 32 +++++ .../micropython/viper_binop_arith_uint.py.exp | 10 ++ tests/micropython/viper_binop_bitwise_uint.py | 58 +++++++++ .../viper_binop_bitwise_uint.py.exp | 22 ++++ tests/micropython/viper_binop_comp_uint.py | 31 +++++ .../micropython/viper_binop_comp_uint.py.exp | 6 + tests/micropython/viper_error.py | 3 + tests/micropython/viper_error.py.exp | 3 + 9 files changed, 251 insertions(+), 27 deletions(-) create mode 100644 tests/micropython/viper_binop_arith_uint.py create mode 100644 tests/micropython/viper_binop_arith_uint.py.exp create mode 100644 tests/micropython/viper_binop_bitwise_uint.py create mode 100644 tests/micropython/viper_binop_bitwise_uint.py.exp create mode 100644 tests/micropython/viper_binop_comp_uint.py create mode 100644 tests/micropython/viper_binop_comp_uint.py.exp diff --git a/py/emitnative.c b/py/emitnative.c index 6c8e9feebaee9..2a657b6964daa 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -2289,7 +2289,8 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { DEBUG_printf("binary_op(" UINT_FMT ")\n", op); vtype_kind_t vtype_lhs = peek_vtype(emit, 1); vtype_kind_t vtype_rhs = peek_vtype(emit, 0); - if (vtype_lhs == VTYPE_INT && vtype_rhs == VTYPE_INT) { + if ((vtype_lhs == VTYPE_INT || vtype_lhs == VTYPE_UINT) + && (vtype_rhs == VTYPE_INT || vtype_rhs == VTYPE_UINT)) { // for integers, inplace and normal ops are equivalent, so use just normal ops if (MP_BINARY_OP_INPLACE_OR <= op && op <= MP_BINARY_OP_INPLACE_POWER) { op += MP_BINARY_OP_OR - MP_BINARY_OP_INPLACE_OR; @@ -2306,9 +2307,13 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { if (op == MP_BINARY_OP_LSHIFT) { ASM_LSL_REG(emit->as, REG_RET); } else { - ASM_ASR_REG(emit->as, REG_RET); + if (vtype_lhs == VTYPE_UINT) { + ASM_LSR_REG(emit->as, REG_RET); + } else { + ASM_ASR_REG(emit->as, REG_RET); + } } - emit_post_push_reg(emit, VTYPE_INT, REG_RET); + emit_post_push_reg(emit, vtype_lhs, REG_RET); return; } #endif @@ -2316,6 +2321,10 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { // special cases for floor-divide and module because we dispatch to helper functions if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_MODULO) { emit_pre_pop_reg_reg(emit, &vtype_rhs, REG_ARG_2, &vtype_lhs, REG_ARG_1); + if (vtype_lhs != VTYPE_INT) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("div/mod not implemented for uint"), mp_binary_op_method_name[op]); + } if (op == MP_BINARY_OP_FLOOR_DIVIDE) { emit_call(emit, MP_F_SMALL_INT_FLOOR_DIVIDE); } else { @@ -2334,31 +2343,35 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { if (op == MP_BINARY_OP_LSHIFT) { ASM_LSL_REG_REG(emit->as, REG_ARG_2, reg_rhs); } else { - ASM_ASR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + if (vtype_lhs == VTYPE_UINT) { + ASM_LSR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + } else { + ASM_ASR_REG_REG(emit->as, REG_ARG_2, reg_rhs); + } } - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); return; } #endif if (op == MP_BINARY_OP_OR) { ASM_OR_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (op == MP_BINARY_OP_XOR) { ASM_XOR_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (op == MP_BINARY_OP_AND) { ASM_AND_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (op == MP_BINARY_OP_ADD) { ASM_ADD_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (op == MP_BINARY_OP_SUBTRACT) { ASM_SUB_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (op == MP_BINARY_OP_MULTIPLY) { ASM_MUL_REG_REG(emit->as, REG_ARG_2, reg_rhs); - emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); + emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); } else if (MP_BINARY_OP_LESS <= op && op <= MP_BINARY_OP_NOT_EQUAL) { // comparison ops are (in enum order): // MP_BINARY_OP_LESS @@ -2367,11 +2380,26 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { // MP_BINARY_OP_LESS_EQUAL // MP_BINARY_OP_MORE_EQUAL // MP_BINARY_OP_NOT_EQUAL + + if (vtype_lhs != vtype_rhs) { + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("comparison of int and uint")); + } + + size_t op_idx = op - MP_BINARY_OP_LESS + (vtype_lhs == VTYPE_UINT ? 0 : 6); + need_reg_single(emit, REG_RET, 0); #if N_X64 asm_x64_xor_r64_r64(emit->as, REG_RET, REG_RET); asm_x64_cmp_r64_with_r64(emit->as, reg_rhs, REG_ARG_2); - static byte ops[6] = { + static byte ops[6 + 6] = { + // unsigned + ASM_X64_CC_JB, + ASM_X64_CC_JA, + ASM_X64_CC_JE, + ASM_X64_CC_JBE, + ASM_X64_CC_JAE, + ASM_X64_CC_JNE, + // signed ASM_X64_CC_JL, ASM_X64_CC_JG, ASM_X64_CC_JE, @@ -2379,11 +2407,19 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_X64_CC_JGE, ASM_X64_CC_JNE, }; - asm_x64_setcc_r8(emit->as, ops[op - MP_BINARY_OP_LESS], REG_RET); + asm_x64_setcc_r8(emit->as, ops[op_idx], REG_RET); #elif N_X86 asm_x86_xor_r32_r32(emit->as, REG_RET, REG_RET); asm_x86_cmp_r32_with_r32(emit->as, reg_rhs, REG_ARG_2); - static byte ops[6] = { + static byte ops[6 + 6] = { + // unsigned + ASM_X86_CC_JB, + ASM_X86_CC_JA, + ASM_X86_CC_JE, + ASM_X86_CC_JBE, + ASM_X86_CC_JAE, + ASM_X86_CC_JNE, + // signed ASM_X86_CC_JL, ASM_X86_CC_JG, ASM_X86_CC_JE, @@ -2391,24 +2427,39 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_X86_CC_JGE, ASM_X86_CC_JNE, }; - asm_x86_setcc_r8(emit->as, ops[op - MP_BINARY_OP_LESS], REG_RET); + asm_x86_setcc_r8(emit->as, ops[op_idx], REG_RET); #elif N_THUMB asm_thumb_cmp_rlo_rlo(emit->as, REG_ARG_2, reg_rhs); - static uint16_t ops[6] = { - ASM_THUMB_OP_ITE_GE, - ASM_THUMB_OP_ITE_GT, + static uint16_t ops[6 + 6] = { + // unsigned + ASM_THUMB_OP_ITE_CC, + ASM_THUMB_OP_ITE_HI, ASM_THUMB_OP_ITE_EQ, + ASM_THUMB_OP_ITE_LS, + ASM_THUMB_OP_ITE_CS, + ASM_THUMB_OP_ITE_NE, + // signed + ASM_THUMB_OP_ITE_LT, ASM_THUMB_OP_ITE_GT, - ASM_THUMB_OP_ITE_GE, ASM_THUMB_OP_ITE_EQ, + ASM_THUMB_OP_ITE_LE, + ASM_THUMB_OP_ITE_GE, + ASM_THUMB_OP_ITE_NE, }; - static byte ret[6] = { 0, 1, 1, 0, 1, 0, }; - asm_thumb_op16(emit->as, ops[op - MP_BINARY_OP_LESS]); - asm_thumb_mov_rlo_i8(emit->as, REG_RET, ret[op - MP_BINARY_OP_LESS]); - asm_thumb_mov_rlo_i8(emit->as, REG_RET, ret[op - MP_BINARY_OP_LESS] ^ 1); + asm_thumb_op16(emit->as, ops[op_idx]); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); #elif N_ARM asm_arm_cmp_reg_reg(emit->as, REG_ARG_2, reg_rhs); - static uint ccs[6] = { + static uint ccs[6 + 6] = { + // unsigned + ASM_ARM_CC_CC, + ASM_ARM_CC_HI, + ASM_ARM_CC_EQ, + ASM_ARM_CC_LS, + ASM_ARM_CC_CS, + ASM_ARM_CC_NE, + // signed ASM_ARM_CC_LT, ASM_ARM_CC_GT, ASM_ARM_CC_EQ, @@ -2416,9 +2467,17 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_ARM_CC_GE, ASM_ARM_CC_NE, }; - asm_arm_setcc_reg(emit->as, REG_RET, ccs[op - MP_BINARY_OP_LESS]); + asm_arm_setcc_reg(emit->as, REG_RET, ccs[op_idx]); #elif N_XTENSA || N_XTENSAWIN - static uint8_t ccs[6] = { + static uint8_t ccs[6 + 6] = { + // unsigned + ASM_XTENSA_CC_LTU, + 0x80 | ASM_XTENSA_CC_LTU, // for GTU we'll swap args + ASM_XTENSA_CC_EQ, + 0x80 | ASM_XTENSA_CC_GEU, // for LEU we'll swap args + ASM_XTENSA_CC_GEU, + ASM_XTENSA_CC_NE, + // signed ASM_XTENSA_CC_LT, 0x80 | ASM_XTENSA_CC_LT, // for GT we'll swap args ASM_XTENSA_CC_EQ, @@ -2426,7 +2485,7 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_XTENSA_CC_GE, ASM_XTENSA_CC_NE, }; - uint8_t cc = ccs[op - MP_BINARY_OP_LESS]; + uint8_t cc = ccs[op_idx]; if ((cc & 0x80) == 0) { asm_xtensa_setcc_reg_reg_reg(emit->as, cc, REG_RET, REG_ARG_2, reg_rhs); } else { diff --git a/tests/micropython/viper_binop_arith_uint.py b/tests/micropython/viper_binop_arith_uint.py new file mode 100644 index 0000000000000..e4270a10a79a9 --- /dev/null +++ b/tests/micropython/viper_binop_arith_uint.py @@ -0,0 +1,32 @@ +# test arithmetic operators with uint type + + +@micropython.viper +def add(x: uint, y: uint): + return x + y, y + x + + +print("add") +print(*add(1, 2)) +print(*(x & 0xFFFFFFFF for x in add(-1, -2))) + + +@micropython.viper +def sub(x: uint, y: uint): + return x - y, y - x + + +print("sub") +print(*(x & 0xFFFFFFFF for x in sub(1, 2))) +print(*(x & 0xFFFFFFFF for x in sub(-1, -2))) + + +@micropython.viper +def mul(x: uint, y: uint): + return x * y, y * x + + +print("mul") +print(*mul(2, 3)) +print(*(x & 0xFFFFFFFF for x in mul(2, -3))) +print(*mul(-2, -3)) diff --git a/tests/micropython/viper_binop_arith_uint.py.exp b/tests/micropython/viper_binop_arith_uint.py.exp new file mode 100644 index 0000000000000..72f84b716a4dd --- /dev/null +++ b/tests/micropython/viper_binop_arith_uint.py.exp @@ -0,0 +1,10 @@ +add +3 3 +4294967293 4294967293 +sub +4294967295 1 +1 4294967295 +mul +6 6 +4294967290 4294967290 +6 6 diff --git a/tests/micropython/viper_binop_bitwise_uint.py b/tests/micropython/viper_binop_bitwise_uint.py new file mode 100644 index 0000000000000..3bc7ba8d1113f --- /dev/null +++ b/tests/micropython/viper_binop_bitwise_uint.py @@ -0,0 +1,58 @@ +# test bitwise operators on uint type + + +@micropython.viper +def shl(x: uint, y: uint) -> uint: + return x << y + + +print("shl") +print(shl(1, 0)) +print(shl(1, 30)) +print(shl(-1, 10) & 0xFFFFFFFF) + + +@micropython.viper +def shr(x: uint, y: uint) -> uint: + return x >> y + + +print("shr") +print(shr(1, 0)) +print(shr(16, 3)) +print(shr(-1, 1) in (0x7FFFFFFF, 0x7FFFFFFF_FFFFFFFF)) + + +@micropython.viper +def and_(x: uint, y: uint): + return x & y, y & x + + +print("and") +print(*and_(1, 0)) +print(*and_(1, 3)) +print(*and_(-1, 2)) +print(*(x & 0xFFFFFFFF for x in and_(-1, -2))) + + +@micropython.viper +def or_(x: uint, y: uint): + return x | y, y | x + + +print("or") +print(*or_(1, 0)) +print(*or_(1, 2)) +print(*(x & 0xFFFFFFFF for x in or_(-1, 2))) + + +@micropython.viper +def xor(x: uint, y: uint): + return x ^ y, y ^ x + + +print("xor") +print(*xor(1, 0)) +print(*xor(1, 3)) +print(*(x & 0xFFFFFFFF for x in xor(-1, 3))) +print(*xor(-1, -3)) diff --git a/tests/micropython/viper_binop_bitwise_uint.py.exp b/tests/micropython/viper_binop_bitwise_uint.py.exp new file mode 100644 index 0000000000000..3ad6469a2fc78 --- /dev/null +++ b/tests/micropython/viper_binop_bitwise_uint.py.exp @@ -0,0 +1,22 @@ +shl +1 +1073741824 +4294966272 +shr +1 +2 +True +and +0 0 +1 1 +2 2 +4294967294 4294967294 +or +1 1 +3 3 +4294967295 4294967295 +xor +1 1 +2 2 +4294967292 4294967292 +2 2 diff --git a/tests/micropython/viper_binop_comp_uint.py b/tests/micropython/viper_binop_comp_uint.py new file mode 100644 index 0000000000000..85aa32c78c733 --- /dev/null +++ b/tests/micropython/viper_binop_comp_uint.py @@ -0,0 +1,31 @@ +# test comparison operators with uint type + + +@micropython.viper +def f(x: uint, y: uint): + if x < y: + print(" <", end="") + if x > y: + print(" >", end="") + if x == y: + print(" ==", end="") + if x <= y: + print(" <=", end="") + if x >= y: + print(" >=", end="") + if x != y: + print(" !=", end="") + + +def test(a, b): + print(a, b, end="") + f(a, b) + print() + + +test(1, 1) +test(2, 1) +test(1, 2) +test(2, -1) +test(-2, 1) +test(-2, -1) diff --git a/tests/micropython/viper_binop_comp_uint.py.exp b/tests/micropython/viper_binop_comp_uint.py.exp new file mode 100644 index 0000000000000..cacce62b6e380 --- /dev/null +++ b/tests/micropython/viper_binop_comp_uint.py.exp @@ -0,0 +1,6 @@ +1 1 == <= >= +2 1 > >= != +1 2 < <= != +2 -1 < <= != +-2 1 > >= != +-2 -1 < <= != diff --git a/tests/micropython/viper_error.py b/tests/micropython/viper_error.py index 790f3d75c4f0e..80617af0c1f29 100644 --- a/tests/micropython/viper_error.py +++ b/tests/micropython/viper_error.py @@ -52,6 +52,7 @@ def f(): # can't do binary op between incompatible types test("@micropython.viper\ndef f(): 1 + []") +test("@micropython.viper\ndef f(x:int, y:uint): x < y") # can't load test("@micropython.viper\ndef f(): 1[0]") @@ -73,6 +74,8 @@ def f(): test("@micropython.viper\ndef f(x:int): ~x") # binary op not implemented +test("@micropython.viper\ndef f(x:uint, y:uint): res = x // y") +test("@micropython.viper\ndef f(x:uint, y:uint): res = x % y") test("@micropython.viper\ndef f(x:int): res = x in x") # yield (from) not implemented diff --git a/tests/micropython/viper_error.py.exp b/tests/micropython/viper_error.py.exp index da9a0ca93ee34..31c85b1d872af 100644 --- a/tests/micropython/viper_error.py.exp +++ b/tests/micropython/viper_error.py.exp @@ -6,6 +6,7 @@ ViperTypeError("local 'x' has type 'int' but source is 'object'",) ViperTypeError("can't implicitly convert 'ptr' to 'bool'",) ViperTypeError("return expected 'int' but got 'object'",) ViperTypeError("can't do binary op between 'int' and 'object'",) +ViperTypeError('comparison of int and uint',) ViperTypeError("can't load from 'int'",) ViperTypeError("can't load from 'int'",) ViperTypeError("can't store to 'int'",) @@ -17,6 +18,8 @@ ViperTypeError('must raise an object',) ViperTypeError('unary op __pos__ not implemented',) ViperTypeError('unary op __neg__ not implemented',) ViperTypeError('unary op __invert__ not implemented',) +ViperTypeError('div/mod not implemented for uint',) +ViperTypeError('div/mod not implemented for uint',) ViperTypeError('binary op not implemented',) NotImplementedError('native yield',) NotImplementedError('native yield',) From 9f911d822ee97edaa94873823db5eb10c31f5f77 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 22 Jun 2020 10:21:02 +1000 Subject: [PATCH 126/352] py/objcomplex: Add mp_obj_get_complex_maybe for use in complex bin-op. This allows complex binary operations to fail gracefully with unsupported operation rather than raising an exception, so that special methods work correctly. Signed-off-by: Damien George --- py/obj.c | 9 ++++++++- py/obj.h | 1 + py/objcomplex.c | 5 ++++- tests/float/cmath_fun.py | 6 ++++++ tests/float/complex_special_mehods.py | 15 +++++++++++++++ tests/run-tests | 1 + 6 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/float/complex_special_mehods.py diff --git a/py/obj.c b/py/obj.c index 07b1612552548..ed047acc39f76 100644 --- a/py/obj.c +++ b/py/obj.c @@ -371,7 +371,7 @@ mp_float_t mp_obj_get_float(mp_obj_t arg) { } #if MICROPY_PY_BUILTINS_COMPLEX -void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { +bool mp_obj_get_complex_maybe(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { if (arg == mp_const_false) { *real = 0; *imag = 0; @@ -392,6 +392,13 @@ void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { } else if (mp_obj_is_type(arg, &mp_type_complex)) { mp_obj_complex_get(arg, real, imag); } else { + return false; + } + return true; +} + +void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { + if (!mp_obj_get_complex_maybe(arg, real, imag)) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to complex")); #else diff --git a/py/obj.h b/py/obj.h index 590b9c4b6a4a1..1fa24eb18c903 100644 --- a/py/obj.h +++ b/py/obj.h @@ -778,6 +778,7 @@ bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); mp_float_t mp_obj_get_float(mp_obj_t self_in); bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value); void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); +bool mp_obj_get_complex_maybe(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); #endif void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items); // *items may point inside a GC block void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items); // *items may point inside a GC block diff --git a/py/objcomplex.c b/py/objcomplex.c index 91e4402309d88..f4c4aeffcb91d 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -178,7 +178,10 @@ void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag) { mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_float_t lhs_imag, mp_obj_t rhs_in) { mp_float_t rhs_real, rhs_imag; - mp_obj_get_complex(rhs_in, &rhs_real, &rhs_imag); // can be any type, this function will convert to float (if possible) + if (!mp_obj_get_complex_maybe(rhs_in, &rhs_real, &rhs_imag)) { + return MP_OBJ_NULL; // op not supported + } + switch (op) { case MP_BINARY_OP_ADD: case MP_BINARY_OP_INPLACE_ADD: diff --git a/tests/float/cmath_fun.py b/tests/float/cmath_fun.py index 7b5e692452765..15b72e7a6203b 100644 --- a/tests/float/cmath_fun.py +++ b/tests/float/cmath_fun.py @@ -57,3 +57,9 @@ if abs(real) < 1e-6: real = 0.0 print("complex(%.5g, %.5g)" % (real, ret.imag)) + +# test invalid type passed to cmath function +try: + log([]) +except TypeError: + print("TypeError") diff --git a/tests/float/complex_special_mehods.py b/tests/float/complex_special_mehods.py new file mode 100644 index 0000000000000..6789013fa6e0d --- /dev/null +++ b/tests/float/complex_special_mehods.py @@ -0,0 +1,15 @@ +# test complex interacting with special methods + + +class A: + def __add__(self, x): + print("__add__") + return 1 + + def __radd__(self, x): + print("__radd__") + return 2 + + +print(A() + 1j) +print(1j + A()) diff --git a/tests/run-tests b/tests/run-tests index f9e4de4b34a20..102b0f7790e14 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -355,6 +355,7 @@ def run_tests(pyb, tests, args, base_path="."): if not has_complex: skip_tests.add('float/complex1.py') skip_tests.add('float/complex1_intbig.py') + skip_tests.add('float/complex_special_mehods.py') skip_tests.add('float/int_big_float.py') skip_tests.add('float/true_value.py') skip_tests.add('float/types.py') From e4fcd216e02eef0b389c84ecd67be3114aac0a5d Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 26 Jun 2020 10:20:27 +1000 Subject: [PATCH 127/352] stm32/usbd_cdc_interface: Remove full==size-1 limitation on tx ringbuf. Before this commit the USB VCP TX ring-buffer used the basic implementation where it can only be filled to a maximum of buffer size-1. For a 1024 size buffer this means the largest packet that can be sent is 1023. Once a packet of this size is sent the next byte copied in goes to the final byte in the buffer, so must be sent as a 1 byte packet before the read pointer can be wrapped around to the beginning. So in large streaming transfers, watching the USB sniffer you basically get alternating 1023 byte packets then 1 byte packets. This commit changes the ring-buffer implementation to a scheme that doesn't have the full-size limitation, and the USB VCP driver can now achieve a constant stream of full-sized packets. This scheme introduces a restriction on the size of the buffer: it must be a power of 2, and the maximum size is half of the size of the index (in this case the index is 16-bit, so the maximum size would be 32767 bytes rounded to 16384 for a power-of-2). But this is not a big limitation because the size of the ring-buffer prior to this commit was restricted to powers of 2 because it was using a mask-based method to wrap the indices. For an explanation of the new scheme see https://www.snellman.net/blog/archive/2016-12-13-ring-buffers/ The RX buffer could likely do with a similar change, though as it's not read from in chunks like the TX buffer it doesn't present the same issue, all that's lost is one byte capacity of the buffer. USB VCP TX throughput is improved by this change, potentially doubling the speed in certain cases. --- ports/stm32/usbd_cdc_interface.c | 72 ++++++++++++++++++++------------ ports/stm32/usbd_cdc_interface.h | 4 +- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index a0e19de9750aa..7a09128527cec 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -171,32 +171,56 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui return USBD_OK; } +static inline uint16_t usbd_cdc_tx_buffer_mask(uint16_t val) { + return val & (USBD_CDC_TX_DATA_SIZE - 1); +} + +static inline uint16_t usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out; +} + +static inline bool usbd_cdc_tx_buffer_empty(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in; +} + +static inline bool usbd_cdc_tx_buffer_will_be_empty(usbd_cdc_itf_t *cdc) { + return cdc->tx_buf_ptr_out_next == cdc->tx_buf_ptr_in; +} + +static inline bool usbd_cdc_tx_buffer_full(usbd_cdc_itf_t *cdc) { + return usbd_cdc_tx_buffer_size(cdc) == USBD_CDC_TX_DATA_SIZE; +} + +static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) { + uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out); + return MIN(usbd_cdc_tx_buffer_size(cdc), to_end); +} + +static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) { + cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data; + cdc->tx_buf_ptr_in++; +} + +static uint8_t *usbd_cdc_tx_buffer_getp(usbd_cdc_itf_t *cdc, uint16_t len) { + cdc->tx_buf_ptr_out_next += len; + return &cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out)]; +} + // Called when the USB IN endpoint is ready to receive more data // (cdc.base.tx_in_progress must be 0) void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t *)cdc_in; - cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow; + cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_next; - if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) { + if (usbd_cdc_tx_buffer_empty(cdc) && !cdc->tx_need_empty_packet) { // No outstanding data to send return; } - - uint32_t len; - if (cdc->tx_buf_ptr_out > cdc->tx_buf_ptr_in) { // rollback - len = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out; - } else { - len = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out; - } - + uint16_t len = usbd_cdc_tx_send_length(cdc); // Should always succeed because cdc.base.tx_in_progress==0 - USBD_CDC_TransmitPacket(&cdc->base, len, &cdc->tx_buf[cdc->tx_buf_ptr_out]); + USBD_CDC_TransmitPacket(&cdc->base, len, usbd_cdc_tx_buffer_getp(cdc, len)); - cdc->tx_buf_ptr_out_shadow += len; - if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) { - cdc->tx_buf_ptr_out_shadow = 0; - } // According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE) // gets held at the USB host until the next packet is sent. This is because a @@ -204,7 +228,7 @@ void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) { // the host waits for all data to arrive (ie, waits for a packet < max packet size). // To flush a packet of exactly max packet size, we need to send a zero-size packet. // See eg http://www.cypress.com/?id=4&rID=92719 - cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in); + cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && usbd_cdc_tx_buffer_will_be_empty(cdc)); } // Attempt to queue data on the USB IN endpoint @@ -291,10 +315,7 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) { } int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { - int32_t tx_waiting = (int32_t)cdc->tx_buf_ptr_in - (int32_t)cdc->tx_buf_ptr_out; - if (tx_waiting < 0) { - tx_waiting += USBD_CDC_TX_DATA_SIZE; - } + int32_t tx_waiting = usbd_cdc_tx_buffer_size(cdc); return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2; } @@ -317,8 +338,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t for (uint32_t i = 0; i < len; i++) { // Wait until the device is connected and the buffer has space, with a given timeout uint32_t start = HAL_GetTick(); - while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED - || ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) { + while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED || usbd_cdc_tx_buffer_full(cdc)) { usbd_cdc_try_tx(cdc); // Wraparound of tick is taken care of by 2's complement arithmetic. if (HAL_GetTick() - start >= timeout) { @@ -333,8 +353,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t } // Write data to device buffer - cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; - cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); + usbd_cdc_tx_buffer_put(cdc, buf[i]); } usbd_cdc_try_tx(cdc); @@ -357,7 +376,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { // If the buffer is full, wait until it gets drained, with a timeout of 500ms // (wraparound of tick is taken care of by 2's complement arithmetic). uint32_t start = HAL_GetTick(); - while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) { + while (usbd_cdc_tx_buffer_full(cdc) && HAL_GetTick() - start <= 500) { usbd_cdc_try_tx(cdc); if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be drained; exit loop @@ -367,8 +386,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { } } - cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; - cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); + usbd_cdc_tx_buffer_put(cdc, buf[i]); } usbd_cdc_try_tx(cdc); } diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index 1847b3a6b0eb1..d0509b09f9a99 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -35,7 +35,7 @@ #define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2 #endif #ifndef USBD_CDC_TX_DATA_SIZE -#define USBD_CDC_TX_DATA_SIZE (1024) // I think this can be any value (was 2048) +#define USBD_CDC_TX_DATA_SIZE (1024) // This must be a power of 2 and no greater than 16384 #endif // Values for connect_state @@ -60,7 +60,7 @@ typedef struct _usbd_cdc_itf_t { uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained - uint16_t tx_buf_ptr_out_shadow; // shadow of above + uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size volatile uint8_t connect_state; // indicates if we are connected From 048a1d675dc92ff49b1a00adedba0a0dea8d5ea0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Jun 2020 16:06:30 +1000 Subject: [PATCH 128/352] stm32/timer: Properly initialise timer deadtime/brk on WB MCUs. Signed-off-by: Damien George --- ports/stm32/timer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index c6f3d21f8671e..9b8c14c0da552 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -477,7 +477,7 @@ STATIC void config_deadtime(pyb_timer_obj_t *self, mp_int_t ticks, mp_int_t brk) deadTimeConfig.DeadTime = compute_dtg_from_ticks(ticks); deadTimeConfig.BreakState = brk == BRK_OFF ? TIM_BREAK_DISABLE : TIM_BREAK_ENABLE; deadTimeConfig.BreakPolarity = brk == BRK_LOW ? TIM_BREAKPOLARITY_LOW : TIM_BREAKPOLARITY_HIGH; - #if defined(STM32F7) || defined(STM32H7) | defined(STM32L4) + #if defined(STM32F7) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) deadTimeConfig.BreakFilter = 0; deadTimeConfig.Break2State = TIM_BREAK_DISABLE; deadTimeConfig.Break2Polarity = TIM_BREAKPOLARITY_LOW; @@ -769,14 +769,14 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons HAL_TIM_Base_Init(&self->tim); #if !defined(STM32L0) #if defined(IS_TIM_ADVANCED_INSTANCE) - if (IS_TIM_ADVANCED_INSTANCE(self->tim.Instance)) { + if (IS_TIM_ADVANCED_INSTANCE(self->tim.Instance)) #elif defined(IS_TIM_BREAK_INSTANCE) - if (IS_TIM_BREAK_INSTANCE(self->tim.Instance)) { + if (IS_TIM_BREAK_INSTANCE(self->tim.Instance)) #else - if (0) { - #endif + if (0) + #endif + { config_deadtime(self, args[ARG_deadtime].u_int, args[ARG_brk].u_int); - } #endif From 4d6f60d4286b3e6ea8c9fb0141ba13e4978882b6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 5 Mar 2020 15:51:20 +1100 Subject: [PATCH 129/352] tools/pydfu.py: Respect longer timeouts requested by DFU device/mboot. --- tools/pydfu.py | 52 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/tools/pydfu.py b/tools/pydfu.py index 030f56bf852e3..8b8e338981b49 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -22,6 +22,8 @@ import usb.core import usb.util import zlib +import time +import math # VID/PID __VID = 0x0483 @@ -29,6 +31,8 @@ # USB request __TIMEOUT __TIMEOUT = 4000 +__NEXT_TIMEOUT = 0 +__STATUS_TIMEOUT = 20000 # DFU commands __DFU_DETACH = 0 @@ -95,6 +99,13 @@ def get_string(dev, index): return usb.util.get_string(dev, index) +def timeout(): + global __NEXT_TIMEOUT + t = max(__TIMEOUT, __NEXT_TIMEOUT) + __NEXT_TIMEOUT = 0 + return int(math.ceil(t)) + + def find_dfu_cfg_descr(descr): if len(descr) == 9 and descr[0] == 9 and descr[1] == _DFU_DESCRIPTOR_TYPE: nt = collections.namedtuple( @@ -150,17 +161,32 @@ def init(): def abort_request(): """Sends an abort request.""" - __dev.ctrl_transfer(0x21, __DFU_ABORT, 0, __DFU_INTERFACE, None, __TIMEOUT) + __dev.ctrl_transfer(0x21, __DFU_ABORT, 0, __DFU_INTERFACE, None, timeout()) def clr_status(): """Clears any error status (perhaps left over from a previous session).""" - __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, __TIMEOUT) + __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, timeout()) def get_status(): """Get the status of the last operation.""" - stat = __dev.ctrl_transfer(0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, 20000) + _timeout = time.time() + max(__STATUS_TIMEOUT, timeout()) + stat = None + while time.time() < _timeout: + try: + stat = __dev.ctrl_transfer( + 0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, int(_timeout - time.time()) + ) + break + except usb.core.USBError as ex: + # If the firmware is blocked the transfer can timeout much quicker than + # the supplied timeout. If so, retry until the overall timeout is used up. + if "Operation timed out" not in str(ex): + raise + + if stat is None: + raise SystemExit("DFU: get_status timed out") # firmware can provide an optional string for any error if stat[5]: @@ -168,6 +194,12 @@ def get_status(): if message: print(message) + # firmware can send a longer timeout request while it's performing slow operation eg. erase + timeout_ms = stat[1] << 16 | stat[2] << 8 | stat[3] + if timeout_ms: + global __NEXT_TIMEOUT + __NEXT_TIMEOUT = __TIMEOUT + timeout_ms + return stat[4] @@ -180,9 +212,9 @@ def check_status(stage, expected): def mass_erase(): """Performs a MASS erase (i.e. erases the entire device).""" # Send DNLOAD with first byte=0x41 - __dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, "\x41", __TIMEOUT) + __dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, "\x41", timeout()) - # Execute last command + # Execute erase and wait until complete check_status("erase", __DFU_STATE_DFU_DOWNLOAD_BUSY) # Check command state @@ -196,7 +228,7 @@ def page_erase(addr): # Send DNLOAD with first byte=0x41 and page address buf = struct.pack(" Date: Thu, 5 Mar 2020 16:05:36 +1100 Subject: [PATCH 130/352] stm32/mboot: Implement DFU mass erase. The implementation internally uses sector erase to wipe everything except the sector(s) that mboot lives in (by erasing starting from APPLICATION_ADDR). The erase command can take some time (eg an STM32F765 with 2MB of flash takes 8 to 10 seconds). This time is normally enough to make pydfu.py fail with a timeout. The DFU standard includes a mechanism for the DFU device to request a longer timeout as part of the get-status response just before starting an operation. This timeout functionality has been implemented here. --- ports/stm32/mboot/dfu.h | 6 +++++ ports/stm32/mboot/main.c | 57 ++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/ports/stm32/mboot/dfu.h b/ports/stm32/mboot/dfu.h index e826e217f032f..a1d4d10d0ef90 100644 --- a/ports/stm32/mboot/dfu.h +++ b/ports/stm32/mboot/dfu.h @@ -67,6 +67,12 @@ typedef enum { DFU_CMD_DNLOAD = 8, } dfu_cmd_t; +enum { + DFU_CMD_DNLOAD_SET_ADDRESS = 0x21, + DFU_CMD_DNLOAD_ERASE = 0x41, + DFU_CMD_DNLOAD_READ_UNPROTECT = 0x92, +}; + // Error status flags typedef enum { DFU_STATUS_OK = 0x00, // No error condition is present. diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 87618e7f410ad..d4527874120ad 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -441,6 +441,11 @@ static int usrbtn_state(void) { /******************************************************************************/ // FLASH +#if defined(STM32WB) +#define FLASH_END FLASH_END_ADDR +#endif +#define APPLICATION_FLASH_LENGTH (FLASH_END + 1 - APPLICATION_ADDR) + #ifndef MBOOT_SPIFLASH_LAYOUT #define MBOOT_SPIFLASH_LAYOUT "" #endif @@ -464,8 +469,9 @@ static int usrbtn_state(void) { #endif static int mboot_flash_mass_erase(void) { - // TODO - return -1; + // Erase all flash pages after mboot. + int ret = flash_erase(APPLICATION_ADDR, APPLICATION_FLASH_LENGTH / sizeof(uint32_t)); + return ret; } static int mboot_flash_page_erase(uint32_t addr, uint32_t *next_addr) { @@ -523,7 +529,7 @@ static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) { // Writable address space interface static int do_mass_erase(void) { - // TODO + // TODO spiflash erase ? return mboot_flash_mass_erase(); } @@ -791,20 +797,45 @@ static void dfu_init(void) { dfu_context.addr = 0x08000000; } +// The DFU_GETSTATUS response before dfu_process_dnload is run should include the needed timeout adjustments +static size_t get_timeout_ms(void) { + if (dfu_context.wBlockNum == 0) { + // download control commands + if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_ERASE) { + if (dfu_context.wLength == 1) { + // mass erase command + // It takes 10-12 seconds to erase a 2MB stm part. Extrapolate a suitable timeout from this. + return APPLICATION_FLASH_LENGTH / 170; + + } else if (dfu_context.wLength == 5) { + // erase page command + return 500; + } + } + } else if (dfu_context.wBlockNum > 1) { + // write data to memory command + return 500; + } + return 0; +} + static int dfu_process_dnload(void) { int ret = -1; if (dfu_context.wBlockNum == 0) { // download control commands - if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x41) { + if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_ERASE) { if (dfu_context.wLength == 1) { // mass erase ret = do_mass_erase(); + if (ret != 0) { + dfu_context.cmd = DFU_CMD_NONE; + } } else if (dfu_context.wLength == 5) { // erase page uint32_t next_addr; ret = do_page_erase(get_le32(&dfu_context.buf[1]), &next_addr); } - } else if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x21) { + } else if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_SET_ADDRESS) { if (dfu_context.wLength == 5) { // set address dfu_context.addr = get_le32(&dfu_context.buf[1]); @@ -888,12 +919,16 @@ static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) { default: dfu_context.state = DFU_STATE_BUSY; } - buf[0] = dfu_context.status; // bStatus - buf[1] = 0; // bwPollTimeout (ms) - buf[2] = 0; // bwPollTimeout (ms) - buf[3] = 0; // bwPollTimeout (ms) - buf[4] = dfu_context.state; // bState - buf[5] = dfu_context.error; // iString + size_t timeout_ms = get_timeout_ms(); + buf[0] = dfu_context.status; // bStatus + buf[1] = (timeout_ms >> 16) & 0xFF; // bwPollTimeout (ms) + buf[2] = (timeout_ms >> 8) & 0xFF; // bwPollTimeout (ms) + buf[3] = timeout_ms & 0xFF; // bwPollTimeout (ms) + buf[4] = dfu_context.state; // bState + buf[5] = dfu_context.error; // iString + // Clear errors now they've been sent + dfu_context.status = DFU_STATUS_OK; + dfu_context.error = 0; return 6; } return -1; From 40006813c3e053e4cf952bfdd32866ce9496b0f8 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 30 Jun 2020 16:33:32 +1000 Subject: [PATCH 131/352] stm32/flash: Update flash_get_sector_info to return -1 on invalid addr. So the caller can tell when an invalid address is used and can take appropriate action. --- ports/stm32/flash.c | 4 ++-- ports/stm32/flash.h | 2 +- ports/stm32/flashbdev.c | 4 ++-- ports/stm32/mboot/main.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 5f96696a9ffd7..499129a6f3e5d 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -151,7 +151,7 @@ bool flash_is_valid_addr(uint32_t addr) { return flash_layout[0].base_address <= addr && addr < end_of_flash; } -uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size) { +int32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size) { if (addr >= flash_layout[0].base_address) { uint32_t sector_index = 0; for (int i = 0; i < MP_ARRAY_SIZE(flash_layout); ++i) { @@ -172,7 +172,7 @@ uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *si } } } - return 0; + return -1; } int flash_erase(uint32_t flash_dest, uint32_t num_word32) { diff --git a/ports/stm32/flash.h b/ports/stm32/flash.h index 12cdaca55aab9..ecda923db7826 100644 --- a/ports/stm32/flash.h +++ b/ports/stm32/flash.h @@ -27,7 +27,7 @@ #define MICROPY_INCLUDED_STM32_FLASH_H bool flash_is_valid_addr(uint32_t addr); -uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size); +int32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size); int flash_erase(uint32_t flash_dest, uint32_t num_word32); int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32); diff --git a/ports/stm32/flashbdev.c b/ports/stm32/flashbdev.c index e105bd35381b9..4153a713c8636 100644 --- a/ports/stm32/flashbdev.c +++ b/ports/stm32/flashbdev.c @@ -181,7 +181,7 @@ int32_t flash_bdev_ioctl(uint32_t op, uint32_t arg) { static uint8_t *flash_cache_get_addr_for_write(uint32_t flash_addr) { uint32_t flash_sector_start; uint32_t flash_sector_size; - uint32_t flash_sector_id = flash_get_sector_info(flash_addr, &flash_sector_start, &flash_sector_size); + int32_t flash_sector_id = flash_get_sector_info(flash_addr, &flash_sector_start, &flash_sector_size); if (flash_sector_size > FLASH_SECTOR_SIZE_MAX) { flash_sector_size = FLASH_SECTOR_SIZE_MAX; } @@ -201,7 +201,7 @@ static uint8_t *flash_cache_get_addr_for_write(uint32_t flash_addr) { static uint8_t *flash_cache_get_addr_for_read(uint32_t flash_addr) { uint32_t flash_sector_start; uint32_t flash_sector_size; - uint32_t flash_sector_id = flash_get_sector_info(flash_addr, &flash_sector_start, &flash_sector_size); + int32_t flash_sector_id = flash_get_sector_info(flash_addr, &flash_sector_start, &flash_sector_size); if (flash_cache_sector_id == flash_sector_id) { // in cache, copy from there return (uint8_t *)CACHE_MEM_START_ADDR + flash_addr - flash_sector_start; diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index d4527874120ad..8cc14c2ac99cb 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -476,25 +476,27 @@ static int mboot_flash_mass_erase(void) { static int mboot_flash_page_erase(uint32_t addr, uint32_t *next_addr) { uint32_t sector_size = 0; - uint32_t sector = flash_get_sector_info(addr, NULL, §or_size); - if (sector == 0) { - // Don't allow to erase the sector with this bootloader in it + uint32_t sector_start = 0; + int32_t sector = flash_get_sector_info(addr, §or_start, §or_size); + if (sector <= 0) { + // Don't allow to erase the sector with this bootloader in it, or invalid sectors dfu_context.status = DFU_STATUS_ERROR_ADDRESS; - dfu_context.error = MBOOT_ERROR_STR_OVERWRITE_BOOTLOADER_IDX; + dfu_context.error = (sector == 0) ? MBOOT_ERROR_STR_OVERWRITE_BOOTLOADER_IDX + : MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; return -1; } - *next_addr = addr + sector_size; + *next_addr = sector_start + sector_size; // Erase the flash page. - int ret = flash_erase(addr, sector_size / sizeof(uint32_t)); + int ret = flash_erase(sector_start, sector_size / sizeof(uint32_t)); if (ret != 0) { return ret; } // Check the erase set bits to 1, at least for the first 256 bytes for (int i = 0; i < 64; ++i) { - if (((volatile uint32_t*)addr)[i] != 0xffffffff) { + if (((volatile uint32_t*)sector_start)[i] != 0xffffffff) { return -2; } } @@ -503,11 +505,12 @@ static int mboot_flash_page_erase(uint32_t addr, uint32_t *next_addr) { } static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) { - uint32_t sector = flash_get_sector_info(addr, NULL, NULL); - if (sector == 0) { + int32_t sector = flash_get_sector_info(addr, NULL, NULL); + if (sector <= 0) { // Don't allow to write the sector with this bootloader in it dfu_context.status = DFU_STATUS_ERROR_ADDRESS; - dfu_context.error = MBOOT_ERROR_STR_OVERWRITE_BOOTLOADER_IDX; + dfu_context.error = (sector == 0) ? MBOOT_ERROR_STR_OVERWRITE_BOOTLOADER_IDX + : MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; return -1; } From 65a7e00078fae27521dd9df78e328c6fa1e3b288 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 30 Jun 2020 14:23:50 +1000 Subject: [PATCH 132/352] stm32/mboot: Add DFU logic to respond to DFU_GETSTATE request. This is required for some DFU programmers, eg ST's DfuSe demo PC app. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 8cc14c2ac99cb..ba0c35b8bfe4c 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -933,6 +933,9 @@ static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) { dfu_context.status = DFU_STATUS_OK; dfu_context.error = 0; return 6; + } else if (cmd == DFU_GETSTATE && len == 1) { + buf[0] = dfu_context.state; // bState + return 1; } return -1; } From cb9aafbf8f0ee912e5f4d0a32d5f9765d9e8f30c Mon Sep 17 00:00:00 2001 From: victor Date: Sun, 28 Jun 2020 23:41:04 +0200 Subject: [PATCH 133/352] docs/library: Clarify that the arg to esp.deepsleep is in microseconds. --- docs/library/esp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/esp.rst b/docs/library/esp.rst index cb2bc7af8d538..b9ae57bd9757f 100644 --- a/docs/library/esp.rst +++ b/docs/library/esp.rst @@ -31,7 +31,7 @@ Functions The system enters the set sleep mode automatically when possible. -.. function:: deepsleep(time=0, /) +.. function:: deepsleep(time_us=0, /) **Note**: ESP8266 only - use `machine.deepsleep()` on ESP32 From b572aa5721e3516367c56fba9e0d09ac0edbbd2c Mon Sep 17 00:00:00 2001 From: spacemanspiff2007 Date: Thu, 25 Jun 2020 15:35:58 +0200 Subject: [PATCH 134/352] docs/esp32: Add info about PWM duty cycle range to esp32 quickref. See related #4581. --- docs/esp32/quickref.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 8861ca4ac87ed..b4f443a910009 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -175,6 +175,7 @@ range from 1Hz to 40MHz but there is a tradeoff; as the base frequency *increases* the duty resolution *decreases*. See `LED Control `_ for more details. +Currently the duty cycle has to be in the range of 0-1023. Use the ``machine.PWM`` class:: From 9d5edb35596b0ff5943bf1cb756dc3c42e8566d9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 14 Jun 2020 16:22:29 +1000 Subject: [PATCH 135/352] lib/utils: Protect all of mpirq.c with MICROPY_ENABLE_SCHEDULER. So it can be unconditionally included in a port's build even if certain configurations in that port do not use its features, to simplify the Makefile. Signed-off-by: Damien George --- lib/utils/mpirq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/utils/mpirq.c b/lib/utils/mpirq.c index 8de13b0b6a3c8..663be182248cc 100644 --- a/lib/utils/mpirq.c +++ b/lib/utils/mpirq.c @@ -31,6 +31,8 @@ #include "py/gc.h" #include "lib/utils/mpirq.h" +#if MICROPY_ENABLE_SCHEDULER + /****************************************************************************** DECLARE PUBLIC DATA ******************************************************************************/ @@ -125,3 +127,5 @@ const mp_obj_type_t mp_irq_type = { .call = mp_irq_call, .locals_dict = (mp_obj_dict_t *)&mp_irq_locals_dict, }; + +#endif // MICROPY_ENABLE_SCHEDULER From f84145bea1bc962b5aecf24bef6ea64202d905ee Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 14 Jun 2020 16:22:56 +1000 Subject: [PATCH 136/352] zephyr: Implement machine.Pin.irq() for setting callbacks on pin change. Supports hard and soft interrupts. In the current implementation, soft interrupt callbacks will only be called when the VM is executing, ie they will not be called during a blocking kernel call like k_msleep. And the behaviour of hard interrupt callbacks will depend on the underlying device, as well as the amount of ISR stack space. Soft and hard interrupts tested on frdm_k64f and nucleo_f767zi boards. Signed-off-by: Damien George --- ports/zephyr/Makefile | 1 + ports/zephyr/machine_pin.c | 133 ++++++++++++++++++++++++++++++++++++ ports/zephyr/main.c | 6 ++ ports/zephyr/modmachine.h | 3 + ports/zephyr/mpconfigport.h | 7 +- 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index 4fe6552846aab..169aebc26772e 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -52,6 +52,7 @@ SRC_C = main.c \ uart_core.c \ zephyr_storage.c \ lib/timeutils/timeutils.c \ + lib/utils/mpirq.c \ lib/utils/stdout_helpers.c \ lib/utils/printf.c \ lib/utils/pyexec.c \ diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c index ad5f54baafb92..79d759eb28bf3 100644 --- a/ports/zephyr/machine_pin.c +++ b/ports/zephyr/machine_pin.c @@ -35,10 +35,50 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" +#include "lib/utils/mpirq.h" #include "modmachine.h" +#if MICROPY_PY_MACHINE + +typedef struct _machine_pin_irq_obj_t { + mp_irq_obj_t base; + struct _machine_pin_irq_obj_t *next; + struct gpio_callback callback; +} machine_pin_irq_obj_t; + +STATIC const mp_irq_methods_t machine_pin_irq_methods; const mp_obj_base_t machine_pin_obj_template = {&machine_pin_type}; +void machine_pin_deinit(void) { + for (machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_list); irq != NULL; irq = irq->next) { + machine_pin_obj_t *pin = MP_OBJ_TO_PTR(irq->base.parent); + gpio_pin_interrupt_configure(pin->port, pin->pin, GPIO_INT_DISABLE); + gpio_remove_callback(pin->port, &irq->callback); + } + MP_STATE_PORT(machine_pin_irq_list) = NULL; +} + +STATIC void gpio_callback_handler(struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { + machine_pin_irq_obj_t *irq = CONTAINER_OF(cb, machine_pin_irq_obj_t, callback); + + #if MICROPY_STACK_CHECK + // This callback executes in an ISR context so the stack-limit check must be changed to + // use the ISR stack for the duration of this function (so that hard IRQ callbacks work). + char *orig_stack_top = MP_STATE_THREAD(stack_top); + size_t orig_stack_limit = MP_STATE_THREAD(stack_limit); + MP_STATE_THREAD(stack_top) = (void *)&irq; + MP_STATE_THREAD(stack_limit) = CONFIG_ISR_STACK_SIZE - 512; + #endif + + mp_irq_handler(&irq->base); + + #if MICROPY_STACK_CHECK + // Restore original stack-limit checking values. + MP_STATE_THREAD(stack_top) = orig_stack_top; + MP_STATE_THREAD(stack_limit) = orig_stack_limit; + #endif +} + 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->port, self->pin); @@ -151,6 +191,66 @@ STATIC mp_obj_t machine_pin_on(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_on_obj, machine_pin_on); +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False) +STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_INT_EDGE_BOTH} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (self->irq == NULL) { + machine_pin_irq_obj_t *irq; + for (irq = MP_STATE_PORT(machine_pin_irq_list); irq != NULL; irq = irq->next) { + machine_pin_obj_t *irq_pin = MP_OBJ_TO_PTR(irq->base.parent); + if (irq_pin->port == self->port && irq_pin->pin == self->pin) { + break; + } + } + if (irq == NULL) { + irq = m_new_obj(machine_pin_irq_obj_t); + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + irq->next = MP_STATE_PORT(machine_pin_irq_list); + gpio_init_callback(&irq->callback, gpio_callback_handler, BIT(self->pin)); + int ret = gpio_add_callback(self->port, &irq->callback); + if (ret != 0) { + mp_raise_OSError(-ret); + } + MP_STATE_PORT(machine_pin_irq_list) = irq; + } + self->irq = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + // configure irq + int ret = gpio_pin_interrupt_configure(self->port, self->pin, GPIO_INT_DISABLE); + if (ret != 0) { + mp_raise_OSError(-ret); + } + + self->irq->base.handler = args[ARG_handler].u_obj; + self->irq->base.ishard = args[ARG_hard].u_bool; + + if (args[ARG_handler].u_obj != mp_const_none) { + ret = gpio_pin_interrupt_configure(self->port, self->pin, args[ARG_trigger].u_int); + if (ret != 0) { + mp_raise_OSError(-ret); + } + } + } + + return MP_OBJ_FROM_PTR(self->irq); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + 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; @@ -172,12 +272,15 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { 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_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, // class constants { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_INPUT) }, { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_INT_EDGE_RISING) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_INT_EDGE_FALLING) }, }; STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); @@ -195,3 +298,33 @@ const mp_obj_type_t machine_pin_type = { .protocol = &machine_pin_pin_p, .locals_dict = (mp_obj_t)&machine_pin_locals_dict, }; + +STATIC mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (new_trigger == 0) { + new_trigger = GPIO_INT_DISABLE; + } + int ret = gpio_pin_interrupt_configure(self->port, self->pin, new_trigger); + if (ret != 0) { + mp_raise_OSError(-ret); + } + return 0; +} + +STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return gpio_get_pending_int(self->port); + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return 0; // TODO + } + return 0; +} + +STATIC const mp_irq_methods_t machine_pin_irq_methods = { + .init = machine_pin_irq, + .trigger = machine_pin_irq_trigger, + .info = machine_pin_irq_info, +}; + +#endif // MICROPY_PY_MACHINE diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 104954f595c0a..78b947d7af9b9 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -55,6 +55,7 @@ #include "extmod/vfs.h" #endif +#include "modmachine.h" #include "modzephyr.h" #ifdef TEST @@ -166,6 +167,11 @@ int real_main(void) { } printf("soft reboot\n"); + + #if MICROPY_PY_MACHINE + machine_pin_deinit(); + #endif + goto soft_reset; return 0; diff --git a/ports/zephyr/modmachine.h b/ports/zephyr/modmachine.h index 84e4d10a885ae..766a9d7ea3874 100644 --- a/ports/zephyr/modmachine.h +++ b/ports/zephyr/modmachine.h @@ -11,6 +11,9 @@ typedef struct _machine_pin_obj_t { mp_obj_base_t base; struct device *port; uint32_t pin; + struct _machine_pin_irq_obj_t *irq; } machine_pin_obj_t; +void machine_pin_deinit(void); + #endif // MICROPY_INCLUDED_ZEPHYR_MODMACHINE_H diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 7044c175d030c..f94ee72707d47 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -81,6 +81,7 @@ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_PY_BUILTINS_COMPLEX (0) +#define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_VFS (1) #define MICROPY_READER_VFS (MICROPY_VFS) @@ -119,7 +120,8 @@ typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM #define MICROPY_PORT_ROOT_POINTERS \ - const char *readline_hist[8]; + const char *readline_hist[8]; \ + void *machine_pin_irq_list; /* Linked list of pin irq objects */ extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_time; @@ -169,3 +171,6 @@ extern const struct _mp_obj_module_t mp_module_zsensor; // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + +#define MICROPY_BEGIN_ATOMIC_SECTION irq_lock +#define MICROPY_END_ATOMIC_SECTION irq_unlock From 41b7734c464cbdcd0d447a4de697ce9b3fcd9e04 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 14 Jun 2020 16:23:09 +1000 Subject: [PATCH 137/352] zephyr/make-minimal: Disable FAT and LFS2 options to make it build. Signed-off-by: Damien George --- ports/zephyr/make-minimal | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/zephyr/make-minimal b/ports/zephyr/make-minimal index 1fc143e4d6af0..d7dddc33427df 100755 --- a/ports/zephyr/make-minimal +++ b/ports/zephyr/make-minimal @@ -11,6 +11,8 @@ make \ CONF_FILE=prj_minimal.conf \ CFLAGS_EXTRA='-DMP_CONFIGFILE=""' \ + MICROPY_VFS_FAT=0 \ + MICROPY_VFS_LFS2=0 \ FROZEN_DIR= \ QEMU_NET=0 \ "$@" From d06ae1d2b11d03e45b6d12a41b9435ae0ca1690c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 30 Jun 2020 23:52:35 +1000 Subject: [PATCH 138/352] py/obj.h: Make existing MP_TYPE_FLAG_xxx macros sequential. There's no reason to have them non-sequential, this was likely a typo from commit 9ec1caf42e7733b5141b7aecf1b6e58834a94bf7. Signed-off-by: Damien George --- py/obj.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/obj.h b/py/obj.h index 1fa24eb18c903..6bec57c35293c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -475,8 +475,8 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // operator and not the __ne__ operator. If it's set then __ne__ may be implemented. #define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) #define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) -#define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0040) -#define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0080) +#define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0004) +#define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0008) #define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010) typedef enum { From 332d83343fb3ef5d2b94b4f058aa53fd0493779e Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 28 Jun 2020 09:39:20 +1000 Subject: [PATCH 139/352] py: Rework mp_convert_member_lookup to properly handle built-ins. This commit fixes lookups of class members to make it so that built-in functions that are used as methods/functions of a class work correctly. The mp_convert_member_lookup() function is pretty much completely changed by this commit, but for the most part it's just reorganised and the indenting changed. The functional changes are: - staticmethod and classmethod checks moved to later in the if-logic, because they are less common and so should be checked after the more common cases. - The explicit mp_obj_is_type(member, &mp_type_type) check is removed because it's now subsumed by other, more general tests in this function. - MP_TYPE_FLAG_BINDS_SELF and MP_TYPE_FLAG_BUILTIN_FUN type flags added to make the checks in this function much simpler (now they just test this bit in type->flags). - An extra check is made for mp_obj_is_instance_type(type) to fix lookup of built-in functions. Fixes #1326 and #6198. Signed-off-by: Damien George --- py/obj.h | 4 ++ py/objclosure.c | 1 + py/objfun.c | 8 ++++ py/objgenerator.c | 2 + py/runtime.c | 82 ++++++++++++++++++--------------- tests/basics/class_bind_self.py | 9 +++- 6 files changed, 67 insertions(+), 39 deletions(-) diff --git a/py/obj.h b/py/obj.h index 6bec57c35293c..f621b1dadd492 100644 --- a/py/obj.h +++ b/py/obj.h @@ -473,11 +473,15 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // then the type may check for equality against a different type. // If MP_TYPE_FLAG_EQ_HAS_NEQ_TEST is clear then the type only implements the __eq__ // operator and not the __ne__ operator. If it's set then __ne__ may be implemented. +// If MP_TYPE_FLAG_BINDS_SELF is set then the type as a method binds self as the first arg. +// If MP_TYPE_FLAG_BUILTIN_FUN is set then the type is a built-in function type. #define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) #define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) #define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0004) #define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0008) #define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010) +#define MP_TYPE_FLAG_BINDS_SELF (0x0020) +#define MP_TYPE_FLAG_BUILTIN_FUN (0x0040) typedef enum { PRINT_STR = 0, diff --git a/py/objclosure.c b/py/objclosure.c index f5038ffc46513..9a8c7631c2bea 100644 --- a/py/objclosure.c +++ b/py/objclosure.c @@ -80,6 +80,7 @@ STATIC void closure_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_ const mp_obj_type_t closure_type = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_closure, #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED .print = closure_print, diff --git a/py/objfun.c b/py/objfun.c index f9a6b4ebc2727..052f4b1cedfd2 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -58,6 +58,7 @@ STATIC mp_obj_t fun_builtin_0_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_type_t mp_type_fun_builtin_0 = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_0_call, .unary_op = mp_generic_unary_op, @@ -72,6 +73,7 @@ STATIC mp_obj_t fun_builtin_1_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_type_t mp_type_fun_builtin_1 = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_1_call, .unary_op = mp_generic_unary_op, @@ -86,6 +88,7 @@ STATIC mp_obj_t fun_builtin_2_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_type_t mp_type_fun_builtin_2 = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_2_call, .unary_op = mp_generic_unary_op, @@ -100,6 +103,7 @@ STATIC mp_obj_t fun_builtin_3_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_type_t mp_type_fun_builtin_3 = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_3_call, .unary_op = mp_generic_unary_op, @@ -130,6 +134,7 @@ STATIC mp_obj_t fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_k const mp_obj_type_t mp_type_fun_builtin_var = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, .name = MP_QSTR_function, .call = fun_builtin_var_call, .unary_op = mp_generic_unary_op, @@ -355,6 +360,7 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { const mp_obj_type_t mp_type_fun_bc = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, #if MICROPY_CPYTHON_COMPAT .print = fun_bc_print, @@ -406,6 +412,7 @@ STATIC mp_obj_t fun_native_call(mp_obj_t self_in, size_t n_args, size_t n_kw, co STATIC const mp_obj_type_t mp_type_fun_native = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .call = fun_native_call, .unary_op = mp_generic_unary_op, @@ -513,6 +520,7 @@ STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const STATIC const mp_obj_type_t mp_type_fun_asm = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .call = fun_asm_call, .unary_op = mp_generic_unary_op, diff --git a/py/objgenerator.c b/py/objgenerator.c index 5e140ba234474..543685fac7860 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -73,6 +73,7 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons const mp_obj_type_t mp_type_gen_wrap = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_generator, .call = gen_wrap_call, .unary_op = mp_generic_unary_op, @@ -126,6 +127,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k const mp_obj_type_t mp_type_native_gen_wrap = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_generator, .call = native_gen_wrap_call, .unary_op = mp_generic_unary_op, diff --git a/py/runtime.c b/py/runtime.c index 157bc74a8dfb4..78da386919532 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -993,6 +993,7 @@ STATIC mp_obj_t checked_fun_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c STATIC const mp_obj_type_t mp_type_checked_fun = { { &mp_type_type }, + .flags = MP_TYPE_FLAG_BINDS_SELF, .name = MP_QSTR_function, .call = checked_fun_call, }; @@ -1011,49 +1012,54 @@ STATIC mp_obj_t mp_obj_new_checked_fun(const mp_obj_type_t *type, mp_obj_t fun) // and put the result in the dest[] array for a possible method call. // Conversion means dealing with static/class methods, callables, and values. // see http://docs.python.org/3/howto/descriptor.html +// and also https://mail.python.org/pipermail/python-dev/2015-March/138950.html void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) { - if (mp_obj_is_type(member, &mp_type_staticmethod)) { - // return just the function - dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; - } else if (mp_obj_is_type(member, &mp_type_classmethod)) { - // return a bound method, with self being the type of this object - // this type should be the type of the original instance, not the base - // type (which is what is passed in the 'type' argument to this function) - if (self != MP_OBJ_NULL) { - type = mp_obj_get_type(self); - } - dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; - dest[1] = MP_OBJ_FROM_PTR(type); - } else if (mp_obj_is_type(member, &mp_type_type)) { - // Don't try to bind types (even though they're callable) - dest[0] = member; - } else if (mp_obj_is_fun(member) - || (mp_obj_is_obj(member) - && (((mp_obj_base_t *)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_closure - || ((mp_obj_base_t *)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_generator))) { - // only functions, closures and generators objects can be bound to self - #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + if (mp_obj_is_obj(member)) { const mp_obj_type_t *m_type = ((mp_obj_base_t *)MP_OBJ_TO_PTR(member))->type; - if (self == MP_OBJ_NULL - && (m_type == &mp_type_fun_builtin_0 - || m_type == &mp_type_fun_builtin_1 - || m_type == &mp_type_fun_builtin_2 - || m_type == &mp_type_fun_builtin_3 - || m_type == &mp_type_fun_builtin_var) - && type != &mp_type_object) { - // we extracted a builtin method without a first argument, so we must - // wrap this function in a type checker - // Note that object will do its own checking so shouldn't be wrapped. - dest[0] = mp_obj_new_checked_fun(type, member); - } else - #endif - { - // return a bound method, with self being this object + if (m_type->flags & MP_TYPE_FLAG_BINDS_SELF) { + // `member` is a function that binds self as its first argument. + if (m_type->flags & MP_TYPE_FLAG_BUILTIN_FUN) { + // `member` is a built-in function, which has special behaviour. + if (mp_obj_is_instance_type(type)) { + // Built-in functions on user types always behave like a staticmethod. + dest[0] = member; + } + #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG + else if (self == MP_OBJ_NULL && type != &mp_type_object) { + // `member` is a built-in method without a first argument, so wrap + // it in a type checker that will check self when it's supplied. + // Note that object will do its own checking so shouldn't be wrapped. + dest[0] = mp_obj_new_checked_fun(type, member); + } + #endif + else { + // Return a (built-in) bound method, with self being this object. + dest[0] = member; + dest[1] = self; + } + } else { + // Return a bound method, with self being this object. + dest[0] = member; + dest[1] = self; + } + } else if (m_type == &mp_type_staticmethod) { + // `member` is a staticmethod, return the function that it wraps. + dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; + } else if (m_type == &mp_type_classmethod) { + // `member` is a classmethod, return a bound method with self being the type of + // this object. This type should be the type of the original instance, not the + // base type (which is what is passed in the `type` argument to this function). + if (self != MP_OBJ_NULL) { + type = mp_obj_get_type(self); + } + dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; + dest[1] = MP_OBJ_FROM_PTR(type); + } else { + // `member` is a value, so just return that value. dest[0] = member; - dest[1] = self; } } else { - // class member is a value, so just return that value + // `member` is a value, so just return that value. dest[0] = member; } } diff --git a/tests/basics/class_bind_self.py b/tests/basics/class_bind_self.py index 16e4f5ac95ade..813f876446ef2 100644 --- a/tests/basics/class_bind_self.py +++ b/tests/basics/class_bind_self.py @@ -40,11 +40,18 @@ def f4(self, arg): # generator print(c.f3()) print(next(c.f4(4))) print(c.f5(5)) -#print(c.f6(-6)) not working in uPy +print(c.f6(-6)) print(c.f7(7)) print(c.f8(8)) print(c.f9(9)) +# test calling the functions accessed via the class itself +print(C.f5(10)) +print(C.f6(-11)) +print(C.f7(12)) +print(C.f8(13)) +print(C.f9(14)) + # not working in uPy #class C(list): # # this acts like a method and binds self From 95ec0debec1064695b9b0ecdbfa2146f17f898bb Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 1 Jul 2020 15:05:03 +1000 Subject: [PATCH 140/352] stm32/mboot: Remove the use of timeout in DFU_GETSTATUS. This is treated more like a "delay before continuing" in the spec and official tools and does not appear to be really needed. In particular, downloading firmware is much slower with non-zero timeouts because the host must pause by the timeout between sending each DFU_GETSTATUS to poll for download/erase complete. --- ports/stm32/mboot/main.c | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index ba0c35b8bfe4c..03620e6a3bdb2 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -800,28 +800,6 @@ static void dfu_init(void) { dfu_context.addr = 0x08000000; } -// The DFU_GETSTATUS response before dfu_process_dnload is run should include the needed timeout adjustments -static size_t get_timeout_ms(void) { - if (dfu_context.wBlockNum == 0) { - // download control commands - if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_ERASE) { - if (dfu_context.wLength == 1) { - // mass erase command - // It takes 10-12 seconds to erase a 2MB stm part. Extrapolate a suitable timeout from this. - return APPLICATION_FLASH_LENGTH / 170; - - } else if (dfu_context.wLength == 5) { - // erase page command - return 500; - } - } - } else if (dfu_context.wBlockNum > 1) { - // write data to memory command - return 500; - } - return 0; -} - static int dfu_process_dnload(void) { int ret = -1; if (dfu_context.wBlockNum == 0) { @@ -922,11 +900,10 @@ static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) { default: dfu_context.state = DFU_STATE_BUSY; } - size_t timeout_ms = get_timeout_ms(); buf[0] = dfu_context.status; // bStatus - buf[1] = (timeout_ms >> 16) & 0xFF; // bwPollTimeout (ms) - buf[2] = (timeout_ms >> 8) & 0xFF; // bwPollTimeout (ms) - buf[3] = timeout_ms & 0xFF; // bwPollTimeout (ms) + buf[1] = 0; // bwPollTimeout_lsb (ms) + buf[2] = 0; // bwPollTimeout (ms) + buf[3] = 0; // bwPollTimeout_msb (ms) buf[4] = dfu_context.state; // bState buf[5] = dfu_context.error; // iString // Clear errors now they've been sent From 494bcad8aba37966297470419b3c690a362495bb Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 1 Jul 2020 15:06:14 +1000 Subject: [PATCH 141/352] stm32/mboot: Disable polling mode by default and use IRQ mode instead. Polling mode will cause failures with the mass-erase command due to USB timeouts, because the USB IRQs are not being serviced. Swiching from polling to IRQ mode fixes this because the USB IRQs can be serviced between page erases. Note that when the flash is being programmed or erased the MCU is halted and cannot respond to USB IRQs, because mboot runs from flash, as opposed to the built-in bootloader which is in system ROM. But the maximum delay in responding to an IRQ is the time taken to erase a single page, about 100ms for large pages, and that is short enough that the USB does not timeout on the host side. Recent tests have shown that in the current mboot code IRQ mode is pretty much the same speed as polling mode (within timing error), code size is slightly reduced in IRQ mode, and IRQ mode idles at about half of the power consumption as polling mode. --- ports/stm32/mboot/main.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 03620e6a3bdb2..99187e3ef8406 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -38,14 +38,12 @@ #include "powerctrl.h" #include "dfu.h" -// Using polling is about 10% faster than not using it (and using IRQ instead) -// This DFU code with polling runs in about 70% of the time of the ST bootloader +// This option selects whether to use explicit polling or IRQs for USB events. +// In some test cases polling mode can run slightly faster, but it uses more power. +// Polling mode will also cause failures with the mass-erase command because USB +// events will not be serviced for the duration of the mass erase. // With STM32WB MCUs only non-polling/IRQ mode is supported. -#if defined(STM32WB) #define USE_USB_POLLING (0) -#else -#define USE_USB_POLLING (1) -#endif // Using cache probably won't make it faster because we run at a low frequency, and best // to keep the MCU config as minimal as possible. From 07f181a216dc2fe323f0511cf3a5f9dc37545c22 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 1 Jul 2020 15:01:16 +1000 Subject: [PATCH 142/352] Revert "tools/pydfu.py: Respect longer timeouts requested by DFU dev..." This reverts commit 4d6f60d4286b3e6ea8c9fb0141ba13e4978882b6. This implementation used the timeout as a maximum amount of time needed for the operation, when actually the spec and other tools suggest that it's the minumum delay needed between subsequent USB transfers. --- tools/pydfu.py | 52 ++++++++++---------------------------------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/tools/pydfu.py b/tools/pydfu.py index 8b8e338981b49..030f56bf852e3 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -22,8 +22,6 @@ import usb.core import usb.util import zlib -import time -import math # VID/PID __VID = 0x0483 @@ -31,8 +29,6 @@ # USB request __TIMEOUT __TIMEOUT = 4000 -__NEXT_TIMEOUT = 0 -__STATUS_TIMEOUT = 20000 # DFU commands __DFU_DETACH = 0 @@ -99,13 +95,6 @@ def get_string(dev, index): return usb.util.get_string(dev, index) -def timeout(): - global __NEXT_TIMEOUT - t = max(__TIMEOUT, __NEXT_TIMEOUT) - __NEXT_TIMEOUT = 0 - return int(math.ceil(t)) - - def find_dfu_cfg_descr(descr): if len(descr) == 9 and descr[0] == 9 and descr[1] == _DFU_DESCRIPTOR_TYPE: nt = collections.namedtuple( @@ -161,32 +150,17 @@ def init(): def abort_request(): """Sends an abort request.""" - __dev.ctrl_transfer(0x21, __DFU_ABORT, 0, __DFU_INTERFACE, None, timeout()) + __dev.ctrl_transfer(0x21, __DFU_ABORT, 0, __DFU_INTERFACE, None, __TIMEOUT) def clr_status(): """Clears any error status (perhaps left over from a previous session).""" - __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, timeout()) + __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, __TIMEOUT) def get_status(): """Get the status of the last operation.""" - _timeout = time.time() + max(__STATUS_TIMEOUT, timeout()) - stat = None - while time.time() < _timeout: - try: - stat = __dev.ctrl_transfer( - 0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, int(_timeout - time.time()) - ) - break - except usb.core.USBError as ex: - # If the firmware is blocked the transfer can timeout much quicker than - # the supplied timeout. If so, retry until the overall timeout is used up. - if "Operation timed out" not in str(ex): - raise - - if stat is None: - raise SystemExit("DFU: get_status timed out") + stat = __dev.ctrl_transfer(0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, 20000) # firmware can provide an optional string for any error if stat[5]: @@ -194,12 +168,6 @@ def get_status(): if message: print(message) - # firmware can send a longer timeout request while it's performing slow operation eg. erase - timeout_ms = stat[1] << 16 | stat[2] << 8 | stat[3] - if timeout_ms: - global __NEXT_TIMEOUT - __NEXT_TIMEOUT = __TIMEOUT + timeout_ms - return stat[4] @@ -212,9 +180,9 @@ def check_status(stage, expected): def mass_erase(): """Performs a MASS erase (i.e. erases the entire device).""" # Send DNLOAD with first byte=0x41 - __dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, "\x41", timeout()) + __dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, "\x41", __TIMEOUT) - # Execute erase and wait until complete + # Execute last command check_status("erase", __DFU_STATE_DFU_DOWNLOAD_BUSY) # Check command state @@ -228,7 +196,7 @@ def page_erase(addr): # Send DNLOAD with first byte=0x41 and page address buf = struct.pack(" Date: Tue, 9 Jun 2020 16:45:17 +1000 Subject: [PATCH 143/352] unix: Make manifest selection match other ports. Changes are: - The default manifest.py is moved to the variants directory (it's in "boards" in other ports). - The coverage variant now uses a custom manifest in its variant directory to add frzmpy/frzstr. - The frzmpy/frzstr tests are moved to variants/coverage/. --- ports/unix/Makefile | 2 +- ports/unix/manifest_coverage.py | 2 -- .../{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy1.py | 0 .../{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy2.py | 0 .../coverage/frzmpy}/frzmpy_pkg1/__init__.py | 0 .../coverage/frzmpy}/frzmpy_pkg2/mod.py | 0 .../{coverage-frzmpy => variants/coverage/frzmpy}/frzqstr.py | 0 .../{coverage-frzstr => variants/coverage/frzstr}/frzstr1.py | 0 .../coverage/frzstr}/frzstr_pkg1/__init__.py | 0 .../coverage/frzstr}/frzstr_pkg2/mod.py | 0 ports/unix/variants/coverage/manifest.py | 2 ++ ports/unix/variants/coverage/mpconfigvariant.mk | 2 +- ports/unix/{ => variants}/manifest.py | 0 13 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 ports/unix/manifest_coverage.py rename ports/unix/{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy1.py (100%) rename ports/unix/{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy2.py (100%) rename ports/unix/{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy_pkg1/__init__.py (100%) rename ports/unix/{coverage-frzmpy => variants/coverage/frzmpy}/frzmpy_pkg2/mod.py (100%) rename ports/unix/{coverage-frzmpy => variants/coverage/frzmpy}/frzqstr.py (100%) rename ports/unix/{coverage-frzstr => variants/coverage/frzstr}/frzstr1.py (100%) rename ports/unix/{coverage-frzstr => variants/coverage/frzstr}/frzstr_pkg1/__init__.py (100%) rename ports/unix/{coverage-frzstr => variants/coverage/frzstr}/frzstr_pkg2/mod.py (100%) create mode 100644 ports/unix/variants/coverage/manifest.py rename ports/unix/{ => variants}/manifest.py (100%) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index ec14166149ba8..3e2fa63a16c3b 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -14,7 +14,7 @@ include ../../py/mkenv.mk include $(VARIANT_DIR)/mpconfigvariant.mk # use FROZEN_MANIFEST for new projects, others are legacy -FROZEN_MANIFEST ?= manifest.py +FROZEN_MANIFEST ?= variants/manifest.py FROZEN_DIR = FROZEN_MPY_DIR = diff --git a/ports/unix/manifest_coverage.py b/ports/unix/manifest_coverage.py deleted file mode 100644 index 0c32d08578e94..0000000000000 --- a/ports/unix/manifest_coverage.py +++ /dev/null @@ -1,2 +0,0 @@ -freeze_as_str('coverage-frzstr') -freeze_as_mpy('coverage-frzmpy') diff --git a/ports/unix/coverage-frzmpy/frzmpy1.py b/ports/unix/variants/coverage/frzmpy/frzmpy1.py similarity index 100% rename from ports/unix/coverage-frzmpy/frzmpy1.py rename to ports/unix/variants/coverage/frzmpy/frzmpy1.py diff --git a/ports/unix/coverage-frzmpy/frzmpy2.py b/ports/unix/variants/coverage/frzmpy/frzmpy2.py similarity index 100% rename from ports/unix/coverage-frzmpy/frzmpy2.py rename to ports/unix/variants/coverage/frzmpy/frzmpy2.py diff --git a/ports/unix/coverage-frzmpy/frzmpy_pkg1/__init__.py b/ports/unix/variants/coverage/frzmpy/frzmpy_pkg1/__init__.py similarity index 100% rename from ports/unix/coverage-frzmpy/frzmpy_pkg1/__init__.py rename to ports/unix/variants/coverage/frzmpy/frzmpy_pkg1/__init__.py diff --git a/ports/unix/coverage-frzmpy/frzmpy_pkg2/mod.py b/ports/unix/variants/coverage/frzmpy/frzmpy_pkg2/mod.py similarity index 100% rename from ports/unix/coverage-frzmpy/frzmpy_pkg2/mod.py rename to ports/unix/variants/coverage/frzmpy/frzmpy_pkg2/mod.py diff --git a/ports/unix/coverage-frzmpy/frzqstr.py b/ports/unix/variants/coverage/frzmpy/frzqstr.py similarity index 100% rename from ports/unix/coverage-frzmpy/frzqstr.py rename to ports/unix/variants/coverage/frzmpy/frzqstr.py diff --git a/ports/unix/coverage-frzstr/frzstr1.py b/ports/unix/variants/coverage/frzstr/frzstr1.py similarity index 100% rename from ports/unix/coverage-frzstr/frzstr1.py rename to ports/unix/variants/coverage/frzstr/frzstr1.py diff --git a/ports/unix/coverage-frzstr/frzstr_pkg1/__init__.py b/ports/unix/variants/coverage/frzstr/frzstr_pkg1/__init__.py similarity index 100% rename from ports/unix/coverage-frzstr/frzstr_pkg1/__init__.py rename to ports/unix/variants/coverage/frzstr/frzstr_pkg1/__init__.py diff --git a/ports/unix/coverage-frzstr/frzstr_pkg2/mod.py b/ports/unix/variants/coverage/frzstr/frzstr_pkg2/mod.py similarity index 100% rename from ports/unix/coverage-frzstr/frzstr_pkg2/mod.py rename to ports/unix/variants/coverage/frzstr/frzstr_pkg2/mod.py diff --git a/ports/unix/variants/coverage/manifest.py b/ports/unix/variants/coverage/manifest.py new file mode 100644 index 0000000000000..6111050884222 --- /dev/null +++ b/ports/unix/variants/coverage/manifest.py @@ -0,0 +1,2 @@ +freeze_as_str("frzstr") +freeze_as_mpy("frzmpy") diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index ddb5027a97ef7..66e694e0a9ea0 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -11,7 +11,7 @@ CFLAGS += \ LDFLAGS += -fprofile-arcs -ftest-coverage -FROZEN_MANIFEST = manifest_coverage.py +FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py MICROPY_ROM_TEXT_COMPRESSION = 1 MICROPY_VFS_FAT = 1 diff --git a/ports/unix/manifest.py b/ports/unix/variants/manifest.py similarity index 100% rename from ports/unix/manifest.py rename to ports/unix/variants/manifest.py From 4050281311744ae5946aa1faec8aa75339bcab2b Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 9 Jun 2020 16:45:30 +1000 Subject: [PATCH 144/352] unix: Enable uasyncio on dev variant. --- ports/unix/variants/dev/manifest.py | 3 +++ ports/unix/variants/dev/mpconfigvariant.h | 4 ++++ ports/unix/variants/dev/mpconfigvariant.mk | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 ports/unix/variants/dev/manifest.py diff --git a/ports/unix/variants/dev/manifest.py b/ports/unix/variants/dev/manifest.py new file mode 100644 index 0000000000000..92a681116a687 --- /dev/null +++ b/ports/unix/variants/dev/manifest.py @@ -0,0 +1,3 @@ +include("$(PORT_DIR)/variants/manifest.py") + +include("$(MPY_DIR)/extmod/uasyncio/manifest.py") diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index eb65134717c6d..c2b959572abaa 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -30,3 +30,7 @@ #define MICROPY_PY_SYS_SETTRACE (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) + +#ifndef MICROPY_PY_UASYNCIO +#define MICROPY_PY_UASYNCIO (1) +#endif diff --git a/ports/unix/variants/dev/mpconfigvariant.mk b/ports/unix/variants/dev/mpconfigvariant.mk index bbd30b6238248..8a63b233d814e 100644 --- a/ports/unix/variants/dev/mpconfigvariant.mk +++ b/ports/unix/variants/dev/mpconfigvariant.mk @@ -1,5 +1,7 @@ PROG ?= micropython-dev +FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py + MICROPY_ROM_TEXT_COMPRESSION = 1 MICROPY_PY_BLUETOOTH = 1 From 9dfb4ae6aacd5eb4317f7a474b9da00c1cec9088 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 25 Jun 2020 22:55:40 +0200 Subject: [PATCH 145/352] nrf/bluetooth/ble_uart: Fix implicit declaration of function. mp_keyboard_interrupt() triggers a compiler error because the function is implicitly declared. This commit adds "py/runtime.h" to the includes. Fixes issue #5732. --- ports/nrf/drivers/bluetooth/ble_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index b94780e26011b..f6bc7f719c63c 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -31,6 +31,7 @@ #include "ringbuffer.h" #include "mphalport.h" #include "lib/utils/interrupt_char.h" +#include "py/runtime.h" #if MICROPY_PY_BLE_NUS From fc1f22a097ac2c6eb418605f36b53936e5138451 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 25 Jun 2020 23:06:00 +0200 Subject: [PATCH 146/352] nrf/bluetooth: Handle data length update request. The Bluetooth link gets disconnected when connecting from a PC after 30-40 seconds. This commit adds handling of the data length update request. The data length parameter pointer is set to NULL in the reply, letting the SoftDevice automatically set values and use them in the data length update procedure. --- ports/nrf/drivers/bluetooth/ble_drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 47f7f98885a91..1a64cdfbd24b9 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -1131,6 +1131,12 @@ static void ble_evt_handler(ble_evt_t * p_ble_evt) { BLE_DRIVER_LOG("GATTS EVT EXCHANGE MTU REQUEST\n"); (void)sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle, 23); // MAX MTU size break; + + case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: + BLE_DRIVER_LOG("BLE GAP EVT DATA LENGTH UPDATE REQUEST\n"); + sd_ble_gap_data_length_update(p_ble_evt->evt.gap_evt.conn_handle, NULL, NULL); + break; + #endif // (BLUETOOTH_SD == 132) || (BLUETOOTH_SD == 140) default: From ab0c14dba014d0ac27b8a5472bd795809a9be885 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 25 Jun 2020 23:32:28 +0200 Subject: [PATCH 147/352] nrf/bluetooth/ble_uart: Add mp_hal_stdio_poll function. This adds support for enabling MICROPY_PY_SYS_STDFILES when running UART over Bluetooth (NUS). --- ports/nrf/drivers/bluetooth/ble_uart.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index f6bc7f719c63c..86cd084746cd0 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -33,6 +33,10 @@ #include "lib/utils/interrupt_char.h" #include "py/runtime.h" +#if MICROPY_PY_SYS_STDFILES +#include "py/stream.h" +#endif + #if MICROPY_PY_BLE_NUS static ubluepy_uuid_obj_t uuid_obj_service = { @@ -136,6 +140,17 @@ void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { mp_hal_stdout_tx_strn(str, len); } +#if MICROPY_PY_SYS_STDFILES +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if ((poll_flags & MP_STREAM_POLL_RD) && ble_uart_enabled() + && !isBufferEmpty(mp_rx_ring_buffer)) { + ret |= MP_STREAM_POLL_RD; + } + return ret; +} +#endif + STATIC void gap_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); From 5996bf72f134dd8f1b72ecd20757dcb1c98cfc89 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 25 Jun 2020 23:41:35 +0200 Subject: [PATCH 148/352] nrf/bluetooth/ble_uart: Fix random advertisement name. The storage space of the advertisement name is not declared static, leading to a random advertisement name. This commit fixes the issue by declaring it static. --- ports/nrf/drivers/bluetooth/ble_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index 86cd084746cd0..b2e79ba44d65a 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -228,7 +228,7 @@ void ble_uart_init0(void) { ble_uart_peripheral.conn_handle = 0xFFFF; - char device_name[] = "mpus"; + static char device_name[] = "mpus"; mp_obj_t service_list = mp_obj_new_list(0, NULL); mp_obj_list_append(service_list, MP_OBJ_FROM_PTR(&ble_uart_service)); From f22f7b285e69aa05ce2dcf1e26de626dd1b1599d Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 25 Jun 2020 23:49:12 +0200 Subject: [PATCH 149/352] nrf/bluetooth/ble_uart: Swap end character on cooked strings. Changing line ending character of cooked strings makes rshell/pyboard.py work correctly over Bluetooth socat/pts devices. --- ports/nrf/drivers/bluetooth/ble_uart.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index b2e79ba44d65a..64e3a05f93192 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -136,8 +136,25 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } } +void ble_uart_tx_char(char c) { + // Not connected: drop output + if (!ble_uart_enabled()) return; + + ubluepy_characteristic_obj_t * p_char = &ble_uart_char_tx; + + ble_drv_attr_s_notify(p_char->p_service->p_periph->conn_handle, + p_char->handle, + 1, + (uint8_t *)&c); +} + void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { - mp_hal_stdout_tx_strn(str, len); + for (const char *top = str + len; str < top; str++) { + if (*str == '\n') { + ble_uart_tx_char('\r'); + } + ble_uart_tx_char(*str); + } } #if MICROPY_PY_SYS_STDFILES From c2317a3a8d5f184de2f816078d91be699274b94e Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sat, 27 Jun 2020 13:07:39 +0200 Subject: [PATCH 150/352] nrf/Makefile: Disable ROM text compression when compiling for debug. When compiling for debug (-O0) the .text segment cannot fit the flash region when MICROPY_ROM_TEXT_COMPRESSION=1, because the compiler does not optimise away the large if-else chain used to select the correct compressed string. This commit enforces MICROPY_ROM_TEXT_COMPRESSION=0 when compiling for debug (DEBUG=1). --- ports/nrf/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 23d5cd20d0d00..8f73336e0b1e8 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -39,7 +39,9 @@ endif QSTR_DEFS = qstrdefsport.h $(BUILD)/pins_qstr.h # MicroPython feature configurations +ifeq ($(DEBUG), 0) MICROPY_ROM_TEXT_COMPRESSION ?= 1 +endif # include py core make definitions include ../../py/py.mk From 15574cd665a8e66157aa97447e3869513da81d45 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 2 Jul 2020 22:19:00 +0200 Subject: [PATCH 151/352] nrf: Add support for time.ticks_xxx functions using RTC1. This commit adds time.ticks_ms/us support using RTC1 as the timebase. It also adds the time.ticks_add/diff helper functions. This feature can be enabled using MICROPY_PY_TIME_TICKS. If disabled the system uses the legacy sleep methods and does not have any ticks functions. In addition support for MICROPY_EVENT_POLL_HOOK was added to the time.sleep_ms(x) function, making this function more power efficient and allows support for select.poll/asyncio. To support this, the RTC's CCR0 was used to schedule a ~1msec event to wakeup the CPU. Some important notes about the RTC timebase: - Since the granularity of RTC1's ticks are approx 30usec, time.ticks_us is not perfect, does not have 1us resolution, but is otherwise quite usable. For tighter measurments the ticker's 1MHz counter should be used. - time.ticks_ms(x) should *not* be called in an IRQ with higher prio than the RTC overflow irq (3). If so it introduces a race condition and possibly leads to wrong tick calculations. See #6171 and #6202. --- ports/nrf/main.c | 5 + ports/nrf/modules/machine/rtcounter.c | 7 ++ ports/nrf/modules/utime/modutime.c | 4 + ports/nrf/mpconfigport.h | 10 ++ ports/nrf/mphalport.c | 143 +++++++++++++++++++++++++- ports/nrf/mphalport.h | 13 ++- 6 files changed, 174 insertions(+), 8 deletions(-) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 3c5d0a05d87f1..670c88e7cad9d 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -52,6 +52,8 @@ #include "i2c.h" #include "adc.h" #include "rtcounter.h" +#include "mphalport.h" + #if MICROPY_PY_MACHINE_HW_PWM #include "pwm.h" #endif @@ -101,6 +103,9 @@ int main(int argc, char **argv) { soft_reset: + #if MICROPY_PY_TIME_TICKS + rtc1_init_time_ticks(); + #endif led_init(); diff --git a/ports/nrf/modules/machine/rtcounter.c b/ports/nrf/modules/machine/rtcounter.c index 5fb28557d8230..c9f907774e6a3 100644 --- a/ports/nrf/modules/machine/rtcounter.c +++ b/ports/nrf/modules/machine/rtcounter.c @@ -153,6 +153,13 @@ STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s int rtc_id = rtc_find(args[ARG_id].u_obj); + #if MICROPY_PY_TIME_TICKS + if (rtc_id == 1) { + // time module uses RTC1, prevent using it + mp_raise_ValueError(MP_ERROR_TEXT("RTC1 reserved by time module")); + } + #endif + // const and non-const part of the RTC object. const machine_rtc_obj_t * self = &machine_rtc_obj[rtc_id]; machine_rtc_config_t *config = self->config; diff --git a/ports/nrf/modules/utime/modutime.c b/ports/nrf/modules/utime/modutime.c index 60cdbe4f33140..bb29141011eab 100644 --- a/ports/nrf/modules/utime/modutime.c +++ b/ports/nrf/modules/utime/modutime.c @@ -42,6 +42,10 @@ STATIC const mp_rom_map_elem_t time_module_globals_table[] = { { 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_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, }; STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 28076114fe42a..fc34a7cf268df 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -174,6 +174,9 @@ #define MICROPY_PY_MACHINE_RTCOUNTER (0) #endif +#ifndef MICROPY_PY_TIME_TICKS +#define MICROPY_PY_TIME_TICKS (0) +#endif #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) @@ -317,6 +320,13 @@ extern const struct _mp_obj_module_t ble_module; /* micro:bit root pointers */ \ void *async_data[2]; \ +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + __WFI(); \ + } while (0); + #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) // We need to provide a declaration/definition of alloca() diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 4469adc80d84f..b8e4c2e4d8b55 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -35,6 +35,121 @@ #include "nrfx_errors.h" #include "nrfx_config.h" +#if MICROPY_PY_TIME_TICKS +#include "nrfx_rtc.h" +#include "nrf_clock.h" +#endif + +#if MICROPY_PY_TIME_TICKS + +// Use RTC1 for time ticks generation (ms and us) with 32kHz tick resolution +// and overflow handling in RTC IRQ. + +#define RTC_TICK_INCREASE_MSEC (33) + +#define RTC_RESCHEDULE_CC(rtc, cc_nr, ticks) \ + do { \ + nrfx_rtc_cc_set(&rtc, cc_nr, nrfx_rtc_counter_get(&rtc) + ticks, true); \ + } while (0); + +// RTC overflow irq handling notes: +// - If has_overflowed is set it could be before or after COUNTER is read. +// If before then an adjustment must be made, if after then no adjustment is necessary. +// - The before case is when COUNTER is very small (because it just overflowed and was set to zero), +// the after case is when COUNTER is very large (because it's just about to overflow +// but we read it right before it overflows). +// - The extra check for counter is to distinguish these cases. 1<<23 because it's halfway +// between min and max values of COUNTER. +#define RTC1_GET_TICKS_ATOMIC(rtc, overflows, counter) \ + do { \ + rtc.p_reg->INTENCLR = RTC_INTENCLR_OVRFLW_Msk; \ + overflows = rtc_overflows; \ + counter = rtc.p_reg->COUNTER; \ + uint32_t has_overflowed = rtc.p_reg->EVENTS_OVRFLW; \ + if (has_overflowed && counter < (1 << 23)) { \ + overflows += 1; \ + } \ + rtc.p_reg->INTENSET = RTC_INTENSET_OVRFLW_Msk; \ + } while (0); + +nrfx_rtc_t rtc1 = NRFX_RTC_INSTANCE(1); +volatile mp_uint_t rtc_overflows = 0; + +const nrfx_rtc_config_t rtc_config_time_ticks = { + .prescaler = 0, + .reliable = 0, + .tick_latency = 0, + #ifdef NRF51 + .interrupt_priority = 1, + #else + .interrupt_priority = 3, + #endif +}; + +STATIC void rtc_irq_time(nrfx_rtc_int_type_t event) { + // irq handler for overflow + if (event == NRFX_RTC_INT_OVERFLOW) { + rtc_overflows += 1; + } + // irq handler for wakeup from WFI (~1msec) + if (event == NRFX_RTC_INT_COMPARE0) { + RTC_RESCHEDULE_CC(rtc1, 0, RTC_TICK_INCREASE_MSEC) + } +} + +void rtc1_init_time_ticks(void) { + // Start the low-frequency clock (if it hasn't been started already) + if (!nrf_clock_lf_is_running(NRF_CLOCK)) { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + } + // Uninitialize first, then set overflow IRQ and first CC event + nrfx_rtc_uninit(&rtc1); + nrfx_rtc_init(&rtc1, &rtc_config_time_ticks, rtc_irq_time); + nrfx_rtc_overflow_enable(&rtc1, true); + RTC_RESCHEDULE_CC(rtc1, 0, RTC_TICK_INCREASE_MSEC) + nrfx_rtc_enable(&rtc1); +} + +mp_uint_t mp_hal_ticks_ms(void) { + // Compute: (rtc_overflows << 24 + COUNTER) * 1000 / 32768 + // + // Note that COUNTER * 1000 / 32768 would overflow during calculation, so use + // the less obvious * 125 / 4096 calculation (overflow secure). + // + // Make sure not to call this function within an irq with higher prio than the + // RTC's irq. This would introduce the danger of preempting the RTC irq and + // calling mp_hal_ticks_ms() at that time would return a false result. + uint32_t overflows; + uint32_t counter; + // guard against overflow irq + RTC1_GET_TICKS_ATOMIC(rtc1, overflows, counter) + return (overflows << 9) * 1000 + (counter * 125 / 4096); +} + +mp_uint_t mp_hal_ticks_us(void) { + // Compute: ticks_us = (overflows << 24 + counter) * 1000000 / 32768 + // = (overflows << 15 * 15625) + (counter * 15625 / 512) + // Since this function is likely to be called in a poll loop it must + // be fast, using an optimized 64bit mult/divide. + uint32_t overflows; + uint32_t counter; + // guard against overflow irq + RTC1_GET_TICKS_ATOMIC(rtc1, overflows, counter) + // first compute counter * 15625 + uint32_t counter_lo = (counter & 0xffff) * 15625; + uint32_t counter_hi = (counter >> 16) * 15625; + // actual value is counter_hi << 16 + counter_lo + return ((overflows << 15) * 15625) + ((counter_hi << 7) + (counter_lo >> 9)); +} + +#else + +mp_uint_t mp_hal_ticks_ms(void) { + return 0; +} + +#endif + // this table converts from HAL_StatusTypeDef to POSIX errno const byte mp_hal_status_to_errno_table[4] = { [HAL_OK] = 0, @@ -70,7 +185,7 @@ int mp_hal_stdin_rx_chr(void) { if (MP_STATE_PORT(board_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(board_stdio_uart))) { return uart_rx_char(MP_STATE_PORT(board_stdio_uart)); } - __WFI(); + MICROPY_EVENT_POLL_HOOK } return 0; @@ -93,6 +208,31 @@ void mp_hal_stdout_tx_str(const char *str) { mp_hal_stdout_tx_strn(str, strlen(str)); } +#if MICROPY_PY_TIME_TICKS + +void mp_hal_delay_us(mp_uint_t us) { + uint32_t now; + if (us == 0) { + return; + } + now = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - now < us) { + } +} + +void mp_hal_delay_ms(mp_uint_t ms) { + uint32_t now; + if (ms == 0) { + return; + } + now = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - now < ms) { + MICROPY_EVENT_POLL_HOOK + } +} + +#else + void mp_hal_delay_us(mp_uint_t us) { if (us == 0) { return; @@ -175,6 +315,7 @@ void mp_hal_delay_ms(mp_uint_t ms) { mp_hal_delay_us(999); } } +#endif #if defined(NRFX_LOG_ENABLED) && (NRFX_LOG_ENABLED == 1) diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index 5614be29fa29f..15b37b7ef22d2 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -41,12 +41,6 @@ typedef enum HAL_TIMEOUT = 0x03 } HAL_StatusTypeDef; -static inline uint32_t hal_tick_fake(void) { - return 0; -} - -#define mp_hal_ticks_ms hal_tick_fake // TODO: implement. Right now, return 0 always - extern const unsigned char mp_hal_status_to_errno_table[4]; NORETURN void mp_hal_raise(HAL_StatusTypeDef status); @@ -70,10 +64,15 @@ const char *nrfx_error_code_lookup(uint32_t err_code); #define mp_hal_pin_od_high(p) mp_hal_pin_high(p) #define mp_hal_pin_open_drain(p) nrf_gpio_cfg_input(p->pin, NRF_GPIO_PIN_NOPULL) +#if MICROPY_PY_TIME_TICKS +void rtc1_init_time_ticks(); +#else +mp_uint_t mp_hal_ticks_ms(void); +#define mp_hal_ticks_us() (0) +#endif // TODO: empty implementation for now. Used by machine_spi.c:69 #define mp_hal_delay_us_fast(p) -#define mp_hal_ticks_us() (0) #define mp_hal_ticks_cpu() (0) #endif From 59ed3bdd9fa8dc6e0677cf8d4478a5a32fee4500 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Thu, 2 Jul 2020 22:19:11 +0200 Subject: [PATCH 152/352] nrf: Enable nrf tick support on all boards by default. Having time.ticks_ms/us/add/diff is very useful and used by many drivers, libraries and components. --- ports/nrf/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index fc34a7cf268df..c5304b20d0d3e 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -175,7 +175,7 @@ #endif #ifndef MICROPY_PY_TIME_TICKS -#define MICROPY_PY_TIME_TICKS (0) +#define MICROPY_PY_TIME_TICKS (1) #endif #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) From f5dd46b4791249c5989859b07c4886c69d8b98b3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 2 Jul 2020 16:12:59 +1000 Subject: [PATCH 153/352] unix/variants: Enable VFS and all supported filesystems on dev variant. So that micropython-dev can be used to test VFS code, and inspect and build filesystem images that are compatible with bare-metal systems. Signed-off-by: Damien George --- ports/unix/variants/dev/mpconfigvariant.h | 21 +++++++++++++++------ ports/unix/variants/dev/mpconfigvariant.mk | 4 +++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index c2b959572abaa..7c3e84cc44e11 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -24,13 +24,22 @@ * THE SOFTWARE. */ -#define MICROPY_REPL_EMACS_WORDS_MOVE (1) -#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) -#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_READER_VFS (1) +#define MICROPY_REPL_EMACS_WORDS_MOVE (1) +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_VFS (1) +#define MICROPY_VFS_POSIX (1) -#define MICROPY_PY_SYS_SETTRACE (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_SYS_SETTRACE (1) +#define MICROPY_PY_UOS_VFS (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #ifndef MICROPY_PY_UASYNCIO -#define MICROPY_PY_UASYNCIO (1) +#define MICROPY_PY_UASYNCIO (1) #endif + +// Use vfs's functions for import stat and builtin open. +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open mp_vfs_open +#define mp_builtin_open_obj mp_vfs_open_obj diff --git a/ports/unix/variants/dev/mpconfigvariant.mk b/ports/unix/variants/dev/mpconfigvariant.mk index 8a63b233d814e..1f8611b6fc30b 100644 --- a/ports/unix/variants/dev/mpconfigvariant.mk +++ b/ports/unix/variants/dev/mpconfigvariant.mk @@ -3,5 +3,7 @@ PROG ?= micropython-dev FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py MICROPY_ROM_TEXT_COMPRESSION = 1 - +MICROPY_VFS_FAT = 1 +MICROPY_VFS_LFS1 = 1 +MICROPY_VFS_LFS2 = 1 MICROPY_PY_BLUETOOTH = 1 From 8594389fe75efc3903970db319f07f89fdb8c8cc Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 30 Jun 2020 22:59:28 +0200 Subject: [PATCH 154/352] stm32/fdcan: Use the right FIFO to calc element address in can_receive. --- ports/stm32/fdcan.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index b3b1da998c79d..bafc7e3f5ad7f 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -218,8 +218,14 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, } // Get pointer to incoming message - uint32_t index = (can->Instance->RXF0S & FDCAN_RXF0S_F0GI) >> 8; - uint32_t *address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * can->Init.RxFifo0ElmtSize * 4)); + uint32_t index, *address; + if (fifo == FDCAN_RX_FIFO0) { + index = (*rxf & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * can->Init.RxFifo0ElmtSize * 4)); + } else { + index = (*rxf & FDCAN_RXF1S_F1GI) >> FDCAN_RXF1S_F1GI_Pos; + address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * can->Init.RxFifo1ElmtSize * 4)); + } // Parse header of message hdr->IdType = *address & FDCAN_ELEMENT_MASK_XTD; From 63b2eb27d44a9beb9f66abd77d3b6526fdb2482d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 30 Jun 2020 23:04:57 +0200 Subject: [PATCH 155/352] stm32/fdcan: Use FDCAN_RXFxS_FxFL instead of hard-coded value. --- ports/stm32/fdcan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index bafc7e3f5ad7f..3ab72cdace1a8 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -200,17 +200,21 @@ void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) { int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, uint8_t *data, uint32_t timeout_ms) { volatile uint32_t *rxf, *rxa; + uint32_t fl; + if (fifo == FDCAN_RX_FIFO0) { rxf = &can->Instance->RXF0S; rxa = &can->Instance->RXF0A; + fl = FDCAN_RXF0S_F0FL; } else { rxf = &can->Instance->RXF1S; rxa = &can->Instance->RXF1A; + fl = FDCAN_RXF1S_F1FL; } // Wait for a message to become available, with timeout uint32_t start = HAL_GetTick(); - while ((*rxf & 7) == 0) { + while ((*rxf & fl) == 0) { MICROPY_EVENT_POLL_HOOK if (HAL_GetTick() - start >= timeout_ms) { return -MP_ETIMEDOUT; From d07073f4e2f37d9614340f7544985dfee834532e Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 30 Jun 2020 23:08:20 +0200 Subject: [PATCH 156/352] stm32/fdcan: Support maximum timeout of HAL_MAX_DELAY in can_receive. --- ports/stm32/fdcan.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 3ab72cdace1a8..2892572f40a96 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -215,10 +215,12 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, // Wait for a message to become available, with timeout uint32_t start = HAL_GetTick(); while ((*rxf & fl) == 0) { - MICROPY_EVENT_POLL_HOOK - if (HAL_GetTick() - start >= timeout_ms) { - return -MP_ETIMEDOUT; + if (timeout_ms != HAL_MAX_DELAY) { + if (HAL_GetTick() - start >= timeout_ms) { + return -MP_ETIMEDOUT; + } } + MICROPY_EVENT_POLL_HOOK } // Get pointer to incoming message From c299cc94e334a5a9576e15e0f046e24810c4c75a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 30 Jun 2020 23:13:57 +0200 Subject: [PATCH 157/352] stm32/pyb_can: Handle timeout arg for FDCAN in pyb_can_send. Following the documented pyb can_send behavior in pyb.CAN docs. --- ports/stm32/pyb_can.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index ad2efbef4be1a..224b8f28b0eab 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -432,6 +432,20 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * HAL_StatusTypeDef status; #if MICROPY_HW_ENABLE_FDCAN + uint32_t timeout_ms = args[ARG_timeout].u_int; + uint32_t start = HAL_GetTick(); + while (HAL_FDCAN_GetTxFifoFreeLevel(&self->can) == 0) { + if (timeout_ms == 0) { + mp_raise_OSError(MP_ETIMEDOUT); + } + // Check for the Timeout + if (timeout_ms != HAL_MAX_DELAY) { + if (HAL_GetTick() - start >= timeout_ms) { + mp_raise_OSError(MP_ETIMEDOUT); + } + } + MICROPY_EVENT_POLL_HOOK + } status = HAL_FDCAN_AddMessageToTxFifoQ(&self->can, &tx_msg, tx_data); #else self->can.pTxMsg = &tx_msg; From b6146ca1a11d9db6fa2276920f2c3c32d9bc4457 Mon Sep 17 00:00:00 2001 From: Thomas Friebel Date: Sun, 5 Jul 2020 16:48:27 +0200 Subject: [PATCH 158/352] extmod/nimble: Fix attr NULL ptr dereference in ble_gatt_attr_read_cb. In case of error, NimBLE calls the read callback with attr = NULL. --- extmod/nimble/modbluetooth_nimble.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 4e0ca88efa468..be7d13d9e6f5d 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -846,7 +846,7 @@ STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_err if (error->status == 0) { gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, attr->handle, attr->om); } - mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, attr->handle, error->status); + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, attr ? attr->handle : -1, error->status); return 0; } From f743bd3d2581596ecc06973ffc52b429931b19fe Mon Sep 17 00:00:00 2001 From: Alex Tsamakos Date: Tue, 23 Jun 2020 02:17:17 +0200 Subject: [PATCH 159/352] nrf/boards: Add initial support for Actinius Icarus. Example make command: make BOARD=actinius_icarus --- ports/nrf/README.md | 2 + .../boards/actinius_icarus/mpconfigboard.h | 81 +++++++++++++++++++ .../boards/actinius_icarus/mpconfigboard.mk | 6 ++ ports/nrf/boards/actinius_icarus/pins.csv | 32 ++++++++ 4 files changed, 121 insertions(+) create mode 100644 ports/nrf/boards/actinius_icarus/mpconfigboard.h create mode 100644 ports/nrf/boards/actinius_icarus/mpconfigboard.mk create mode 100644 ports/nrf/boards/actinius_icarus/pins.csv diff --git a/ports/nrf/README.md b/ports/nrf/README.md index b5f39267ebf2f..a02548afe1f9a 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -43,6 +43,7 @@ This is a port of MicroPython to the Nordic Semiconductor nRF series of chips. * [Particle Xenon](https://docs.particle.io/xenon/) * nRF9160 * [PCA10090](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF9160-DK) + * [Actinius Icarus](https://www.actinius.com/icarus) ## Compile and Flash @@ -135,6 +136,7 @@ pca10056 | s140 | Peripheral and Central | [Segge pca10059 | s140 | Peripheral and Central | Manual, SWDIO and SWCLK solder points on the sides. particle_xenon | s140 | Peripheral and Central | [Black Magic Probe](#black-magic-probe-targets) pca10090 | None (bsdlib.a) | None (LTE/GNSS) | [Segger](#segger-targets) +actinius_icarus | None (bsdlib.a) | None (LTE/GNSS) | [Segger](#segger-targets) ## IDAP-M/IDAP-Link Targets diff --git a/ports/nrf/boards/actinius_icarus/mpconfigboard.h b/ports/nrf/boards/actinius_icarus/mpconfigboard.h new file mode 100644 index 0000000000000..e344d2d6d0968 --- /dev/null +++ b/ports/nrf/boards/actinius_icarus/mpconfigboard.h @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Actinius + * + * 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. + */ + +#define ACTINIUS_ICARUS + +#define MICROPY_HW_BOARD_NAME "Actinius Icarus" +#define MICROPY_HW_MCU_NAME "NRF9160" +#define MICROPY_PY_SYS_PLATFORM "nrf9160" + +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_HW_PWM (0) +#define MICROPY_PY_MACHINE_HW_SPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_RTCOUNTER (0) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_ADC (0) +#define MICROPY_PY_MACHINE_TEMP (0) +#define MICROPY_PY_RANDOM_HW_RNG (0) + +#define MICROPY_MBFS (0) +#define MICROPY_VFS (1) + +#define MICROPY_HW_HAS_LED (1) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (0) +#define MICROPY_HW_HAS_SDCARD (0) +#define MICROPY_HW_HAS_MMA7660 (0) +#define MICROPY_HW_HAS_LIS3DSH (0) +#define MICROPY_HW_HAS_LCD (0) +#define MICROPY_HW_ENABLE_RNG (0) +#define MICROPY_HW_ENABLE_RTC (0) +#define MICROPY_HW_ENABLE_TIMER (0) +#define MICROPY_HW_ENABLE_SERVO (0) +#define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_CAN (0) + +#define MICROPY_HW_LED_TRICOLOR (1) +#define MICROPY_HW_LED_PULLUP (1) + +#define MICROPY_HW_LED_RED (10) // LED1 / RED +#define MICROPY_HW_LED_GREEN (11) // LED2 / GREEN +#define MICROPY_HW_LED_BLUE (12) // LED3 / BLUE + +// UART config +#define MICROPY_HW_UART1_RX (6) +#define MICROPY_HW_UART1_TX (9) +#define MICROPY_HW_UART1_CTS (25) +#define MICROPY_HW_UART1_RTS (7) +#define MICROPY_HW_UART1_HWFC (1) + +// SPI0 config +#define MICROPY_HW_SPI0_NAME "SPI0" + +#define MICROPY_HW_SPI0_SCK (20) +#define MICROPY_HW_SPI0_MOSI (21) +#define MICROPY_HW_SPI0_MISO (22) + +#define HELP_TEXT_BOARD_LED "1,2,3" diff --git a/ports/nrf/boards/actinius_icarus/mpconfigboard.mk b/ports/nrf/boards/actinius_icarus/mpconfigboard.mk new file mode 100644 index 0000000000000..c1e05fd306272 --- /dev/null +++ b/ports/nrf/boards/actinius_icarus/mpconfigboard.mk @@ -0,0 +1,6 @@ +MCU_SERIES = m33 +MCU_VARIANT = nrf91 +MCU_SUB_VARIANT = nrf9160 +LD_FILES += boards/nrf9160_1M_256k.ld + +NRF_DEFINES += -DNRF9160_XXAA -DNRF_TRUSTZONE_NONSECURE diff --git a/ports/nrf/boards/actinius_icarus/pins.csv b/ports/nrf/boards/actinius_icarus/pins.csv new file mode 100644 index 0000000000000..50c284dfdc5cc --- /dev/null +++ b/ports/nrf/boards/actinius_icarus/pins.csv @@ -0,0 +1,32 @@ +P0,P0 +P1,P1 +P2,P2 +P3,P3 +P4,P4 +BUTTON,P5 +UART_RX,P6 +UART_RTS,P7 +SIM_SELECT,P8 +UART_TX,P9 +LED1,P10 +LED2,P11 +LED3,P12 +BAT_SENSE,P13 +A1,P14 +A2,P15 +A3,P16 +A4,P17 +A5,P18 +A6,P19 +SPI_SCK,P20 +SPI_MOSI,P21 +SPI_MISO,P22 +P23,P23 +P24,P24 +UART_CTS,P25 +I2C_SDA,P26 +I2C_SCL,P27 +ACCEL_INT1,P28 +ACCEL_INT2,P29 +P30,P30 +P31,P31 From b776fe69692e92497c7aae0f38dfcbe14b2ca024 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 9 Jul 2020 20:41:11 +0200 Subject: [PATCH 160/352] nrf/nrfx_config: Disable RTC2 for nRF9160 targets. nRF9160 does not have any RTC2. Disable the configuration in case of NRF9160_XXAA. --- ports/nrf/nrfx_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 19c744510feb5..beb6b34ab368d 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -130,7 +130,7 @@ #define NRFX_RTC_ENABLED (MICROPY_PY_MACHINE_RTCOUNTER) #define NRFX_RTC0_ENABLED 1 #define NRFX_RTC1_ENABLED 1 -#define NRFX_RTC2_ENABLED (!NRF51) +#define NRFX_RTC2_ENABLED (!NRF51) && (!NRF9160_XXAA) #define NRFX_TIMER_ENABLED (MICROPY_PY_MACHINE_TIMER) #define NRFX_TIMER0_ENABLED 1 From 95d0d1c48675c946a3c3dd1c2f9ab293f21bb943 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 9 Jul 2020 21:03:09 +0200 Subject: [PATCH 161/352] nrf/boards: Enable RTCounter machine module for nrf9160 boards. Resolves dependencies for MICROPY_PY_TIME_TICKS which requires to link against nrfx_rtc.c functions by setting MICROPY_PY_MACHINE_RTCOUNTER to 1. --- ports/nrf/boards/actinius_icarus/mpconfigboard.h | 2 +- ports/nrf/boards/pca10090/mpconfigboard.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/nrf/boards/actinius_icarus/mpconfigboard.h b/ports/nrf/boards/actinius_icarus/mpconfigboard.h index e344d2d6d0968..e99d731df941d 100644 --- a/ports/nrf/boards/actinius_icarus/mpconfigboard.h +++ b/ports/nrf/boards/actinius_icarus/mpconfigboard.h @@ -34,7 +34,7 @@ #define MICROPY_PY_MACHINE_HW_PWM (0) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_TIMER (1) -#define MICROPY_PY_MACHINE_RTCOUNTER (0) +#define MICROPY_PY_MACHINE_RTCOUNTER (1) #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_ADC (0) #define MICROPY_PY_MACHINE_TEMP (0) diff --git a/ports/nrf/boards/pca10090/mpconfigboard.h b/ports/nrf/boards/pca10090/mpconfigboard.h index e4e514290949b..28381c40d1e6f 100644 --- a/ports/nrf/boards/pca10090/mpconfigboard.h +++ b/ports/nrf/boards/pca10090/mpconfigboard.h @@ -34,7 +34,7 @@ #define MICROPY_PY_MACHINE_HW_PWM (0) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_TIMER (0) -#define MICROPY_PY_MACHINE_RTCOUNTER (0) +#define MICROPY_PY_MACHINE_RTCOUNTER (1) #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_ADC (0) #define MICROPY_PY_MACHINE_TEMP (0) From 411e1157e7ea9ee65db5e78bdd5d2ee577d92cc2 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Fri, 10 Jul 2020 19:55:35 +0200 Subject: [PATCH 162/352] travis: Install newer toolchain for nrf job. Update toolchain to GNU Arm Embedded Toolchain. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f6fe3e1e4e39f..2f86345ae8bcb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -292,7 +292,10 @@ jobs: - stage: test name: "nrf port build" install: - - sudo apt-get install gcc-arm-none-eabi + # need newer gcc version for Cortex-M33 support + - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa + - sudo apt-get update -qq || true + - sudo apt-get install gcc-arm-embedded - sudo apt-get install libnewlib-arm-none-eabi - arm-none-eabi-gcc --version script: From d9e8edc8bcff7102d655b63c672ccb44adeca688 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Fri, 10 Jul 2020 09:19:34 +0200 Subject: [PATCH 163/352] travis: Add pca10090 build to nrf job. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2f86345ae8bcb..8eb5ef6bae514 100644 --- a/.travis.yml +++ b/.travis.yml @@ -304,6 +304,7 @@ jobs: - make ${MAKEOPTS} -C ports/nrf BOARD=pca10040 - make ${MAKEOPTS} -C ports/nrf BOARD=microbit - make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 SD=s132 + - make ${MAKEOPTS} -C ports/nrf BOARD=pca10090 # bare-arm and minimal ports, with size-diff check - stage: test From 486cb6dd4a1a17ff2567f721855d4626fdf69412 Mon Sep 17 00:00:00 2001 From: Matt Trentini Date: Mon, 18 May 2020 14:45:46 +1000 Subject: [PATCH 164/352] nrf: Add board definition for nRF52840-MDK-USB-Dongle. --- ports/nrf/README.md | 1 + ports/nrf/boards/memory.ld | 7 +- .../boards/nrf52840-mdk-usb-dongle/README.md | 49 +++++++++++++ .../nrf52840-mdk-usb-dongle/mpconfigboard.h | 71 +++++++++++++++++++ .../nrf52840-mdk-usb-dongle/mpconfigboard.mk | 7 ++ .../nrf52840_open_bootloader.ld | 2 + .../boards/nrf52840-mdk-usb-dongle/pins.csv | 17 +++++ 7 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 ports/nrf/boards/nrf52840-mdk-usb-dongle/README.md create mode 100644 ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h create mode 100644 ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk create mode 100644 ports/nrf/boards/nrf52840-mdk-usb-dongle/nrf52840_open_bootloader.ld create mode 100644 ports/nrf/boards/nrf52840-mdk-usb-dongle/pins.csv diff --git a/ports/nrf/README.md b/ports/nrf/README.md index a02548afe1f9a..b08a034561e0c 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -41,6 +41,7 @@ This is a port of MicroPython to the Nordic Semiconductor nRF series of chips. * [PCA10056](http://www.nordicsemi.com/eng/Products/nRF52840-Preview-DK) * [PCA10059](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) * [Particle Xenon](https://docs.particle.io/xenon/) + * [nRF52840 MDK USB Dongle](boards/nrf52840-mdk-usb-dongle/README.md) * nRF9160 * [PCA10090](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF9160-DK) * [Actinius Icarus](https://www.actinius.com/icarus) diff --git a/ports/nrf/boards/memory.ld b/ports/nrf/boards/memory.ld index c95daf3d94fa0..f1f9a2a4c48b8 100644 --- a/ports/nrf/boards/memory.ld +++ b/ports/nrf/boards/memory.ld @@ -1,14 +1,17 @@ /* Flash layout: softdevice | application | filesystem */ /* RAM layout: softdevice RAM | application RAM */ -_sd_size = DEFINED(_sd_size) ? _sd_size : 0; + +_ram_start = DEFINED(_ram_start) ? _ram_start : 0x20000000; +_flash_start = DEFINED(_flash_start) ? _flash_start : 0; +_sd_size = DEFINED(_sd_size) ? _sd_size : _flash_start; _sd_ram = DEFINED(_sd_ram) ? _sd_ram : 0; _fs_size = DEFINED(_fs_size) ? _fs_size : 64K; /* TODO: set to 0 if not using the filesystem */ _app_size = _flash_size - _sd_size - _fs_size; _app_start = _sd_size; _fs_start = _sd_size + _app_size; _fs_end = _fs_start + _fs_size; -_app_ram_start = 0x20000000 + _sd_ram; +_app_ram_start = _ram_start + _sd_ram; _app_ram_size = _ram_size - _sd_ram; _heap_start = _ebss; _heap_end = _ram_end - _stack_size; diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/README.md b/ports/nrf/boards/nrf52840-mdk-usb-dongle/README.md new file mode 100644 index 0000000000000..c39a800d776cf --- /dev/null +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/README.md @@ -0,0 +1,49 @@ +nRF52840 MDK USB Dongle +======================= + +The *[nRF52840 MDK USB +Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle)* is a small, +low-cost development board in a USB dongle form-factor powered by an nRF52840 +with 1MB flash and 256KB RAM. + +This device is pre-installed with [Open +Bootloader](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/programming/), +allowing DFU upgrades over USB using Nordic [nRF +Connect](https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Connect-for-desktop) +or [nrfutil](https://github.com/NordicSemiconductor/pc-nrfutil/). To support +Open Bootloader, the flash and memory layout must be adjusted slightly (details +[here](https://devzone.nordicsemi.com/nordic/short-range-guides/b/getting-started/posts/nrf52840-dongle-programming-tutorial)) +from the typical nRF build; this board definition ensure the appropriate build +configuration is used for MicroPython. + + +Pinout +------ + +The [pinout +diagram](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/#pinout-diagram) +provides an overview of the available pins and their capabilities. All pins are +available in MicroPython, using the pin numbers labelled in the diagram +(excluding the leading port number, *P0*). + +The three LEDs are available either through the usual `Pin` mechanism - pins +22-24 - or by `board.LED(n)` where n can be 1, 2 or 3. + + +Build instructions +------------------ + +Follow the standard [nRF Port build instructions](../../README.md); but use +`nrf52840-mdk-usb-dongle` as the value for `BOARD`: + + make BOARD=nrf52840-mdk-usb-dongle + +The build artifacts will be created in `build-nrf52840-mdk-usb-dongle`. Once +built, the easiest way to deploy to the device is to open `firmware.hex` using +*nRF Connect* and select *Write*. Detailed instructions can be found on the +[developer +wiki](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/programming/). + +**Note** that the regular method of deployment for the MicroPython nRF port +(using `make deploy`) will *not* operate correctly and will overwrite the +bootloader. diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h new file mode 100644 index 0000000000000..f048c39e0efdd --- /dev/null +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Matt Trentini + * + * 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. + */ + +#define MICROPY_HW_BOARD_NAME "MDK-USB-DONGLE" +#define MICROPY_HW_MCU_NAME "NRF52840" +#define MICROPY_PY_SYS_PLATFORM "nrf52840-MDK-USB-Dongle" + +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_HW_PWM (1) +#define MICROPY_PY_MACHINE_HW_SPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_RTCOUNTER (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_ADC (1) +#define MICROPY_PY_MACHINE_TEMP (1) + +#define MICROPY_HW_ENABLE_RNG (1) + +#define MICROPY_HW_USB_CDC (1) + +#define MICROPY_HW_HAS_LED (1) +#define MICROPY_HW_LED_COUNT (3) +#define MICROPY_HW_LED_PULLUP (1) + +#define MICROPY_HW_LED1 (22) // LED1 GREEN +#define MICROPY_HW_LED2 (23) // LED2 RED +#define MICROPY_HW_LED3 (24) // LED3 BLUE + +// UART config +#define MICROPY_HW_UART1_RX (7) +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_CTS (9) +#define MICROPY_HW_UART1_RTS (10) +#define MICROPY_HW_UART1_HWFC (1) + +// SPI0 config +#define MICROPY_HW_SPI0_NAME "SPI0" + +#define MICROPY_HW_SPI0_SCK (19) +#define MICROPY_HW_SPI0_MOSI (20) +#define MICROPY_HW_SPI0_MISO (21) + +#define MICROPY_HW_PWM0_NAME "PWM0" +#define MICROPY_HW_PWM1_NAME "PWM1" +#define MICROPY_HW_PWM2_NAME "PWM2" +#define MICROPY_HW_PWM3_NAME "PWM3" + +#define HELP_TEXT_BOARD_LED "1,2,3" diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk new file mode 100644 index 0000000000000..f98d5c88a9076 --- /dev/null +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk @@ -0,0 +1,7 @@ +MCU_SERIES = m4 +MCU_VARIANT = nrf52 +MCU_SUB_VARIANT = nrf52840 +SOFTDEV_VERSION = 6.1.1 +LD_FILES += boards/nrf52840-mdk-usb-dongle/nrf52840_open_bootloader.ld boards/nrf52840_1M_256k.ld + +NRF_DEFINES += -DNRF52840_XXAA diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/nrf52840_open_bootloader.ld b/ports/nrf/boards/nrf52840-mdk-usb-dongle/nrf52840_open_bootloader.ld new file mode 100644 index 0000000000000..d6c6e743a4daf --- /dev/null +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/nrf52840_open_bootloader.ld @@ -0,0 +1,2 @@ +_ram_start = 0x20000008; +_flash_start = 0x1000; diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/pins.csv b/ports/nrf/boards/nrf52840-mdk-usb-dongle/pins.csv new file mode 100644 index 0000000000000..57162863778eb --- /dev/null +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/pins.csv @@ -0,0 +1,17 @@ +P2,P2,ADC1_CH0 +P3,P3,ADC1_CH1 +P4,P4,ADC1_CH2 +P5,P5,ADC1_CH3 +P6,P6 +P7,P7 +P8,P8 +P9,P9 +P10,P10 +USER,P18,P18 +P19,P19 +P20,P20 +P21,P21 +LED_G,P22,P22 +LED_R,P23,P23 +LED_B,P24,P24 +P25,P25 From 342800c9a24e4a6b6453d3e0a7b96ddfb2ed33db Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 16 Jul 2020 12:10:45 +0200 Subject: [PATCH 165/352] travis: Change nrf pca10056 board to build with s140 SoftDevice. It might compile fine with S132 as SoftDevice for nRF52840. However, there might be different hardware on the SoC which in turn could make it fail. SoftDevice S140 is correct BLE stack for nRF52840 SoC and would provide a more accurate test build. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8eb5ef6bae514..48ce39f60ad89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -299,11 +299,11 @@ jobs: - sudo apt-get install libnewlib-arm-none-eabi - arm-none-eabi-gcc --version script: - - ports/nrf/drivers/bluetooth/download_ble_stack.sh s132_nrf52_6_1_1 + - ports/nrf/drivers/bluetooth/download_ble_stack.sh s140_nrf52_6_1_1 - make ${MAKEOPTS} -C ports/nrf submodules - make ${MAKEOPTS} -C ports/nrf BOARD=pca10040 - make ${MAKEOPTS} -C ports/nrf BOARD=microbit - - make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 SD=s132 + - make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 SD=s140 - make ${MAKEOPTS} -C ports/nrf BOARD=pca10090 # bare-arm and minimal ports, with size-diff check From 5d0be97bd9d8e9511852d2917fe0c6560232e85e Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 17 Jun 2020 15:10:55 +1000 Subject: [PATCH 166/352] unix: Make the MICROPY_xxx_ATOMIC_SECTION mutex recursive. This mutex is used to make the unix port behave more like bare metal, i.e. it allows "IRQ handlers" to run exclusively by making the mutex recursive. --- ports/unix/btstack_usb.c | 3 +++ ports/unix/mpthreadport.c | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/unix/btstack_usb.c b/ports/unix/btstack_usb.c index 76f32d6d27cfa..ab6a49f39ae73 100644 --- a/ports/unix/btstack_usb.c +++ b/ports/unix/btstack_usb.c @@ -156,7 +156,10 @@ STATIC void *btstack_thread(void *arg) { // Or, if a timeout results in it being set to TIMEOUT. while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { + // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); btstack_run_loop_embedded_execute_once(); + MICROPY_END_ATOMIC_SECTION(atomic_state); // The USB transport schedules events to the run loop at 1ms intervals, // and the implementation currently polls rather than selects. diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 711cf2a6bbffa..de0f5923bafe9 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -65,7 +65,7 @@ STATIC pthread_key_t tls_key; // The mutex is used for any code in this port that needs to be thread safe. // Specifically for thread management, access to the linked list is one example. // But also, e.g. scheduler state. -STATIC pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; +STATIC pthread_mutex_t thread_mutex; STATIC thread_t *thread; // this is used to synchronise the signal handler of the thread @@ -111,6 +111,13 @@ void mp_thread_init(void) { pthread_key_create(&tls_key, NULL); pthread_setspecific(tls_key, &mp_state_ctx.thread); + // Needs to be a recursive mutex to emulate the behavior of + // BEGIN_ATOMIC_SECTION on bare metal. + pthread_mutexattr_t thread_mutex_attr; + pthread_mutexattr_init(&thread_mutex_attr); + pthread_mutexattr_settype(&thread_mutex_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&thread_mutex, &thread_mutex_attr); + // create first entry in linked list of all threads thread = malloc(sizeof(thread_t)); thread->id = pthread_self(); From 07aec4681f6ea5173315e660c6ba39cd12a2aa58 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 17 Jun 2020 13:54:31 +1000 Subject: [PATCH 167/352] examples/bluetooth: In ble_advertising.py, skip appearance if not set. --- examples/bluetooth/ble_advertising.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/bluetooth/ble_advertising.py b/examples/bluetooth/ble_advertising.py index a496119967a5d..eed527f55d210 100644 --- a/examples/bluetooth/ble_advertising.py +++ b/examples/bluetooth/ble_advertising.py @@ -47,7 +47,8 @@ def _append(adv_type, value): _append(_ADV_TYPE_UUID128_COMPLETE, b) # See org.bluetooth.characteristic.gap.appearance.xml - _append(_ADV_TYPE_APPEARANCE, struct.pack(" Date: Tue, 16 Jun 2020 21:53:22 +1000 Subject: [PATCH 168/352] extmod/btstack: Schedule notify/indicate/write ops for bg completion. The goal of this commit is to allow using ble.gatts_notify() at any time, even if the stack is not ready to send the notification right now. It also addresses the same issue for ble.gatts_indicate() and ble.gattc_write() (without response). In addition this commit fixes the case where the buffer passed to write-with-response wasn't copied, meaning it could be modified by the caller, affecting the in-progress write. The changes are: - gatts_notify/indicate will now run in the background if the ACL buffer is currently full, meaning that notify/indicate can be called at any time. - gattc_write(mode=0) (no response) will now allow for one outstanding write. - gattc_write(mode=1) (with response) will now copy the buffer so that it can't be modified by the caller while the write is in progress. All four paths also now track the buffer while the operation is in progress, which prevents the GC free'ing the buffer while it's still needed. --- extmod/btstack/modbluetooth_btstack.c | 284 ++++++++++++++++-- extmod/btstack/modbluetooth_btstack.h | 3 + extmod/modbluetooth.c | 85 ++++-- extmod/modbluetooth.h | 2 +- extmod/nimble/modbluetooth_nimble.c | 6 +- tests/multi_bluetooth/ble_characteristic.py | 24 ++ .../multi_bluetooth/ble_characteristic.py.exp | 9 + 7 files changed, 353 insertions(+), 60 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 206bc98f9ff71..5b2aec67da247 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -53,8 +53,6 @@ STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3; volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; -STATIC btstack_packet_callback_registration_t hci_event_callback_registration; - STATIC int btstack_error_to_errno(int err) { DEBUG_EVENT_printf(" --> btstack error: %d\n", err); if (err == ERROR_CODE_SUCCESS) { @@ -85,6 +83,159 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu return result; } +// Notes on supporting background ops (e.g. an attempt to gatts_notify while +// an existing notification is in progress): + +// GATTS Notify/Indicate (att_server_notify/indicate) +// * When available, copies buffer immediately. +// * Otherwise fails with BTSTACK_ACL_BUFFERS_FULL +// * Use att_server_request_to_send_notification/indication to get callback +// * Takes btstack_context_callback_registration_t (and takes ownership) and conn_handle. +// * Callback is invoked with just the context member of the btstack_context_callback_registration_t + +// GATTC Write without response (gatt_client_write_value_of_characteristic_without_response) +// * When available, copies buffer immediately. +// * Otherwise, fails with GATT_CLIENT_BUSY. +// * Use gatt_client_request_can_write_without_response_event to get callback +// * Takes btstack_packet_handler_t (function pointer) and conn_handle +// * Callback is invoked, use gatt_event_can_write_without_response_get_handle to get the conn_handle (no other context) +// * There can only be one pending gatt_client_request_can_write_without_response_event (otherwise we fail with EALREADY). + +// GATTC Write with response (gatt_client_write_value_of_characteristic) +// * When peripheral is available, takes ownership of buffer. +// * Otherwise, fails with GATT_CLIENT_IN_WRONG_STATE (we fail the operation). +// * Raises GATT_EVENT_QUERY_COMPLETE to the supplied packet handler. + +// For notify/indicate/write-without-response that proceed immediately, nothing extra required. +// For all other cases, buffer needs to be copied and protected from GC. +// For notify/indicate: +// * btstack_context_callback_registration_t: +// * needs to be malloc'ed +// * needs to be protected from GC +// * context arg needs to point back to the callback registration so it can be freed and un-protected +// For write-without-response +// * only the conn_handle is available in the callback +// * so we need a queue of conn_handle->(value_handle, copied buffer) + +// Pending operation types. +enum { + // Queued for sending when possible. + MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, // Waiting for context callback + MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, // Waiting for context callback + MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle + // Hold buffer pointer until complete. + MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event +}; + +// Pending operation: +// - Holds a GC reference to the copied outgoing buffer. +// - Provides enough information for the callback handler to execute the desired operation. +struct _mp_btstack_pending_op_t { + btstack_linked_item_t *next; // Must be first field to match btstack_linked_item. + + // See enum above. + uint16_t op_type; + + // For all op types. + uint16_t conn_handle; + uint16_t value_handle; + + // For notify/indicate only. + // context_registration.context will point back to this struct. + btstack_context_callback_registration_t context_registration; + + // For notify/indicate/write-without-response, this is the actual buffer to send. + // For write-with-response, just holding onto the buffer for GC ref. + size_t len; + uint8_t buf[]; +}; + +// Must hold MICROPY_PY_BLUETOOTH_ENTER. +STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op, bool del) { + bool removed = btstack_linked_list_remove(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); + assert(removed); + (void)removed; + if (del) { + m_del_var(mp_btstack_pending_op_t, uint8_t, pending_op->len, pending_op); + } +} + +// Called in response to a gatts_notify/indicate being unable to complete, which then calls +// att_server_request_to_send_notification. +// We now have an opportunity to re-try the operation with an empty ACL buffer. +STATIC void btstack_notify_indicate_ready_handler(void *context) { + MICROPY_PY_BLUETOOTH_ENTER + mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)context; + DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%lu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len); + if (pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY) { + int err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->buf, pending_op->len); + DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); + assert(err == ERROR_CODE_SUCCESS); + (void)err; + } else { + assert(pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE); + int err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, NULL, 0); + DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); + assert(err == ERROR_CODE_SUCCESS); + (void)err; + } + // Can't free the pending op as we're in IRQ context. Leave it for the GC. + btstack_remove_pending_operation(pending_op, false /* del */); + MICROPY_PY_BLUETOOTH_EXIT +} + +// Register a pending background operation -- copies the buffer, and makes it known to the GC. +STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { + DEBUG_EVENT_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%lu\n", op_type, conn_handle, value_handle, len); + mp_btstack_pending_op_t *pending_op = m_new_obj_var(mp_btstack_pending_op_t, uint8_t, len); + pending_op->op_type = op_type; + pending_op->conn_handle = conn_handle; + pending_op->value_handle = value_handle; + pending_op->len = len; + memcpy(pending_op->buf, buf, len); + + if (op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY || op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE) { + pending_op->context_registration.callback = &btstack_notify_indicate_ready_handler; + pending_op->context_registration.context = pending_op; + } + + MICROPY_PY_BLUETOOTH_ENTER + bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); + assert(added); + (void)added; + MICROPY_PY_BLUETOOTH_EXIT + + return pending_op; +} + +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + +// Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle). +// Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE. +// At the moment, both will set value_handle=0xffff as the events do not know their value_handle. +// TODO: Can we make btstack give us the value_handle for regular write (with response) so that we +// know for sure that we're using the correct entry. +STATIC mp_btstack_pending_op_t *btstack_finish_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, bool del) { + MICROPY_PY_BLUETOOTH_ENTER + DEBUG_EVENT_printf("btstack_finish_pending_operation op_type=%d conn_handle=%d value_handle=%d\n", op_type, conn_handle, value_handle); + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops); + while (btstack_linked_list_iterator_has_next(&it)) { + mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)btstack_linked_list_iterator_next(&it); + + if (pending_op->op_type == op_type && pending_op->conn_handle == conn_handle && (value_handle == 0xffff || pending_op->value_handle == value_handle)) { + DEBUG_EVENT_printf("btstack_finish_pending_operation: found value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); + btstack_remove_pending_operation(pending_op, del); + MICROPY_PY_BLUETOOTH_EXIT + return del ? NULL : pending_op; + } + } + DEBUG_EVENT_printf("btstack_finish_pending_operation: not found\n"); + MICROPY_PY_BLUETOOTH_EXIT + return NULL; +} +#endif + STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { @@ -161,12 +312,17 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { - DEBUG_EVENT_printf(" --> gatt query complete\n"); uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_EVENT_printf(" --> gatt query complete irq=%d conn_handle=%d status=%d\n", irq, conn_handle, status); if (irq == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { - // TODO there is no value_handle available to pass here - mp_bluetooth_gattc_on_read_write_status(irq, conn_handle, 0, status); + // TODO there is no value_handle available to pass here. + // TODO try and get this implemented in btstack. + mp_bluetooth_gattc_on_read_write_status(irq, conn_handle, 0xffff, status); + // Unref the saved buffer for the write operation on this conn_handle. + if (irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { + btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, 0xffff, false /* del */); + } } else if (irq == MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { @@ -223,6 +379,16 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_INDICATE, conn_handle, value_handle, len, &atomic_state); mp_bluetooth_gattc_on_data_available_chunk(data, len); mp_bluetooth_gattc_on_data_available_end(atomic_state); + } else if (event_type == GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE) { + uint16_t conn_handle = gatt_event_can_write_without_response_get_handle(packet); + DEBUG_EVENT_printf(" --> gatt can write without response %d\n", conn_handle); + mp_btstack_pending_op_t *pending_op = btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, 0xffff, false /* !del */); + if (pending_op) { + DEBUG_EVENT_printf(" --> ready for value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); + gatt_client_write_value_of_characteristic_without_response(pending_op->conn_handle, pending_op->value_handle, pending_op->len, (uint8_t *)pending_op->buf); + // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. + } + #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else { DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); @@ -238,6 +404,10 @@ STATIC void btstack_packet_handler_generic(uint8_t packet_type, uint16_t channel btstack_packet_handler(packet_type, packet, 0); } +STATIC btstack_packet_callback_registration_t hci_event_callback_registration = { + .callback = &btstack_packet_handler_generic +}; + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // For when the handler is being used for service discovery. STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { @@ -326,7 +496,6 @@ int mp_bluetooth_init(void) { #endif // Register for HCI events. - hci_event_callback_registration.callback = &btstack_packet_handler_generic; hci_add_event_handler(&hci_event_callback_registration); // Set a timeout for HCI initialisation. @@ -410,8 +579,7 @@ size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { } int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) { - mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, buf, len); - return 0; + return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, buf, len); } int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { @@ -556,7 +724,10 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m if (props & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)) { // btstack creates the CCCB as the next handle. mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, MP_BLUETOOTH_CCCB_LEN); - mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf)); + int ret = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf)); + if (ret) { + return ret; + } } DEBUG_EVENT_printf("Registered char with handle %u\n", handles[handle_index]); ++handle_index; @@ -607,19 +778,63 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { uint8_t *data = NULL; size_t len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, &len); + return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, len); } -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len) { +int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send\n"); - // TODO: We need to use att_server_request_to_send_notification here as the stack may not be ready to send a notification. - int err = att_server_notify(conn_handle, value_handle, value, *value_len); - return btstack_error_to_errno(err); + + // Attempt to send immediately. If it succeeds, btstack will copy the buffer. + MICROPY_PY_BLUETOOTH_ENTER + int err = att_server_notify(conn_handle, value_handle, value, value_len); + MICROPY_PY_BLUETOOTH_EXIT + + if (err == BTSTACK_ACL_BUFFERS_FULL) { + DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); + // Schedule callback, making a copy of the buffer. + mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, conn_handle, value_handle, value, value_len); + + err = att_server_request_to_send_notification(&pending_op->context_registration, conn_handle); + + if (err != ERROR_CODE_SUCCESS) { + // Failure. Unref and free the pending operation. + btstack_remove_pending_operation(pending_op, true /* del */); + } + + return 0; + } else { + return btstack_error_to_errno(err); + } } int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate\n"); - return btstack_error_to_errno(att_server_indicate(conn_handle, value_handle, NULL, 0)); + + uint8_t *data = NULL; + size_t len = 0; + mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); + + // Attempt to send immediately, will copy buffer. + MICROPY_PY_BLUETOOTH_ENTER + int err = att_server_indicate(conn_handle, value_handle, data, len); + MICROPY_PY_BLUETOOTH_EXIT + + if (err == BTSTACK_ACL_BUFFERS_FULL) { + DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n"); + // Schedule callback, making a copy of the buffer. + mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, conn_handle, value_handle, data, len); + + err = att_server_request_to_send_indication(&pending_op->context_registration, conn_handle); + + if (err != ERROR_CODE_SUCCESS) { + // Failure. Unref and free the pending operation. + btstack_remove_pending_operation(pending_op, true /* del */); + } + + return 0; + } else { + return btstack_error_to_errno(err); + } } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { @@ -751,19 +966,42 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { DEBUG_EVENT_printf("mp_bluetooth_gattc_write\n"); - // TODO the below gatt_client functions do not copy the data and require it to be valid - // until the write is done, so there should be some kind of buffering done here. + // We should be distinguishing between gatt_client_write_value_of_characteristic vs + // gatt_client_write_characteristic_descriptor_using_descriptor_handle. + // However both are implemented using send_gatt_write_attribute_value_request under the hood, + // and we get the exact same event to the packet handler. + // Same story for the "without response" version. + + int err; + mp_btstack_pending_op_t *pending_op = NULL; if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { - // TODO need to call gatt_client_request_can_write_without_response_event then do - // the actual write on the callback from that. - return btstack_error_to_errno(gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value)); + // If possible, this will send immediately, copying the buffer directly to the ACL buffer. + err = gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value); + if (err == GATT_CLIENT_BUSY) { + DEBUG_EVENT_printf("mp_bluetooth_gattc_write: client busy\n"); + // Can't send right now, need to take a copy of the buffer and add it to the queue. + pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, value_handle, value, *value_len); + // Notify when this conn_handle can write. + err = gatt_client_request_can_write_without_response_event(&btstack_packet_handler_generic, conn_handle); + } else { + DEBUG_EVENT_printf("mp_bluetooth_gattc_write: other failure: %d\n", err); + } + } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { + // Pending operation copies the value buffer and keeps a GC reference + // until the response comes back (there is always a response). + pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, value_handle, value, *value_len); + err = gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, pending_op->len, pending_op->buf); + } else { + return MP_EINVAL; } - if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { - return btstack_error_to_errno(gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, *value_len, (uint8_t *)value)); + + if (pending_op && err != ERROR_CODE_SUCCESS) { + // Failure. Unref and free the pending operation. + btstack_remove_pending_operation(pending_op, true /* del */); } - return MP_EINVAL; + return btstack_error_to_errno(err); } #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 3bdb5f271e5dd..c943b6d72d548 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -33,6 +33,8 @@ #include "lib/btstack/src/btstack.h" +typedef struct _mp_btstack_pending_op_t mp_btstack_pending_op_t; + typedef struct _mp_bluetooth_btstack_root_pointers_t { // This stores both the advertising data and the scan response data, concatenated together. uint8_t *adv_data; @@ -45,6 +47,7 @@ typedef struct _mp_bluetooth_btstack_root_pointers_t { #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Registration for notify/indicate events. gatt_client_notification_t notification; + btstack_linked_list_t pending_ops; #endif } mp_bluetooth_btstack_root_pointers_t; diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index da40a1548541d..d94e5aec91ef2 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -321,8 +321,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map case MP_QSTR_gap_name: { mp_buffer_info_t bufinfo; mp_get_buffer_raise(e->value, &bufinfo, MP_BUFFER_READ); - int ret = mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len); - bluetooth_handle_errno(ret); + bluetooth_handle_errno(mp_bluetooth_gap_set_device_name(bufinfo.buf, bufinfo.len)); break; } case MP_QSTR_rxbuf: { @@ -663,10 +662,9 @@ STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) if (n_args == 4) { mp_buffer_info_t bufinfo = {0}; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - size_t len = bufinfo.len; - int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, &len); + int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, bufinfo.len); bluetooth_handle_errno(err); - return MP_OBJ_NEW_SMALL_INT(len); + return mp_const_none; } else { int err = mp_bluetooth_gatts_notify(conn_handle, value_handle); return bluetooth_handle_errno(err); @@ -674,6 +672,15 @@ STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify); +STATIC mp_obj_t bluetooth_ble_gatts_indicate(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t value_handle_in) { + mp_int_t conn_handle = mp_obj_get_int(conn_handle_in); + mp_int_t value_handle = mp_obj_get_int(value_handle_in); + + int err = mp_bluetooth_gatts_indicate(conn_handle, value_handle); + return bluetooth_handle_errno(err); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_gatts_indicate_obj, bluetooth_ble_gatts_indicate); + STATIC mp_obj_t bluetooth_ble_gatts_set_buffer(size_t n_args, const mp_obj_t *args) { mp_int_t value_handle = mp_obj_get_int(args[1]); mp_int_t len = mp_obj_get_int(args[2]); @@ -765,6 +772,7 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) }, + { MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) }, #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // GATT Client (i.e. central/scanner role) @@ -1147,47 +1155,58 @@ mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, ui } int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len) { + MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); - if (!entry) { - return MP_EINVAL; - } - - *value = entry->data; - *value_len = entry->data_len; - if (entry->append) { - entry->data_len = 0; + if (entry) { + *value = entry->data; + *value_len = entry->data_len; + if (entry->append) { + entry->data_len = 0; + } } - - return 0; + MICROPY_PY_BLUETOOTH_EXIT + return entry ? 0 : MP_EINVAL; } int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len) { + MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); - if (!entry) { - return MP_EINVAL; - } + if (entry) { + if (value_len > entry->data_alloc) { + uint8_t *data = m_new_maybe(uint8_t, value_len); + if (data) { + entry->data = data; + entry->data_alloc = value_len; + } else { + MICROPY_PY_BLUETOOTH_EXIT + return MP_ENOMEM; + } + } - if (value_len > entry->data_alloc) { - entry->data = m_new(uint8_t, value_len); - entry->data_alloc = value_len; + memcpy(entry->data, value, value_len); + entry->data_len = value_len; } - - memcpy(entry->data, value, value_len); - entry->data_len = value_len; - - return 0; + MICROPY_PY_BLUETOOTH_EXIT + return entry ? 0 : MP_EINVAL; } int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append) { + MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); - if (!entry) { - return MP_EINVAL; + if (entry) { + uint8_t *data = m_renew_maybe(uint8_t, entry->data, entry->data_alloc, len, true); + if (data) { + entry->data = data; + entry->data_alloc = len; + entry->data_len = 0; + entry->append = append; + } else { + MICROPY_PY_BLUETOOTH_EXIT + return MP_ENOMEM; + } } - entry->data = m_renew(uint8_t, entry->data, entry->data_alloc, len); - entry->data_alloc = len; - entry->data_len = 0; - entry->append = append; - return 0; + MICROPY_PY_BLUETOOTH_EXIT + return entry ? 0 : MP_EINVAL; } #endif // MICROPY_PY_BLUETOOTH diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index c8b8dc63f2a67..a232ee2d1d095 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -199,7 +199,7 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t // Notify the central that it should do a read. int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle); // Notify the central, including a data payload. (Note: does not set the gatts db value). -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len); +int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len); // Indicate the central. int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index be7d13d9e6f5d..2e94685131d87 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -587,13 +587,13 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { return ble_hs_err_to_errno(ble_gattc_notify(conn_handle, value_handle)); } -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len) { +int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - struct os_mbuf *om = ble_hs_mbuf_from_flat(value, *value_len); + struct os_mbuf *om = ble_hs_mbuf_from_flat(value, value_len); if (om == NULL) { - return -1; + return MP_ENOMEM; } // TODO: check that notify_custom takes ownership of om, if not os_mbuf_free_chain(om). return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om)); diff --git a/tests/multi_bluetooth/ble_characteristic.py b/tests/multi_bluetooth/ble_characteristic.py index 33d92b823bbb2..fd6fd467248de 100644 --- a/tests/multi_bluetooth/ble_characteristic.py +++ b/tests/multi_bluetooth/ble_characteristic.py @@ -16,6 +16,7 @@ _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) +_IRQ_GATTC_INDICATE = const(19) SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444") @@ -59,6 +60,8 @@ def irq(event, data): print("_IRQ_GATTC_WRITE_DONE", data[-1]) elif event == _IRQ_GATTC_NOTIFY: print("_IRQ_GATTC_NOTIFY", data[-1]) + elif event == _IRQ_GATTC_INDICATE: + print("_IRQ_GATTC_INDICATE", data[-1]) if waiting_event is not None: if (isinstance(waiting_event, int) and event == waiting_event) or ( @@ -115,6 +118,16 @@ def instance0(): print("gatts_notify") ble.gatts_notify(conn_handle, char_handle, "periph2") + # Wait for a write to the characteristic from the central. + wait_for_event(_IRQ_GATTS_WRITE, TIMEOUT_MS) + + # Wait a bit, then notify a new value on the characteristic. + time.sleep_ms(1000) + print("gatts_write") + ble.gatts_write(char_handle, "periph3") + print("gatts_indicate") + ble.gatts_indicate(conn_handle, char_handle) + # Wait for the central to disconnect. wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) finally: @@ -163,6 +176,17 @@ def instance1(): ble.gattc_read(conn_handle, value_handle) wait_for_event(_IRQ_GATTC_READ_RESULT, TIMEOUT_MS) + # Write to the characteristic, and ask for a response. + print("gattc_write") + ble.gattc_write(conn_handle, value_handle, "central2", 1) + wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS) + + # Wait for a indicate (should have new data), then read new value. + wait_for_event(_IRQ_GATTC_INDICATE, TIMEOUT_MS) + print("gattc_read") + ble.gattc_read(conn_handle, value_handle) + wait_for_event(_IRQ_GATTC_READ_RESULT, TIMEOUT_MS) + # Disconnect from peripheral. print("gap_disconnect:", ble.gap_disconnect(conn_handle)) wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) diff --git a/tests/multi_bluetooth/ble_characteristic.py.exp b/tests/multi_bluetooth/ble_characteristic.py.exp index d89842ef7b097..7f66a4d907455 100644 --- a/tests/multi_bluetooth/ble_characteristic.py.exp +++ b/tests/multi_bluetooth/ble_characteristic.py.exp @@ -6,6 +6,9 @@ gatts_write gatts_notify _IRQ_GATTS_WRITE b'central1' gatts_notify +_IRQ_GATTS_WRITE b'central2' +gatts_write +gatts_indicate _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect @@ -26,5 +29,11 @@ _IRQ_GATTC_NOTIFY b'periph2' gattc_read _IRQ_GATTC_READ_RESULT b'central1' _IRQ_GATTC_READ_DONE 0 +gattc_write +_IRQ_GATTC_WRITE_DONE 0 +_IRQ_GATTC_INDICATE b'periph3' +gattc_read +_IRQ_GATTC_READ_RESULT b'periph3' +_IRQ_GATTC_READ_DONE 0 gap_disconnect: True _IRQ_PERIPHERAL_DISCONNECT From 89a95b7c85cd90967020b80a0a4649b2f745bea8 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 17 Jun 2020 14:57:52 +1000 Subject: [PATCH 169/352] examples/bluetooth: Add simple UART demo with central and peripheral. --- examples/bluetooth/ble_simple_central.py | 245 ++++++++++++++++++++ examples/bluetooth/ble_simple_peripheral.py | 98 ++++++++ 2 files changed, 343 insertions(+) create mode 100644 examples/bluetooth/ble_simple_central.py create mode 100644 examples/bluetooth/ble_simple_peripheral.py diff --git a/examples/bluetooth/ble_simple_central.py b/examples/bluetooth/ble_simple_central.py new file mode 100644 index 0000000000000..f6996366e3da1 --- /dev/null +++ b/examples/bluetooth/ble_simple_central.py @@ -0,0 +1,245 @@ +# This example finds and connects to a peripheral running the +# UART service (e.g. ble_simple_peripheral.py). + +import bluetooth +import random +import struct +import time +import micropython + +from ble_advertising import decode_services, decode_name + +from micropython import const + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_GATTS_READ_REQUEST = const(4) +_IRQ_SCAN_RESULT = const(5) +_IRQ_SCAN_DONE = const(6) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_SERVICE_RESULT = const(9) +_IRQ_GATTC_SERVICE_DONE = const(10) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_DESCRIPTOR_RESULT = const(13) +_IRQ_GATTC_DESCRIPTOR_DONE = const(14) +_IRQ_GATTC_READ_RESULT = const(15) +_IRQ_GATTC_READ_DONE = const(16) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) +_IRQ_GATTC_INDICATE = const(19) + +_ADV_IND = const(0x00) +_ADV_DIRECT_IND = const(0x01) +_ADV_SCAN_IND = const(0x02) +_ADV_NONCONN_IND = const(0x03) + +_UART_SERVICE_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") +_UART_RX_CHAR_UUID = bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") +_UART_TX_CHAR_UUID = bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") + + +class BLESimpleCentral: + def __init__(self, ble): + self._ble = ble + self._ble.active(True) + self._ble.irq(handler=self._irq) + + self._reset() + + def _reset(self): + # Cached name and address from a successful scan. + self._name = None + self._addr_type = None + self._addr = None + + # Callbacks for completion of various operations. + # These reset back to None after being invoked. + self._scan_callback = None + self._conn_callback = None + self._read_callback = None + + # Persistent callback for when new data is notified from the device. + self._notify_callback = None + + # Connected device. + self._conn_handle = None + self._start_handle = None + self._end_handle = None + self._tx_handle = None + self._rx_handle = None + + def _irq(self, event, data): + if event == _IRQ_SCAN_RESULT: + addr_type, addr, adv_type, rssi, adv_data = data + if adv_type in (_ADV_IND, _ADV_DIRECT_IND,) and _UART_SERVICE_UUID in decode_services( + adv_data + ): + # Found a potential device, remember it and stop scanning. + self._addr_type = addr_type + self._addr = bytes( + addr + ) # Note: addr buffer is owned by caller so need to copy it. + self._name = decode_name(adv_data) or "?" + self._ble.gap_scan(None) + + elif event == _IRQ_SCAN_DONE: + if self._scan_callback: + if self._addr: + # Found a device during the scan (and the scan was explicitly stopped). + self._scan_callback(self._addr_type, self._addr, self._name) + self._scan_callback = None + else: + # Scan timed out. + self._scan_callback(None, None, None) + + elif event == _IRQ_PERIPHERAL_CONNECT: + # Connect successful. + conn_handle, addr_type, addr, = data + if addr_type == self._addr_type and addr == self._addr: + self._conn_handle = conn_handle + self._ble.gattc_discover_services(self._conn_handle) + + elif event == _IRQ_PERIPHERAL_DISCONNECT: + # Disconnect (either initiated by us or the remote end). + conn_handle, _, _, = data + if conn_handle == self._conn_handle: + # If it was initiated by us, it'll already be reset. + self._reset() + + elif event == _IRQ_GATTC_SERVICE_RESULT: + # Connected device returned a service. + conn_handle, start_handle, end_handle, uuid = data + print("service", data) + if conn_handle == self._conn_handle and uuid == _UART_SERVICE_UUID: + self._start_handle, self._end_handle = start_handle, end_handle + + elif event == _IRQ_GATTC_SERVICE_DONE: + # Service query complete. + if self._start_handle and self._end_handle: + self._ble.gattc_discover_characteristics( + self._conn_handle, self._start_handle, self._end_handle + ) + else: + print("Failed to find uart service.") + + elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: + # Connected device returned a characteristic. + conn_handle, def_handle, value_handle, properties, uuid = data + if conn_handle == self._conn_handle and uuid == _UART_RX_CHAR_UUID: + self._rx_handle = value_handle + if conn_handle == self._conn_handle and uuid == _UART_TX_CHAR_UUID: + self._tx_handle = value_handle + + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: + # Characteristic query complete. + if self._tx_handle is not None and self._rx_handle is not None: + # We've finished connecting and discovering device, fire the connect callback. + if self._conn_callback: + self._conn_callback() + else: + print("Failed to find uart rx characteristic.") + + elif event == _IRQ_GATTC_WRITE_DONE: + conn_handle, value_handle, status = data + print("TX complete") + + elif event == _IRQ_GATTC_NOTIFY: + conn_handle, value_handle, notify_data = data + if conn_handle == self._conn_handle and value_handle == self._tx_handle: + if self._notify_callback: + self._notify_callback(notify_data) + + # Returns true if we've successfully connected and discovered characteristics. + def is_connected(self): + return ( + self._conn_handle is not None + and self._tx_handle is not None + and self._rx_handle is not None + ) + + # Find a device advertising the environmental sensor service. + def scan(self, callback=None): + self._addr_type = None + self._addr = None + self._scan_callback = callback + self._ble.gap_scan(2000, 30000, 30000) + + # Connect to the specified device (otherwise use cached address from a scan). + def connect(self, addr_type=None, addr=None, callback=None): + self._addr_type = addr_type or self._addr_type + self._addr = addr or self._addr + self._conn_callback = callback + if self._addr_type is None or self._addr is None: + return False + self._ble.gap_connect(self._addr_type, self._addr) + return True + + # Disconnect from current device. + def disconnect(self): + if not self._conn_handle: + return + self._ble.gap_disconnect(self._conn_handle) + self._reset() + + # Send data over the UART + def write(self, v, response=False): + if not self.is_connected(): + return + self._ble.gattc_write(self._conn_handle, self._rx_handle, v, 1 if response else 0) + + # Set handler for when data is received over the UART. + def on_notify(self, callback): + self._notify_callback = callback + + +def demo(): + ble = bluetooth.BLE() + central = BLESimpleCentral(ble) + + not_found = False + + def on_scan(addr_type, addr, name): + if addr_type is not None: + print("Found peripheral:", addr_type, addr, name) + central.connect() + else: + nonlocal not_found + not_found = True + print("No peripheral found.") + + central.scan(callback=on_scan) + + # Wait for connection... + while not central.is_connected(): + time.sleep_ms(100) + if not_found: + return + + print("Connected") + + def on_rx(v): + print("RX", v) + + central.on_notify(on_rx) + + with_response = False + + i = 0 + while central.is_connected(): + try: + v = str(i) + "_" + print("TX", v) + central.write(v, with_response) + except: + print("TX failed") + i += 1 + time.sleep_ms(400 if with_response else 30) + + print("Disconnected") + + +if __name__ == "__main__": + demo() diff --git a/examples/bluetooth/ble_simple_peripheral.py b/examples/bluetooth/ble_simple_peripheral.py new file mode 100644 index 0000000000000..08cd7fa98d69c --- /dev/null +++ b/examples/bluetooth/ble_simple_peripheral.py @@ -0,0 +1,98 @@ +# This example demonstrates a UART periperhal. + +import bluetooth +import random +import struct +import time +from ble_advertising import advertising_payload + +from micropython import const + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) + +_UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") +_UART_TX = ( + bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"), + bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY, +) +_UART_RX = ( + bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"), + bluetooth.FLAG_WRITE | bluetooth.FLAG_WRITE_NO_RESPONSE, +) +_UART_SERVICE = ( + _UART_UUID, + (_UART_TX, _UART_RX,), +) + + +class BLESimplePeripheral: + def __init__(self, ble, name="mpy-uart"): + self._ble = ble + self._ble.active(True) + self._ble.irq(handler=self._irq) + ((self._handle_tx, self._handle_rx,),) = self._ble.gatts_register_services( + (_UART_SERVICE,) + ) + self._connections = set() + self._write_callback = None + self._payload = advertising_payload(name=name, services=[_UART_UUID],) + self._advertise() + + def _irq(self, event, data): + # Track connections so we can send notifications. + if event == _IRQ_CENTRAL_CONNECT: + conn_handle, _, _, = data + print("New connection", conn_handle) + self._connections.add(conn_handle) + elif event == _IRQ_CENTRAL_DISCONNECT: + conn_handle, _, _, = data + print("Disconnected", conn_handle) + self._connections.remove(conn_handle) + # Start advertising again to allow a new connection. + self._advertise() + elif event == _IRQ_GATTS_WRITE: + conn_handle, value_handle = data + value = self._ble.gatts_read(value_handle) + if value_handle == self._handle_rx and self._write_callback: + self._write_callback(value) + + def send(self, data): + for conn_handle in self._connections: + self._ble.gatts_notify(conn_handle, self._handle_tx, data) + + def is_connected(self): + return len(self._connections) > 0 + + def _advertise(self, interval_us=500000): + print("Starting advertising") + self._ble.gap_advertise(interval_us, adv_data=self._payload) + + def on_write(self, callback): + self._write_callback = callback + + +def demo(): + ble = bluetooth.BLE() + p = BLESimplePeripheral(ble) + + def on_rx(v): + print("RX", v) + + p.on_write(on_rx) + + i = 0 + while True: + if p.is_connected(): + # Short burst of queued notifications. + for _ in range(3): + data = str(i) + "_" + print("TX", data) + p.send(data) + i += 1 + time.sleep_ms(100) + + +if __name__ == "__main__": + demo() From b7698841b20cd20a6729ba9d9f19a93f4fd33a5c Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 17 Jul 2020 15:18:32 +1000 Subject: [PATCH 170/352] docs/library: Add gatts_indicate() doc to ubluetooth.rst. Also clarify behavior of `gatts_notify` and add some TODOs about adding an event for indication acknowledgement. --- docs/library/ubluetooth.rst | 43 +++++++++++++++++---------- extmod/btstack/modbluetooth_btstack.c | 2 ++ extmod/nimble/modbluetooth_nimble.c | 2 ++ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index f96a7caaf828e..ed2ffefe750ad 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -48,7 +48,8 @@ Configuration - ``'mac'``: Returns the device MAC address. If a device has a fixed address (e.g. PYBD) then it will be returned. Otherwise (e.g. ESP32) a random address will be generated when the BLE interface is made active. - Note: on some ports, accessing this value requires that the interface is + + **Note:** on some ports, accessing this value requires that the interface is active (so that the MAC address can be queried from the controller). - ``'gap_name'``: Get/set the GAP device name used by service 0x1800, @@ -71,12 +72,12 @@ Event Handling arguments, ``event`` (which will be one of the codes below) and ``data`` (which is an event-specific tuple of values). - Note: the ``addr``, ``adv_data``, ``char_data``, ``notify_data``, and - ``uuid`` entries in the tuples are - references to data managed by the :mod:`ubluetooth` module (i.e. the same - instance will be re-used across multiple calls to the event handler). If - your program wants to use this data outside of the handler, then it must - copy them first, e.g. by using ``bytes(addr)`` or ``bluetooth.UUID(uuid)``. + **Note:** the ``addr``, ``adv_data``, ``char_data``, ``notify_data``, and + ``uuid`` entries in the tuples are references to data managed by the + :mod:`ubluetooth` module (i.e. the same instance will be re-used across + multiple calls to the event handler). If your program wants to use this + data outside of the handler, then it must copy them first, e.g. by using + ``bytes(addr)`` or ``bluetooth.UUID(uuid)``. An event handler showing all possible events:: @@ -189,7 +190,7 @@ Broadcaster Role (Advertiser) protocol (e.g. ``bytes``, ``bytearray``, ``str``). *adv_data* is included in all broadcasts, and *resp_data* is send in reply to an active scan. - Note: if *adv_data* (or *resp_data*) is ``None``, then the data passed + **Note:** if *adv_data* (or *resp_data*) is ``None``, then the data passed to the previous call to ``gap_advertise`` will be re-used. This allows a broadcaster to resume advertising with just ``gap_advertise(interval_us)``. To clear the advertising payload pass an empty ``bytes``, i.e. ``b''``. @@ -280,8 +281,8 @@ writes from a central to a given characteristic, use ( (hr,), (tx, rx,), ) = bt.gatts_register_services(SERVICES) The three value handles (``hr``, ``tx``, ``rx``) can be used with - :meth:`gatts_read `, :meth:`gatts_write `, - and :meth:`gatts_notify `. + :meth:`gatts_read `, :meth:`gatts_write `, :meth:`gatts_notify `, and + :meth:`gatts_indicate `. **Note:** Advertising must be stopped before registering services. @@ -296,12 +297,24 @@ writes from a central to a given characteristic, use .. method:: BLE.gatts_notify(conn_handle, value_handle, [data]) - Notifies a connected central that this value has changed and that it should - issue a read of the current value from this peripheral. + Sends a notification request to a connected central. + + If *data* is specified, then that value is sent to the central as part of + the notification. The local value will not be modified. + + Otherwise, if *data* is not specified, then the current local value (as + set with :meth:`gatts_write `) will be sent. + +.. method:: BLE.gatts_indicate(conn_handle, value_handle) + + Sends an indication request to a connected central. + + **Note:** This does not currently support sending a custom value, it will + always send the current local value (as set with :meth:`gatts_write + `). - If *data* is specified, then the that value is sent to the central as part - of the notification, avoiding the need for a separate read request. Note - that this will not update the local value stored. + **Note:** This does not currently support generating an event for sucessful + acknowledgment of the indication. .. method:: BLE.gatts_set_buffer(value_handle, len, append=False, /) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 5b2aec67da247..b114275b6e87c 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -814,6 +814,8 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { size_t len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); + // TODO: Handle ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE to generate acknowledgment event. + // Attempt to send immediately, will copy buffer. MICROPY_PY_BLUETOOTH_ENTER int err = att_server_indicate(conn_handle, value_handle, data, len); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 2e94685131d87..b859213aabd3d 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -603,6 +603,8 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } + // TODO: catch BLE_GAP_EVENT_NOTIFY_TX to raise an event for completed + // indication transaction. return ble_hs_err_to_errno(ble_gattc_indicate(conn_handle, value_handle)); } From 43ceadac5503ac6854dd0d400bd13b93f36dd6a6 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 20 Jul 2020 11:29:19 +1000 Subject: [PATCH 171/352] extmod/modbluetooth: Ignore unused self_in in ble_gatts_indicate. --- extmod/modbluetooth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index d94e5aec91ef2..ced67365a68ad 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -673,6 +673,7 @@ STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify); STATIC mp_obj_t bluetooth_ble_gatts_indicate(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t value_handle_in) { + (void)self_in; mp_int_t conn_handle = mp_obj_get_int(conn_handle_in); mp_int_t value_handle = mp_obj_get_int(value_handle_in); From 3c7ca2004c78ec386e136b947ed5e05a39b61aaf Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 20 Jul 2020 11:46:55 +1000 Subject: [PATCH 172/352] extmod/modbluetooth: Fix so it builds in peripheral-only mode. --- extmod/btstack/modbluetooth_btstack.c | 22 ++++++++++++---------- extmod/btstack/modbluetooth_btstack.h | 3 ++- extmod/modbluetooth.c | 2 ++ extmod/nimble/modbluetooth_nimble.c | 14 +++++++------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index b114275b6e87c..d25b01d815591 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -70,6 +70,7 @@ STATIC int btstack_error_to_errno(int err) { } } +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uuid128) { mp_obj_bluetooth_uuid_t result; if (uuid16 != 0) { @@ -82,6 +83,7 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu } return result; } +#endif // Notes on supporting background ops (e.g. an attempt to gatts_notify while // an existing notification is in progress): @@ -286,16 +288,6 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_EVENT_printf(" --> btstack # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { DEBUG_EVENT_printf(" --> hci vendor specific\n"); - } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { - DEBUG_EVENT_printf(" --> gap advertising report\n"); - bd_addr_t address; - gap_event_advertising_report_get_address(packet, address); - uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); - uint8_t address_type = gap_event_advertising_report_get_address_type(packet); - int8_t rssi = gap_event_advertising_report_get_rssi(packet); - uint8_t length = gap_event_advertising_report_get_data_length(packet); - const uint8_t *data = gap_event_advertising_report_get_data(packet); - mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { DEBUG_EVENT_printf(" --> hci disconnect complete\n"); uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); @@ -311,6 +303,16 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t uint8_t addr[6] = {0}; mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { + DEBUG_EVENT_printf(" --> gap advertising report\n"); + bd_addr_t address; + gap_event_advertising_report_get_address(packet, address); + uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); + uint8_t address_type = gap_event_advertising_report_get_address_type(packet); + int8_t rssi = gap_event_advertising_report_get_rssi(packet); + uint8_t length = gap_event_advertising_report_get_data_length(packet); + const uint8_t *data = gap_event_advertising_report_get_data(packet); + mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length); } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); uint16_t status = gatt_event_query_complete_get_att_status(packet); diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index c943b6d72d548..2fad86f22605e 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -44,10 +44,11 @@ typedef struct _mp_bluetooth_btstack_root_pointers_t { // Characteristic (and descriptor) value storage. mp_gatts_db_t gatts_db; + btstack_linked_list_t pending_ops; + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Registration for notify/indicate events. gatt_client_notification_t notification; - btstack_linked_list_t pending_ops; #endif } mp_bluetooth_btstack_root_pointers_t; diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index ced67365a68ad..fb5c6ac8c99ab 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -836,10 +836,12 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size // Note the int8_t got packed into the ringbuf as a uint8_t. data_tuple->items[j++] = MP_OBJ_NEW_SMALL_INT((int8_t)ringbuf_get(ringbuf)); } + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE if (uuid) { ringbuf_get_uuid(ringbuf, uuid); data_tuple->items[j++] = MP_OBJ_FROM_PTR(uuid); } + #endif // The code that enqueues into the ringbuf should ensure that it doesn't // put more than bt->irq_data_data_alloc bytes into the ringbuf, because // that's what's available here in bt->irq_data_bytes. diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index b859213aabd3d..d60e23ecb4b9b 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -96,6 +96,13 @@ STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid, ble_u } } +// modbluetooth (and the layers above it) work in BE for addresses, Nimble works in LE. +STATIC void reverse_addr_byte_order(uint8_t *addr_out, const uint8_t *addr_in) { + for (int i = 0; i < 6; ++i) { + addr_out[i] = addr_in[5 - i]; + } +} + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid) { @@ -123,13 +130,6 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid) { return result; } -// modbluetooth (and the layers above it) work in BE for addresses, Nimble works in LE. -STATIC void reverse_addr_byte_order(uint8_t *addr_out, const uint8_t *addr_in) { - for (int i = 0; i < 6; ++i) { - addr_out[i] = addr_in[5 - i]; - } -} - STATIC ble_addr_t create_nimble_addr(uint8_t addr_type, const uint8_t *addr) { ble_addr_t addr_nimble; addr_nimble.type = addr_type; From 9d823a5d9a6730edde8e1df1e5ff4add1ad17094 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 20 Jul 2020 16:58:10 +1000 Subject: [PATCH 173/352] extmod/modbluetooth: Add event for "indicate acknowledgement". This commit adds the IRQ_GATTS_INDICATE_DONE BLE event which will be raised with the status of gatts_indicate (unlike notify, indications require acknowledgement). An example of its use is added to ble_temperature.py, and to the multitests in ble_characteristic.py. Implemented for btstack and nimble bindings, tested in both directions between unix/btstack and pybd/nimble. --- examples/bluetooth/ble_temperature.py | 19 +++++--- extmod/btstack/modbluetooth_btstack.c | 44 ++++++++++++++++--- extmod/modbluetooth.c | 15 +++++++ extmod/modbluetooth.h | 6 +++ extmod/nimble/modbluetooth_nimble.c | 13 +++++- tests/multi_bluetooth/ble_characteristic.py | 8 +++- .../multi_bluetooth/ble_characteristic.py.exp | 1 + 7 files changed, 92 insertions(+), 14 deletions(-) diff --git a/examples/bluetooth/ble_temperature.py b/examples/bluetooth/ble_temperature.py index fd24e74d7498a..0e2da2239d50a 100644 --- a/examples/bluetooth/ble_temperature.py +++ b/examples/bluetooth/ble_temperature.py @@ -13,13 +13,14 @@ _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_INDICATE_DONE = const(20) # org.bluetooth.service.environmental_sensing _ENV_SENSE_UUID = bluetooth.UUID(0x181A) # org.bluetooth.characteristic.temperature _TEMP_CHAR = ( bluetooth.UUID(0x2A6E), - bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY, + bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_INDICATE, ) _ENV_SENSE_SERVICE = ( _ENV_SENSE_UUID, @@ -52,15 +53,21 @@ def _irq(self, event, data): self._connections.remove(conn_handle) # Start advertising again to allow a new connection. self._advertise() + elif event == _IRQ_GATTS_INDICATE_DONE: + conn_handle, value_handle, status, = data - def set_temperature(self, temp_deg_c, notify=False): + def set_temperature(self, temp_deg_c, notify=False, indicate=False): # Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius. # Write the local value, ready for a central to read. self._ble.gatts_write(self._handle, struct.pack(" att connected\n"); + // The ATT_EVENT_*CONNECTED events are fired for both peripheral and central role, with no way to tell which. + // So we use the HCI_EVENT_LE_META event directly in the main packet handler. } else if (event_type == ATT_EVENT_DISCONNECTED) { DEBUG_EVENT_printf(" --> att disconnected\n"); - } else if (event_type == HCI_EVENT_LE_META) { + } else if (event_type == ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE) { + DEBUG_EVENT_printf(" --> att indication complete\n"); + uint16_t conn_handle = att_event_handle_value_indication_complete_get_conn_handle(packet); + uint16_t value_handle = att_event_handle_value_indication_complete_get_attribute_handle(packet); + uint8_t status = att_event_handle_value_indication_complete_get_status(packet); + mp_bluetooth_gatts_on_indicate_complete(conn_handle, value_handle, status); + } else if (event_type == HCI_EVENT_LE_META || event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { + // Ignore, duplicated by att_server.c. + } else { + DEBUG_EVENT_printf(" --> hci att server event type: unknown (0x%02x)\n", event_type); + } +} + +STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { + DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + + uint8_t event_type = hci_event_packet_get_type(packet); + + if (event_type == HCI_EVENT_LE_META) { DEBUG_EVENT_printf(" --> hci le meta\n"); if (hci_event_le_meta_get_subevent_code(packet) == HCI_SUBEVENT_LE_CONNECTION_COMPLETE) { uint16_t conn_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); @@ -277,7 +305,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; } } else if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT) { - DEBUG_EVENT_printf(" --> hci transport packet set\n"); + DEBUG_EVENT_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { DEBUG_EVENT_printf(" --> hci command complete\n"); } else if (event_type == HCI_EVENT_COMMAND_STATUS) { @@ -288,6 +316,8 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_EVENT_printf(" --> btstack # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { DEBUG_EVENT_printf(" --> hci vendor specific\n"); + } else if (event_type == GATT_EVENT_MTU) { + DEBUG_EVENT_printf(" --> hci MTU\n"); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { DEBUG_EVENT_printf(" --> hci disconnect complete\n"); uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); @@ -500,6 +530,9 @@ int mp_bluetooth_init(void) { // Register for HCI events. hci_add_event_handler(&hci_event_callback_registration); + // Register for ATT server events. + att_server_register_packet_handler(&btstack_packet_handler_att_server); + // Set a timeout for HCI initialisation. btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS); btstack_run_loop_set_timer_handler(&btstack_init_deinit_timeout, btstack_init_deinit_timeout_handler); @@ -816,7 +849,8 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { size_t len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - // TODO: Handle ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE to generate acknowledgment event. + // Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when + // acknowledged (or timeout/error). // Attempt to send immediately, will copy buffer. MICROPY_PY_BLUETOOTH_ENTER diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index fb5c6ac8c99ab..7bfb774782048 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -800,6 +800,7 @@ STATIC const mp_rom_map_elem_t mp_module_bluetooth_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_FLAG_READ), MP_ROM_INT(MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) }, { MP_ROM_QSTR(MP_QSTR_FLAG_WRITE), MP_ROM_INT(MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE) }, { MP_ROM_QSTR(MP_QSTR_FLAG_NOTIFY), MP_ROM_INT(MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) }, + { MP_ROM_QSTR(MP_QSTR_FLAG_INDICATE), MP_ROM_INT(MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) }, { MP_ROM_QSTR(MP_QSTR_FLAG_WRITE_NO_RESPONSE), MP_ROM_INT(MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE) }, }; @@ -887,6 +888,9 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTS_WRITE) { // conn_handle, value_handle ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, NULL); + } else if (event == MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE) { + // conn_handle, value_handle, status + ringbuf_extract(&o->ringbuf, data_tuple, 2, 1, NULL, 0, NULL, NULL); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT) { // addr_type, addr, adv_type, rssi, adv_data @@ -999,6 +1003,17 @@ void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle) { schedule_ringbuf(atomic_state); } +void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t value_handle, uint8_t status) { + MICROPY_PY_BLUETOOTH_ENTER + mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); + if (enqueue_irq(o, 2 + 2 + 1, MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE)) { + ringbuf_put16(&o->ringbuf, conn_handle); + ringbuf_put16(&o->ringbuf, value_handle); + ringbuf_put(&o->ringbuf, status); + } + schedule_ringbuf(atomic_state); +} + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE void mp_bluetooth_gap_on_scan_complete(void) { MICROPY_PY_BLUETOOTH_ENTER diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index a232ee2d1d095..cdb86e5e63c44 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -68,6 +68,7 @@ #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE (1 << 2) #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE (1 << 3) #define MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY (1 << 4) +#define MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE (1 << 5) // For mp_bluetooth_gattc_write, the mode parameter #define MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE (0) @@ -107,6 +108,7 @@ #define MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE (17) #define MP_BLUETOOTH_IRQ_GATTC_NOTIFY (18) #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) +#define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) /* These aren't included in the module for space reasons, but can be used @@ -132,6 +134,7 @@ _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) +_IRQ_GATTS_INDICATE_DONE = const(20) */ // Common UUID type. @@ -245,6 +248,9 @@ void mp_bluetooth_gap_on_connected_disconnected(uint8_t event, uint16_t conn_han // Call this when a characteristic is written to. void mp_bluetooth_gatts_on_write(uint16_t conn_handle, uint16_t value_handle); +// Call this when an acknowledgment is received for an indication. +void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t value_handle, uint8_t status); + #if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK // Call this when a characteristic is read from. Return false to deny the read. bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index d60e23ecb4b9b..843989b2b8986 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -248,6 +248,15 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr); break; + + case BLE_GAP_EVENT_NOTIFY_TX: { + DEBUG_EVENT_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); + // This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0). + if (event->notify_tx.indication && event->notify_tx.status != 0) { + // Map "done/ack" to 0, otherwise pass the status directly. + mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status); + } + } } return 0; @@ -603,8 +612,8 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - // TODO: catch BLE_GAP_EVENT_NOTIFY_TX to raise an event for completed - // indication transaction. + // This will raise BLE_GAP_EVENT_NOTIFY_TX with a status when it is + // acknowledged (or timeout/error). return ble_hs_err_to_errno(ble_gattc_indicate(conn_handle, value_handle)); } diff --git a/tests/multi_bluetooth/ble_characteristic.py b/tests/multi_bluetooth/ble_characteristic.py index fd6fd467248de..0d72c181fdec6 100644 --- a/tests/multi_bluetooth/ble_characteristic.py +++ b/tests/multi_bluetooth/ble_characteristic.py @@ -17,12 +17,13 @@ _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) +_IRQ_GATTS_INDICATE_DONE = const(20) SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444") CHAR = ( CHAR_UUID, - bluetooth.FLAG_READ | bluetooth.FLAG_WRITE | bluetooth.FLAG_NOTIFY, + bluetooth.FLAG_READ | bluetooth.FLAG_WRITE | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_INDICATE, ) SERVICE = ( SERVICE_UUID, @@ -62,6 +63,8 @@ def irq(event, data): print("_IRQ_GATTC_NOTIFY", data[-1]) elif event == _IRQ_GATTC_INDICATE: print("_IRQ_GATTC_INDICATE", data[-1]) + elif event == _IRQ_GATTS_INDICATE_DONE: + print("_IRQ_GATTS_INDICATE_DONE", data[-1]) if waiting_event is not None: if (isinstance(waiting_event, int) and event == waiting_event) or ( @@ -128,6 +131,9 @@ def instance0(): print("gatts_indicate") ble.gatts_indicate(conn_handle, char_handle) + # Wait for the indicate ack. + wait_for_event(_IRQ_GATTS_INDICATE_DONE, TIMEOUT_MS) + # Wait for the central to disconnect. wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) finally: diff --git a/tests/multi_bluetooth/ble_characteristic.py.exp b/tests/multi_bluetooth/ble_characteristic.py.exp index 7f66a4d907455..25b5544efc459 100644 --- a/tests/multi_bluetooth/ble_characteristic.py.exp +++ b/tests/multi_bluetooth/ble_characteristic.py.exp @@ -9,6 +9,7 @@ gatts_notify _IRQ_GATTS_WRITE b'central2' gatts_write gatts_indicate +_IRQ_GATTS_INDICATE_DONE 0 _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect From c7f7c0214c433d52204fe73613bd6162507a0482 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 20 Jul 2020 17:33:12 +1000 Subject: [PATCH 174/352] docs/library: For ubluetooth, add docs for _IRQ_GATTS_INDICATE_DONE. --- docs/library/ubluetooth.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index ed2ffefe750ad..e103932efd2f6 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -148,6 +148,10 @@ Event Handling elif event == _IRQ_GATTC_INDICATE: # A peripheral has sent an indicate request. conn_handle, value_handle, notify_data = data + elif event == _IRQ_GATTS_INDICATE_DONE: + # A central has acknowledged the indication. + # Note: Status will be zero on successful acknowledgment, implementation-specific value otherwise. + conn_handle, value_handle, status = data The event codes are:: @@ -171,6 +175,7 @@ The event codes are:: _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) + _IRQ_GATTS_INDICATE_DONE = const(20) In order to save space in the firmware, these constants are not included on the :mod:`ubluetooth` module. Add the ones that you need from the list above to your @@ -313,8 +318,8 @@ writes from a central to a given characteristic, use always send the current local value (as set with :meth:`gatts_write `). - **Note:** This does not currently support generating an event for sucessful - acknowledgment of the indication. + On acknowledgment (or failure, e.g. timeout), the + ``_IRQ_GATTS_INDICATE_DONE`` event will be raised. .. method:: BLE.gatts_set_buffer(value_handle, len, append=False, /) From 9aa214077e6d1e0fba1a775431fedea4c8d76558 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Thu, 26 Mar 2020 23:17:35 -0700 Subject: [PATCH 175/352] extmod/modussl: Improve exception error messages. This commit adds human readable error messages when mbedtls or axtls raise an exception. Currently often just an EIO error is raised so the user is lost and can't tell whether it's a cert error, buffer overrun, connecting to a non-ssl port, etc. The axtls and mbedtls error raising in the ussl module is modified to raise: OSError(-err_num, "error string") For axtls a small error table of strings is added and used for the second argument of the OSErrer. For mbedtls the code uses mbedtls' built-in strerror function, and if there is an out of memory condition it just produces OSError(-err_num). Producing the error string for mbedtls is conditional on them being included in the mbedtls build, via MBEDTLS_ERROR_C. --- extmod/modussl_axtls.c | 55 ++++++++++++++++++++++++++-- extmod/modussl_mbedtls.c | 43 ++++++++++++++++++++-- tests/extmod/ussl_basic.py | 2 +- tests/extmod/ussl_basic.py.exp | 3 +- tests/net_inet/tls_num_errors.py | 44 ++++++++++++++++++++++ tests/net_inet/tls_num_errors.py.exp | 2 + tests/net_inet/tls_text_errors.py | 33 +++++++++++++++++ 7 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 tests/net_inet/tls_num_errors.py create mode 100644 tests/net_inet/tls_num_errors.py.exp create mode 100644 tests/net_inet/tls_text_errors.py diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 7b0e3cbcbc327..0b0ce35fc8711 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -29,6 +29,7 @@ #include "py/runtime.h" #include "py/stream.h" +#include "py/objstr.h" #if MICROPY_PY_USSL && MICROPY_SSL_AXTLS @@ -54,6 +55,56 @@ struct ssl_args { STATIC const mp_obj_type_t ussl_socket_type; +// Table of errors +struct ssl_errs { + int16_t errnum; + const char *errstr; +}; +STATIC const struct ssl_errs ssl_error_tab[] = { + { SSL_NOT_OK, "NOT_OK" }, + { SSL_ERROR_DEAD, "DEAD" }, + { SSL_CLOSE_NOTIFY, "CLOSE_NOTIFY" }, + { SSL_EAGAIN, "EAGAIN" }, + { SSL_ERROR_CONN_LOST, "CONN_LOST" }, + { SSL_ERROR_RECORD_OVERFLOW, "RECORD_OVERFLOW" }, + { SSL_ERROR_SOCK_SETUP_FAILURE, "SOCK_SETUP_FAILURE" }, + { SSL_ERROR_INVALID_HANDSHAKE, "INVALID_HANDSHAKE" }, + { SSL_ERROR_INVALID_PROT_MSG, "INVALID_PROT_MSG" }, + { SSL_ERROR_INVALID_HMAC, "INVALID_HMAC" }, + { SSL_ERROR_INVALID_VERSION, "INVALID_VERSION" }, + { SSL_ERROR_UNSUPPORTED_EXTENSION, "UNSUPPORTED_EXTENSION" }, + { SSL_ERROR_INVALID_SESSION, "INVALID_SESSION" }, + { SSL_ERROR_NO_CIPHER, "NO_CIPHER" }, + { SSL_ERROR_INVALID_CERT_HASH_ALG, "INVALID_CERT_HASH_ALG" }, + { SSL_ERROR_BAD_CERTIFICATE, "BAD_CERTIFICATE" }, + { SSL_ERROR_INVALID_KEY, "INVALID_KEY" }, + { SSL_ERROR_FINISHED_INVALID, "FINISHED_INVALID" }, + { SSL_ERROR_NO_CERT_DEFINED, "NO_CERT_DEFINED" }, + { SSL_ERROR_NO_CLIENT_RENOG, "NO_CLIENT_RENOG" }, + { SSL_ERROR_NOT_SUPPORTED, "NOT_SUPPORTED" }, +}; + +STATIC NORETURN void ussl_raise_error(int err) { + for (size_t i = 0; i < MP_ARRAY_SIZE(ssl_error_tab); i++) { + if (ssl_error_tab[i].errnum == err) { + // construct string object + mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + if (o_str == NULL) { + break; + } + o_str->base.type = &mp_type_str; + o_str->data = (const byte *)ssl_error_tab[i].errstr; + o_str->len = strlen((char *)o_str->data); + 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)); + } + } + mp_raise_OSError(err); +} + + STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args) { #if MICROPY_PY_USSL_FINALISER mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); @@ -107,9 +158,7 @@ STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args int res = ssl_handshake_status(o->ssl_sock); if (res != SSL_OK) { - printf("ssl_handshake_status: %d\n", res); - ssl_display_error(res); - mp_raise_OSError(MP_EIO); + ussl_raise_error(res); } } diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 9e117c82ccf34..94061ddc8b946 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -34,6 +34,7 @@ #include "py/runtime.h" #include "py/stream.h" +#include "py/objstr.h" // mbedtls_time_t #include "mbedtls/platform.h" @@ -43,6 +44,7 @@ #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/debug.h" +#include "mbedtls/error.h" typedef struct _mp_obj_ssl_socket_t { mp_obj_base_t base; @@ -74,6 +76,42 @@ STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, cons } #endif +STATIC NORETURN void mbedtls_raise_error(int err) { + #if defined(MBEDTLS_ERROR_C) + // Including mbedtls_strerror takes about 16KB on the esp32 due to all the 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. + // "small" negative integer error codes come from underlying stream/sockets, not mbedtls + if (err < 0 && err > -256) { + mp_raise_OSError(-err); + } + + // Try to allocate memory for the message + #define ERR_STR_MAX 100 // 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 = strnlen((char *)o_str_buf, ERR_STR_MAX); + + // 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); // typ. err is negative + #endif +} + STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t *)ctx; @@ -85,7 +123,7 @@ STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { if (mp_is_nonblocking_error(err)) { return MBEDTLS_ERR_SSL_WANT_WRITE; } - return -err; + return -err; // convert an MP_ERRNO to something mbedtls passes through as error } else { return out_sz; } @@ -197,7 +235,6 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { 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) { - printf("mbedtls_ssl_handshake error: -%x\n", -ret); goto cleanup; } } @@ -221,7 +258,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); } else { - mp_raise_OSError(MP_EIO); + mbedtls_raise_error(ret); } } diff --git a/tests/extmod/ussl_basic.py b/tests/extmod/ussl_basic.py index b4e21c7dce660..9e1821dca9981 100644 --- a/tests/extmod/ussl_basic.py +++ b/tests/extmod/ussl_basic.py @@ -9,7 +9,7 @@ # create in client mode try: - ss = ssl.wrap_socket(io.BytesIO()) + ss = ssl.wrap_socket(io.BytesIO(), server_hostname="test.example.com") except OSError as er: print("wrap_socket:", repr(er)) diff --git a/tests/extmod/ussl_basic.py.exp b/tests/extmod/ussl_basic.py.exp index 5282338319efb..eb7df855aa099 100644 --- a/tests/extmod/ussl_basic.py.exp +++ b/tests/extmod/ussl_basic.py.exp @@ -1,5 +1,4 @@ -ssl_handshake_status: -256 -wrap_socket: OSError(5,) +wrap_socket: OSError(-256, 'CONN_LOST') <_SSLSocket 4 b'' diff --git a/tests/net_inet/tls_num_errors.py b/tests/net_inet/tls_num_errors.py new file mode 100644 index 0000000000000..dd7f714e6ef7e --- /dev/null +++ b/tests/net_inet/tls_num_errors.py @@ -0,0 +1,44 @@ +# test that modtls produces a numerical error message when out of heap + +try: + import usocket as socket, ussl as ssl, sys +except: + import socket, ssl, sys +try: + from micropython import alloc_emergency_exception_buf, heap_lock, heap_unlock +except: + print("SKIP") + raise SystemExit + + +# test with heap locked to see it switch to number-only error message +def test(addr): + alloc_emergency_exception_buf(256) + s = socket.socket() + s.connect(addr) + try: + s.setblocking(False) + s = ssl.wrap_socket(s, do_handshake=False) + heap_lock() + print("heap is locked") + while True: + ret = s.write("foo") + if ret: + break + heap_unlock() + print("wrap: no exception") + except OSError as e: + heap_unlock() + # mbedtls produces "-29184" + # axtls produces "RECORD_OVERFLOW" + ok = "-29184" in str(e) or "RECORD_OVERFLOW" in str(e) + print("wrap:", ok) + if not ok: + print("got exception:", e) + s.close() + + +if __name__ == "__main__": + # connect to plain HTTP port, oops! + addr = socket.getaddrinfo("micropython.org", 80)[0][-1] + test(addr) diff --git a/tests/net_inet/tls_num_errors.py.exp b/tests/net_inet/tls_num_errors.py.exp new file mode 100644 index 0000000000000..e6a15634d00ae --- /dev/null +++ b/tests/net_inet/tls_num_errors.py.exp @@ -0,0 +1,2 @@ +heap is locked +wrap: True diff --git a/tests/net_inet/tls_text_errors.py b/tests/net_inet/tls_text_errors.py new file mode 100644 index 0000000000000..2ba167b868ef8 --- /dev/null +++ b/tests/net_inet/tls_text_errors.py @@ -0,0 +1,33 @@ +# test that modtls produces a text error message + +try: + import usocket as socket, ussl as ssl, sys +except: + import socket, ssl, sys + + +def test(addr): + s = socket.socket() + s.connect(addr) + try: + s = ssl.wrap_socket(s) + print("wrap: no exception") + except OSError as e: + # mbedtls produces "mbedtls -0x7200: SSL - An invalid SSL record was received" + # axtls produces "RECORD_OVERFLOW" + # CPython produces "[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1108)" + ok = ( + "invalid SSL record" in str(e) + or "RECORD_OVERFLOW" in str(e) + or "wrong version" in str(e) + ) + print("wrap:", ok) + if not ok: + print("got exception:", e) + s.close() + + +if __name__ == "__main__": + # connect to plain HTTP port, oops! + addr = socket.getaddrinfo("micropython.org", 80)[0][-1] + test(addr) From 98e583430fb7b793119db27bad9f98119e81579f Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Thu, 2 Jul 2020 12:48:16 -0700 Subject: [PATCH 176/352] lib/libc: Add implementation of strncpy. --- lib/libc/string0.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/libc/string0.c b/lib/libc/string0.c index 8c86bf65f7b40..5f40a71e3e284 100644 --- a/lib/libc/string0.c +++ b/lib/libc/string0.c @@ -169,6 +169,23 @@ char *strcpy(char *dest, const char *src) { return dest; } +// Public Domain implementation of strncpy from: +// http://en.wikibooks.org/wiki/C_Programming/Strings#The_strncpy_function +char *strncpy(char *s1, const char *s2, size_t n) { + char *dst = s1; + const char *src = s2; + /* Copy bytes, one at a time. */ + while (n > 0) { + n--; + if ((*dst++ = *src++) == '\0') { + /* If we get here, we found a null character at the end of s2 */ + *dst = '\0'; + break; + } + } + return s1; + } + // needed because gcc optimises strcpy + strcat to this char *stpcpy(char *dest, const char *src) { while (*src) { From 3e758ef235793502061edd122cd5cd91172faf51 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 29 May 2020 13:05:47 -0700 Subject: [PATCH 177/352] lib/mbedtls_errors: Add code to patch mbedtls for shortened error strs. The file `mbedtls_errors/mp_mbedtls_errors.c` can be used instead of `mbedtls/library/error.c` to give shorter error strings, reducing the build size of the error strings from about 12-16kB down to about 2-5kB. --- lib/mbedtls_errors/README.md | 42 ++ lib/mbedtls_errors/do-esp32.sh | 7 + lib/mbedtls_errors/do-mp.sh | 4 + lib/mbedtls_errors/do-test.sh | 4 + lib/mbedtls_errors/error.fmt | 165 ++++++ lib/mbedtls_errors/generate_errors.diff | 22 + lib/mbedtls_errors/mp_mbedtls_errors.c | 705 ++++++++++++++++++++++++ lib/mbedtls_errors/tester.c | 58 ++ tools/codeformat.py | 1 + 9 files changed, 1008 insertions(+) create mode 100644 lib/mbedtls_errors/README.md create mode 100755 lib/mbedtls_errors/do-esp32.sh create mode 100755 lib/mbedtls_errors/do-mp.sh create mode 100755 lib/mbedtls_errors/do-test.sh create mode 100644 lib/mbedtls_errors/error.fmt create mode 100644 lib/mbedtls_errors/generate_errors.diff create mode 100644 lib/mbedtls_errors/mp_mbedtls_errors.c create mode 100644 lib/mbedtls_errors/tester.c diff --git a/lib/mbedtls_errors/README.md b/lib/mbedtls_errors/README.md new file mode 100644 index 0000000000000..0e13021eb1910 --- /dev/null +++ b/lib/mbedtls_errors/README.md @@ -0,0 +1,42 @@ +MBEDTLS Error Strings for MicroPython +===================================== + +This directory contains source code and tools to rework the Mbedtls error strings for +micropython to use less space. In short, instead of storing and printing something like +"SSL - Our own certificate(s) is/are too large to send in an SSL message" it prints +the name of the error #define, which would be "MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE" in +this case, and only stores `SSL_CERTIFICATE_TOO_LARGE` in flash. The exact Mbedtls error +defines are used because they're easy to search for to find more detailed information. + +Mbedtls defines a specific format for error value #defines and +includes a Perl script to gather all `MBEDTLS_ERR` defines from includes files together with +english error text. From that the Perl script generates `mbedtls_strerror()`. The files in this +directory modify this process to produce a more space efficient error lookup table with +shorter error strings. + +The files are as follows: +- `generate_errors.diff` - diff for original mbedtls perl script +- `error.fmt` - modified code template for MicroPython +- `mp_mbedtls_errors.c` - source file with `mbedtls_strerror` this is built using the include + files in `../mbedtls` +- `do-mp.sh` - shell script to produce `mp_mbedtls_errors.c` +- `tester.c` - simple C main to test `mp_mbedtls_errors.c` locally on a dev box +- `do-test.sh` - shell script to produce `mp_mbedtls_errors.c` and compile the `tester` app +- `do-esp32.sh` - shell script to produce `esp32_mbedtls_errors.c` -- see below + +In order not to store multiple copies of `mbedtls_errors.c` +([https://github.com/micropython/micropython/pull/5819#discussion_r445528006](see)) +it is assumed that all ports use the same version of mbedtls with the same error #defines. +This is true as of MP v1.13, and ESP-IDF versions 3.3.2 and 4.0.1. If anything changes in the +future the `do-esp32.sh` script can be used to generate an esp32-specific version. + +### How-to + +- To build MicroPython all that is needed is to include the `mp_mbedtls_errors.c` into the build + (the Makefiles do this automatically). Note that Perl is not needed for routine MicroPython + builds. +- When a new version of Mbedtls is pulled-in the `do-mp.sh` script should be run to + re-generate `mp_mbedtls_errors.c`. +- The `tester` app should be run if changes to the string handling in `error.fmt` are made: + it tests that there is not an off-by-one error in the string copying/appending, etc. +- To include `mbedtls_strerror` error strings define `MBEDTLS_ERROR_C` in the build. diff --git a/lib/mbedtls_errors/do-esp32.sh b/lib/mbedtls_errors/do-esp32.sh new file mode 100755 index 0000000000000..6fd4682415c3f --- /dev/null +++ b/lib/mbedtls_errors/do-esp32.sh @@ -0,0 +1,7 @@ +#! /bin/bash -e +# Generate esp32_mbedtls_errors.c for use in the Esp32 port, with the ESP-IDF version of mbedtls +# The IDF_PATH env var must be set to the top-level dir of ESPIDF +echo "IDF_PATH=$IDF_PATH" +MBEDTLS=$IDF_PATH/components/mbedtls/mbedtls +patch -o esp32_generate_errors.pl $MBEDTLS/scripts/generate_errors.pl +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#define mbedtls_snprintf snprintf +#define mbedtls_time_t time_t +#endif + +#if defined(MBEDTLS_ERROR_C) + +#include + +HEADER_INCLUDED + +// Error code table type +struct ssl_errs { + int16_t errnum; + const char *errstr; +}; + +// Table of high level error codes +static const struct ssl_errs mbedtls_high_level_error_tab[] = { +// BEGIN generated code +HIGH_LEVEL_CODE_CHECKS +// END generated code +}; + +static const struct ssl_errs mbedtls_low_level_error_tab[] = { +// Low level error codes +// +// BEGIN generated code +LOW_LEVEL_CODE_CHECKS +// END generated code +}; + +static const char *mbedtls_err_prefix = "MBEDTLS_ERR_"; +#define MBEDTLS_ERR_PREFIX_LEN ( sizeof("MBEDTLS_ERR_")-1 ) + +// copy error text into buffer, ensure null termination, return strlen of result +static size_t mbedtls_err_to_str(int err, const struct ssl_errs tab[], int tab_len, char *buf, size_t buflen) { + if (buflen == 0) return 0; + + // prefix for all error names + strncpy(buf, mbedtls_err_prefix, buflen); + if (buflen <= MBEDTLS_ERR_PREFIX_LEN+1) { + buf[buflen-1] = 0; + return buflen-1; + } + + // append error name from table + for (int i = 0; i < tab_len; i++) { + if (tab[i].errnum == err) { + strncpy(buf+MBEDTLS_ERR_PREFIX_LEN, tab[i].errstr, buflen-MBEDTLS_ERR_PREFIX_LEN); + buf[buflen-1] = 0; + return strlen(buf); + } + } + + mbedtls_snprintf(buf+MBEDTLS_ERR_PREFIX_LEN, buflen-MBEDTLS_ERR_PREFIX_LEN, "UNKNOWN (0x%04X)", + err); + return strlen(buf); +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +void mbedtls_strerror(int ret, char *buf, size_t buflen) { + int use_ret; + + if (buflen == 0) return; + + buf[buflen-1] = 0; + + if (ret < 0) ret = -ret; + + // + // High-level error codes + // + uint8_t got_hl = (ret & 0xFF80) != 0; + if (got_hl) { + use_ret = ret & 0xFF80; + + // special case +#if defined(MBEDTLS_SSL_TLS_C) + if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { + strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen); + buf[buflen-1] = 0; + return; + } +#endif + + size_t len = mbedtls_err_to_str(use_ret, mbedtls_high_level_error_tab, + ARRAY_SIZE(mbedtls_high_level_error_tab), buf, buflen); + + buf += len; + buflen -= len; + if (buflen == 0) return; + } + + // + // Low-level error codes + // + use_ret = ret & ~0xFF80; + + if (use_ret == 0) return; + + // If high level code is present, make a concatenation between both error strings. + if (got_hl) { + if (buflen < 2) return; + *buf++ = '+'; + buflen--; + } + + mbedtls_err_to_str(use_ret, mbedtls_low_level_error_tab, + ARRAY_SIZE(mbedtls_low_level_error_tab), buf, buflen); +} + +#else /* MBEDTLS_ERROR_C */ + +#if defined(MBEDTLS_ERROR_STRERROR_DUMMY) + +/* + * Provide an non-function in case MBEDTLS_ERROR_C is not defined + */ +void mbedtls_strerror( int ret, char *buf, size_t buflen ) +{ + ((void) ret); + + if( buflen > 0 ) + buf[0] = '\0'; +} + +#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */ + +#endif /* MBEDTLS_ERROR_C */ diff --git a/lib/mbedtls_errors/generate_errors.diff b/lib/mbedtls_errors/generate_errors.diff new file mode 100644 index 0000000000000..ad24c372faed7 --- /dev/null +++ b/lib/mbedtls_errors/generate_errors.diff @@ -0,0 +1,22 @@ +--- generate_errors_orig.pl 2020-06-20 08:40:38.819060379 -0700 ++++ generate_errors.pl 2020-06-20 08:47:26.511163591 -0700 +@@ -162,16 +162,12 @@ + + if ($error_name eq "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE") + { +- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n". +- "${white_space}\{\n". +- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n". +- "${white_space} return;\n". +- "${white_space}}\n" ++ # no-op, this case is hard-coded in error.fmt + } + else + { +- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n". +- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n" ++ my $error_text = $error_name =~ s/^MBEDTLS_ERR_//r; ++ ${$code_check} .= "${white_space}{ -($error_name), \"$error_text\" },\n" + } + }; + diff --git a/lib/mbedtls_errors/mp_mbedtls_errors.c b/lib/mbedtls_errors/mp_mbedtls_errors.c new file mode 100644 index 0000000000000..03a91f0dc9495 --- /dev/null +++ b/lib/mbedtls_errors/mp_mbedtls_errors.c @@ -0,0 +1,705 @@ +/* + * Error message information + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_ERROR_C) || defined(MBEDTLS_ERROR_STRERROR_DUMMY) +#include "mbedtls/error.h" +#include +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#define mbedtls_snprintf snprintf +#define mbedtls_time_t time_t +#endif + +#if defined(MBEDTLS_ERROR_C) + +#include + +#if defined(MBEDTLS_AES_C) +#include "mbedtls/aes.h" +#endif + +#if defined(MBEDTLS_ARC4_C) +#include "mbedtls/arc4.h" +#endif + +#if defined(MBEDTLS_ARIA_C) +#include "mbedtls/aria.h" +#endif + +#if defined(MBEDTLS_BASE64_C) +#include "mbedtls/base64.h" +#endif + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#if defined(MBEDTLS_BLOWFISH_C) +#include "mbedtls/blowfish.h" +#endif + +#if defined(MBEDTLS_CAMELLIA_C) +#include "mbedtls/camellia.h" +#endif + +#if defined(MBEDTLS_CCM_C) +#include "mbedtls/ccm.h" +#endif + +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + +#if defined(MBEDTLS_CIPHER_C) +#include "mbedtls/cipher.h" +#endif + +#if defined(MBEDTLS_CMAC_C) +#include "mbedtls/cmac.h" +#endif + +#if defined(MBEDTLS_CTR_DRBG_C) +#include "mbedtls/ctr_drbg.h" +#endif + +#if defined(MBEDTLS_DES_C) +#include "mbedtls/des.h" +#endif + +#if defined(MBEDTLS_DHM_C) +#include "mbedtls/dhm.h" +#endif + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_ENTROPY_C) +#include "mbedtls/entropy.h" +#endif + +#if defined(MBEDTLS_GCM_C) +#include "mbedtls/gcm.h" +#endif + +#if defined(MBEDTLS_HKDF_C) +#include "mbedtls/hkdf.h" +#endif + +#if defined(MBEDTLS_HMAC_DRBG_C) +#include "mbedtls/hmac_drbg.h" +#endif + +#if defined(MBEDTLS_MD_C) +#include "mbedtls/md.h" +#endif + +#if defined(MBEDTLS_MD2_C) +#include "mbedtls/md2.h" +#endif + +#if defined(MBEDTLS_MD4_C) +#include "mbedtls/md4.h" +#endif + +#if defined(MBEDTLS_MD5_C) +#include "mbedtls/md5.h" +#endif + +#if defined(MBEDTLS_NET_C) +#include "mbedtls/net_sockets.h" +#endif + +#if defined(MBEDTLS_OID_C) +#include "mbedtls/oid.h" +#endif + +#if defined(MBEDTLS_PADLOCK_C) +#include "mbedtls/padlock.h" +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif + +#if defined(MBEDTLS_PK_C) +#include "mbedtls/pk.h" +#endif + +#if defined(MBEDTLS_PKCS12_C) +#include "mbedtls/pkcs12.h" +#endif + +#if defined(MBEDTLS_PKCS5_C) +#include "mbedtls/pkcs5.h" +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#endif + +#if defined(MBEDTLS_POLY1305_C) +#include "mbedtls/poly1305.h" +#endif + +#if defined(MBEDTLS_RIPEMD160_C) +#include "mbedtls/ripemd160.h" +#endif + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_SHA1_C) +#include "mbedtls/sha1.h" +#endif + +#if defined(MBEDTLS_SHA256_C) +#include "mbedtls/sha256.h" +#endif + +#if defined(MBEDTLS_SHA512_C) +#include "mbedtls/sha512.h" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) +#include "mbedtls/ssl.h" +#endif + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) +#include "mbedtls/x509.h" +#endif + +#if defined(MBEDTLS_XTEA_C) +#include "mbedtls/xtea.h" +#endif + + +// Error code table type +struct ssl_errs { + int16_t errnum; + const char *errstr; +}; + +// Table of high level error codes +static const struct ssl_errs mbedtls_high_level_error_tab[] = { +// BEGIN generated code +#if defined(MBEDTLS_CIPHER_C) + { -(MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE), "CIPHER_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA), "CIPHER_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_CIPHER_ALLOC_FAILED), "CIPHER_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_CIPHER_INVALID_PADDING), "CIPHER_INVALID_PADDING" }, + { -(MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED), "CIPHER_FULL_BLOCK_EXPECTED" }, + { -(MBEDTLS_ERR_CIPHER_AUTH_FAILED), "CIPHER_AUTH_FAILED" }, + { -(MBEDTLS_ERR_CIPHER_INVALID_CONTEXT), "CIPHER_INVALID_CONTEXT" }, + { -(MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED), "CIPHER_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_CIPHER_C */ + +#if defined(MBEDTLS_DHM_C) + { -(MBEDTLS_ERR_DHM_BAD_INPUT_DATA), "DHM_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED), "DHM_READ_PARAMS_FAILED" }, + { -(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED), "DHM_MAKE_PARAMS_FAILED" }, + { -(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED), "DHM_READ_PUBLIC_FAILED" }, + { -(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED), "DHM_MAKE_PUBLIC_FAILED" }, + { -(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED), "DHM_CALC_SECRET_FAILED" }, + { -(MBEDTLS_ERR_DHM_INVALID_FORMAT), "DHM_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_DHM_ALLOC_FAILED), "DHM_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_DHM_FILE_IO_ERROR), "DHM_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_DHM_HW_ACCEL_FAILED), "DHM_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_DHM_SET_GROUP_FAILED), "DHM_SET_GROUP_FAILED" }, +#endif /* MBEDTLS_DHM_C */ + +#if defined(MBEDTLS_ECP_C) + { -(MBEDTLS_ERR_ECP_BAD_INPUT_DATA), "ECP_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL), "ECP_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE), "ECP_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_ECP_VERIFY_FAILED), "ECP_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_ECP_ALLOC_FAILED), "ECP_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_ECP_RANDOM_FAILED), "ECP_RANDOM_FAILED" }, + { -(MBEDTLS_ERR_ECP_INVALID_KEY), "ECP_INVALID_KEY" }, + { -(MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH), "ECP_SIG_LEN_MISMATCH" }, + { -(MBEDTLS_ERR_ECP_HW_ACCEL_FAILED), "ECP_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_ECP_IN_PROGRESS), "ECP_IN_PROGRESS" }, +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_MD_C) + { -(MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE), "MD_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_MD_BAD_INPUT_DATA), "MD_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_MD_ALLOC_FAILED), "MD_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_MD_FILE_IO_ERROR), "MD_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_MD_HW_ACCEL_FAILED), "MD_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_MD_C */ + +#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) + { -(MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT), "PEM_NO_HEADER_FOOTER_PRESENT" }, + { -(MBEDTLS_ERR_PEM_INVALID_DATA), "PEM_INVALID_DATA" }, + { -(MBEDTLS_ERR_PEM_ALLOC_FAILED), "PEM_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_PEM_INVALID_ENC_IV), "PEM_INVALID_ENC_IV" }, + { -(MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG), "PEM_UNKNOWN_ENC_ALG" }, + { -(MBEDTLS_ERR_PEM_PASSWORD_REQUIRED), "PEM_PASSWORD_REQUIRED" }, + { -(MBEDTLS_ERR_PEM_PASSWORD_MISMATCH), "PEM_PASSWORD_MISMATCH" }, + { -(MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE), "PEM_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_PEM_BAD_INPUT_DATA), "PEM_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */ + +#if defined(MBEDTLS_PK_C) + { -(MBEDTLS_ERR_PK_ALLOC_FAILED), "PK_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_PK_TYPE_MISMATCH), "PK_TYPE_MISMATCH" }, + { -(MBEDTLS_ERR_PK_BAD_INPUT_DATA), "PK_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_PK_FILE_IO_ERROR), "PK_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_PK_KEY_INVALID_VERSION), "PK_KEY_INVALID_VERSION" }, + { -(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT), "PK_KEY_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_PK_UNKNOWN_PK_ALG), "PK_UNKNOWN_PK_ALG" }, + { -(MBEDTLS_ERR_PK_PASSWORD_REQUIRED), "PK_PASSWORD_REQUIRED" }, + { -(MBEDTLS_ERR_PK_PASSWORD_MISMATCH), "PK_PASSWORD_MISMATCH" }, + { -(MBEDTLS_ERR_PK_INVALID_PUBKEY), "PK_INVALID_PUBKEY" }, + { -(MBEDTLS_ERR_PK_INVALID_ALG), "PK_INVALID_ALG" }, + { -(MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE), "PK_UNKNOWN_NAMED_CURVE" }, + { -(MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE), "PK_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_PK_SIG_LEN_MISMATCH), "PK_SIG_LEN_MISMATCH" }, + { -(MBEDTLS_ERR_PK_HW_ACCEL_FAILED), "PK_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_PK_C */ + +#if defined(MBEDTLS_PKCS12_C) + { -(MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA), "PKCS12_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE), "PKCS12_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT), "PKCS12_PBE_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH), "PKCS12_PASSWORD_MISMATCH" }, +#endif /* MBEDTLS_PKCS12_C */ + +#if defined(MBEDTLS_PKCS5_C) + { -(MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA), "PKCS5_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_PKCS5_INVALID_FORMAT), "PKCS5_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE), "PKCS5_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH), "PKCS5_PASSWORD_MISMATCH" }, +#endif /* MBEDTLS_PKCS5_C */ + +#if defined(MBEDTLS_RSA_C) + { -(MBEDTLS_ERR_RSA_BAD_INPUT_DATA), "RSA_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_RSA_INVALID_PADDING), "RSA_INVALID_PADDING" }, + { -(MBEDTLS_ERR_RSA_KEY_GEN_FAILED), "RSA_KEY_GEN_FAILED" }, + { -(MBEDTLS_ERR_RSA_KEY_CHECK_FAILED), "RSA_KEY_CHECK_FAILED" }, + { -(MBEDTLS_ERR_RSA_PUBLIC_FAILED), "RSA_PUBLIC_FAILED" }, + { -(MBEDTLS_ERR_RSA_PRIVATE_FAILED), "RSA_PRIVATE_FAILED" }, + { -(MBEDTLS_ERR_RSA_VERIFY_FAILED), "RSA_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE), "RSA_OUTPUT_TOO_LARGE" }, + { -(MBEDTLS_ERR_RSA_RNG_FAILED), "RSA_RNG_FAILED" }, + { -(MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION), "RSA_UNSUPPORTED_OPERATION" }, + { -(MBEDTLS_ERR_RSA_HW_ACCEL_FAILED), "RSA_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_SSL_TLS_C) + { -(MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE), "SSL_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_SSL_BAD_INPUT_DATA), "SSL_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_SSL_INVALID_MAC), "SSL_INVALID_MAC" }, + { -(MBEDTLS_ERR_SSL_INVALID_RECORD), "SSL_INVALID_RECORD" }, + { -(MBEDTLS_ERR_SSL_CONN_EOF), "SSL_CONN_EOF" }, + { -(MBEDTLS_ERR_SSL_UNKNOWN_CIPHER), "SSL_UNKNOWN_CIPHER" }, + { -(MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN), "SSL_NO_CIPHER_CHOSEN" }, + { -(MBEDTLS_ERR_SSL_NO_RNG), "SSL_NO_RNG" }, + { -(MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE), "SSL_NO_CLIENT_CERTIFICATE" }, + { -(MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE), "SSL_CERTIFICATE_TOO_LARGE" }, + { -(MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED), "SSL_CERTIFICATE_REQUIRED" }, + { -(MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED), "SSL_PRIVATE_KEY_REQUIRED" }, + { -(MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED), "SSL_CA_CHAIN_REQUIRED" }, + { -(MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE), "SSL_UNEXPECTED_MESSAGE" }, + { -(MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED), "SSL_PEER_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY), "SSL_PEER_CLOSE_NOTIFY" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO), "SSL_BAD_HS_CLIENT_HELLO" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO), "SSL_BAD_HS_SERVER_HELLO" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE), "SSL_BAD_HS_CERTIFICATE" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST), "SSL_BAD_HS_CERTIFICATE_REQUEST" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE), "SSL_BAD_HS_SERVER_KEY_EXCHANGE" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE), "SSL_BAD_HS_SERVER_HELLO_DONE" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY), "SSL_BAD_HS_CERTIFICATE_VERIFY" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC), "SSL_BAD_HS_CHANGE_CIPHER_SPEC" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_FINISHED), "SSL_BAD_HS_FINISHED" }, + { -(MBEDTLS_ERR_SSL_ALLOC_FAILED), "SSL_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED), "SSL_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH), "SSL_HW_ACCEL_FALLTHROUGH" }, + { -(MBEDTLS_ERR_SSL_COMPRESSION_FAILED), "SSL_COMPRESSION_FAILED" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION), "SSL_BAD_HS_PROTOCOL_VERSION" }, + { -(MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET), "SSL_BAD_HS_NEW_SESSION_TICKET" }, + { -(MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED), "SSL_SESSION_TICKET_EXPIRED" }, + { -(MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH), "SSL_PK_TYPE_MISMATCH" }, + { -(MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY), "SSL_UNKNOWN_IDENTITY" }, + { -(MBEDTLS_ERR_SSL_INTERNAL_ERROR), "SSL_INTERNAL_ERROR" }, + { -(MBEDTLS_ERR_SSL_COUNTER_WRAPPING), "SSL_COUNTER_WRAPPING" }, + { -(MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO), "SSL_WAITING_SERVER_HELLO_RENEGO" }, + { -(MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED), "SSL_HELLO_VERIFY_REQUIRED" }, + { -(MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL), "SSL_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE), "SSL_NO_USABLE_CIPHERSUITE" }, + { -(MBEDTLS_ERR_SSL_WANT_READ), "SSL_WANT_READ" }, + { -(MBEDTLS_ERR_SSL_WANT_WRITE), "SSL_WANT_WRITE" }, + { -(MBEDTLS_ERR_SSL_TIMEOUT), "SSL_TIMEOUT" }, + { -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT), "SSL_CLIENT_RECONNECT" }, + { -(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD), "SSL_UNEXPECTED_RECORD" }, + { -(MBEDTLS_ERR_SSL_NON_FATAL), "SSL_NON_FATAL" }, + { -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH), "SSL_INVALID_VERIFY_HASH" }, + { -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING), "SSL_CONTINUE_PROCESSING" }, + { -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS), "SSL_ASYNC_IN_PROGRESS" }, + { -(MBEDTLS_ERR_SSL_EARLY_MESSAGE), "SSL_EARLY_MESSAGE" }, + { -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS), "SSL_CRYPTO_IN_PROGRESS" }, +#endif /* MBEDTLS_SSL_TLS_C */ + +#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) + { -(MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE), "X509_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_X509_UNKNOWN_OID), "X509_UNKNOWN_OID" }, + { -(MBEDTLS_ERR_X509_INVALID_FORMAT), "X509_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_X509_INVALID_VERSION), "X509_INVALID_VERSION" }, + { -(MBEDTLS_ERR_X509_INVALID_SERIAL), "X509_INVALID_SERIAL" }, + { -(MBEDTLS_ERR_X509_INVALID_ALG), "X509_INVALID_ALG" }, + { -(MBEDTLS_ERR_X509_INVALID_NAME), "X509_INVALID_NAME" }, + { -(MBEDTLS_ERR_X509_INVALID_DATE), "X509_INVALID_DATE" }, + { -(MBEDTLS_ERR_X509_INVALID_SIGNATURE), "X509_INVALID_SIGNATURE" }, + { -(MBEDTLS_ERR_X509_INVALID_EXTENSIONS), "X509_INVALID_EXTENSIONS" }, + { -(MBEDTLS_ERR_X509_UNKNOWN_VERSION), "X509_UNKNOWN_VERSION" }, + { -(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG), "X509_UNKNOWN_SIG_ALG" }, + { -(MBEDTLS_ERR_X509_SIG_MISMATCH), "X509_SIG_MISMATCH" }, + { -(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED), "X509_CERT_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT), "X509_CERT_UNKNOWN_FORMAT" }, + { -(MBEDTLS_ERR_X509_BAD_INPUT_DATA), "X509_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_X509_ALLOC_FAILED), "X509_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_X509_FILE_IO_ERROR), "X509_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_X509_BUFFER_TOO_SMALL), "X509_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_X509_FATAL_ERROR), "X509_FATAL_ERROR" }, +#endif /* MBEDTLS_X509_USE_C || MBEDTLS_X509_CREATE_C */ +// END generated code +}; + +static const struct ssl_errs mbedtls_low_level_error_tab[] = { +// Low level error codes +// +// BEGIN generated code +#if defined(MBEDTLS_AES_C) + { -(MBEDTLS_ERR_AES_INVALID_KEY_LENGTH), "AES_INVALID_KEY_LENGTH" }, + { -(MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH), "AES_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_AES_BAD_INPUT_DATA), "AES_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE), "AES_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_AES_HW_ACCEL_FAILED), "AES_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_ARC4_C) + { -(MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED), "ARC4_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_ARC4_C */ + +#if defined(MBEDTLS_ARIA_C) + { -(MBEDTLS_ERR_ARIA_BAD_INPUT_DATA), "ARIA_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH), "ARIA_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE), "ARIA_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED), "ARIA_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_ARIA_C */ + +#if defined(MBEDTLS_ASN1_PARSE_C) + { -(MBEDTLS_ERR_ASN1_OUT_OF_DATA), "ASN1_OUT_OF_DATA" }, + { -(MBEDTLS_ERR_ASN1_UNEXPECTED_TAG), "ASN1_UNEXPECTED_TAG" }, + { -(MBEDTLS_ERR_ASN1_INVALID_LENGTH), "ASN1_INVALID_LENGTH" }, + { -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH), "ASN1_LENGTH_MISMATCH" }, + { -(MBEDTLS_ERR_ASN1_INVALID_DATA), "ASN1_INVALID_DATA" }, + { -(MBEDTLS_ERR_ASN1_ALLOC_FAILED), "ASN1_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL), "ASN1_BUF_TOO_SMALL" }, +#endif /* MBEDTLS_ASN1_PARSE_C */ + +#if defined(MBEDTLS_BASE64_C) + { -(MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL), "BASE64_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_BASE64_INVALID_CHARACTER), "BASE64_INVALID_CHARACTER" }, +#endif /* MBEDTLS_BASE64_C */ + +#if defined(MBEDTLS_BIGNUM_C) + { -(MBEDTLS_ERR_MPI_FILE_IO_ERROR), "MPI_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_MPI_BAD_INPUT_DATA), "MPI_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_MPI_INVALID_CHARACTER), "MPI_INVALID_CHARACTER" }, + { -(MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL), "MPI_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_MPI_NEGATIVE_VALUE), "MPI_NEGATIVE_VALUE" }, + { -(MBEDTLS_ERR_MPI_DIVISION_BY_ZERO), "MPI_DIVISION_BY_ZERO" }, + { -(MBEDTLS_ERR_MPI_NOT_ACCEPTABLE), "MPI_NOT_ACCEPTABLE" }, + { -(MBEDTLS_ERR_MPI_ALLOC_FAILED), "MPI_ALLOC_FAILED" }, +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_BLOWFISH_C) + { -(MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA), "BLOWFISH_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH), "BLOWFISH_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED), "BLOWFISH_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_BLOWFISH_C */ + +#if defined(MBEDTLS_CAMELLIA_C) + { -(MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA), "CAMELLIA_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH), "CAMELLIA_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED), "CAMELLIA_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CCM_C) + { -(MBEDTLS_ERR_CCM_BAD_INPUT), "CCM_BAD_INPUT" }, + { -(MBEDTLS_ERR_CCM_AUTH_FAILED), "CCM_AUTH_FAILED" }, + { -(MBEDTLS_ERR_CCM_HW_ACCEL_FAILED), "CCM_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_CCM_C */ + +#if defined(MBEDTLS_CHACHA20_C) + { -(MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA), "CHACHA20_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE), "CHACHA20_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED), "CHACHA20_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_CHACHA20_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + { -(MBEDTLS_ERR_CHACHAPOLY_BAD_STATE), "CHACHAPOLY_BAD_STATE" }, + { -(MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED), "CHACHAPOLY_AUTH_FAILED" }, +#endif /* MBEDTLS_CHACHAPOLY_C */ + +#if defined(MBEDTLS_CMAC_C) + { -(MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED), "CMAC_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_CMAC_C */ + +#if defined(MBEDTLS_CTR_DRBG_C) + { -(MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED), "CTR_DRBG_ENTROPY_SOURCE_FAILED" }, + { -(MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG), "CTR_DRBG_REQUEST_TOO_BIG" }, + { -(MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG), "CTR_DRBG_INPUT_TOO_BIG" }, + { -(MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR), "CTR_DRBG_FILE_IO_ERROR" }, +#endif /* MBEDTLS_CTR_DRBG_C */ + +#if defined(MBEDTLS_DES_C) + { -(MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH), "DES_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_DES_HW_ACCEL_FAILED), "DES_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_ENTROPY_C) + { -(MBEDTLS_ERR_ENTROPY_SOURCE_FAILED), "ENTROPY_SOURCE_FAILED" }, + { -(MBEDTLS_ERR_ENTROPY_MAX_SOURCES), "ENTROPY_MAX_SOURCES" }, + { -(MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED), "ENTROPY_NO_SOURCES_DEFINED" }, + { -(MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE), "ENTROPY_NO_STRONG_SOURCE" }, + { -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR), "ENTROPY_FILE_IO_ERROR" }, +#endif /* MBEDTLS_ENTROPY_C */ + +#if defined(MBEDTLS_GCM_C) + { -(MBEDTLS_ERR_GCM_AUTH_FAILED), "GCM_AUTH_FAILED" }, + { -(MBEDTLS_ERR_GCM_HW_ACCEL_FAILED), "GCM_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_GCM_BAD_INPUT), "GCM_BAD_INPUT" }, +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_HKDF_C) + { -(MBEDTLS_ERR_HKDF_BAD_INPUT_DATA), "HKDF_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_HKDF_C */ + +#if defined(MBEDTLS_HMAC_DRBG_C) + { -(MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG), "HMAC_DRBG_REQUEST_TOO_BIG" }, + { -(MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG), "HMAC_DRBG_INPUT_TOO_BIG" }, + { -(MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR), "HMAC_DRBG_FILE_IO_ERROR" }, + { -(MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED), "HMAC_DRBG_ENTROPY_SOURCE_FAILED" }, +#endif /* MBEDTLS_HMAC_DRBG_C */ + +#if defined(MBEDTLS_MD2_C) + { -(MBEDTLS_ERR_MD2_HW_ACCEL_FAILED), "MD2_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_MD2_C */ + +#if defined(MBEDTLS_MD4_C) + { -(MBEDTLS_ERR_MD4_HW_ACCEL_FAILED), "MD4_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_MD4_C */ + +#if defined(MBEDTLS_MD5_C) + { -(MBEDTLS_ERR_MD5_HW_ACCEL_FAILED), "MD5_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_MD5_C */ + +#if defined(MBEDTLS_NET_C) + { -(MBEDTLS_ERR_NET_SOCKET_FAILED), "NET_SOCKET_FAILED" }, + { -(MBEDTLS_ERR_NET_CONNECT_FAILED), "NET_CONNECT_FAILED" }, + { -(MBEDTLS_ERR_NET_BIND_FAILED), "NET_BIND_FAILED" }, + { -(MBEDTLS_ERR_NET_LISTEN_FAILED), "NET_LISTEN_FAILED" }, + { -(MBEDTLS_ERR_NET_ACCEPT_FAILED), "NET_ACCEPT_FAILED" }, + { -(MBEDTLS_ERR_NET_RECV_FAILED), "NET_RECV_FAILED" }, + { -(MBEDTLS_ERR_NET_SEND_FAILED), "NET_SEND_FAILED" }, + { -(MBEDTLS_ERR_NET_CONN_RESET), "NET_CONN_RESET" }, + { -(MBEDTLS_ERR_NET_UNKNOWN_HOST), "NET_UNKNOWN_HOST" }, + { -(MBEDTLS_ERR_NET_BUFFER_TOO_SMALL), "NET_BUFFER_TOO_SMALL" }, + { -(MBEDTLS_ERR_NET_INVALID_CONTEXT), "NET_INVALID_CONTEXT" }, + { -(MBEDTLS_ERR_NET_POLL_FAILED), "NET_POLL_FAILED" }, + { -(MBEDTLS_ERR_NET_BAD_INPUT_DATA), "NET_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_NET_C */ + +#if defined(MBEDTLS_OID_C) + { -(MBEDTLS_ERR_OID_NOT_FOUND), "OID_NOT_FOUND" }, + { -(MBEDTLS_ERR_OID_BUF_TOO_SMALL), "OID_BUF_TOO_SMALL" }, +#endif /* MBEDTLS_OID_C */ + +#if defined(MBEDTLS_PADLOCK_C) + { -(MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED), "PADLOCK_DATA_MISALIGNED" }, +#endif /* MBEDTLS_PADLOCK_C */ + +#if defined(MBEDTLS_PLATFORM_C) + { -(MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED), "PLATFORM_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED), "PLATFORM_FEATURE_UNSUPPORTED" }, +#endif /* MBEDTLS_PLATFORM_C */ + +#if defined(MBEDTLS_POLY1305_C) + { -(MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA), "POLY1305_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE), "POLY1305_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED), "POLY1305_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_POLY1305_C */ + +#if defined(MBEDTLS_RIPEMD160_C) + { -(MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED), "RIPEMD160_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_RIPEMD160_C */ + +#if defined(MBEDTLS_SHA1_C) + { -(MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED), "SHA1_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_SHA1_BAD_INPUT_DATA), "SHA1_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_SHA1_C */ + +#if defined(MBEDTLS_SHA256_C) + { -(MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED), "SHA256_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_SHA256_BAD_INPUT_DATA), "SHA256_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_SHA256_C */ + +#if defined(MBEDTLS_SHA512_C) + { -(MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED), "SHA512_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_SHA512_BAD_INPUT_DATA), "SHA512_BAD_INPUT_DATA" }, +#endif /* MBEDTLS_SHA512_C */ + +#if defined(MBEDTLS_THREADING_C) + { -(MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE), "THREADING_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_THREADING_BAD_INPUT_DATA), "THREADING_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_THREADING_MUTEX_ERROR), "THREADING_MUTEX_ERROR" }, +#endif /* MBEDTLS_THREADING_C */ + +#if defined(MBEDTLS_XTEA_C) + { -(MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH), "XTEA_INVALID_INPUT_LENGTH" }, + { -(MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED), "XTEA_HW_ACCEL_FAILED" }, +#endif /* MBEDTLS_XTEA_C */ +// END generated code +}; + +static const char *mbedtls_err_prefix = "MBEDTLS_ERR_"; +#define MBEDTLS_ERR_PREFIX_LEN ( sizeof("MBEDTLS_ERR_")-1 ) + +// copy error text into buffer, ensure null termination, return strlen of result +static size_t mbedtls_err_to_str(int err, const struct ssl_errs tab[], int tab_len, char *buf, size_t buflen) { + if (buflen == 0) return 0; + + // prefix for all error names + strncpy(buf, mbedtls_err_prefix, buflen); + if (buflen <= MBEDTLS_ERR_PREFIX_LEN+1) { + buf[buflen-1] = 0; + return buflen-1; + } + + // append error name from table + for (int i = 0; i < tab_len; i++) { + if (tab[i].errnum == err) { + strncpy(buf+MBEDTLS_ERR_PREFIX_LEN, tab[i].errstr, buflen-MBEDTLS_ERR_PREFIX_LEN); + buf[buflen-1] = 0; + return strlen(buf); + } + } + + mbedtls_snprintf(buf+MBEDTLS_ERR_PREFIX_LEN, buflen-MBEDTLS_ERR_PREFIX_LEN, "UNKNOWN (0x%04X)", + err); + return strlen(buf); +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +void mbedtls_strerror(int ret, char *buf, size_t buflen) { + int use_ret; + + if (buflen == 0) return; + + buf[buflen-1] = 0; + + if (ret < 0) ret = -ret; + + // + // High-level error codes + // + uint8_t got_hl = (ret & 0xFF80) != 0; + if (got_hl) { + use_ret = ret & 0xFF80; + + // special case +#if defined(MBEDTLS_SSL_TLS_C) + if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { + strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen); + buf[buflen-1] = 0; + return; + } +#endif + + size_t len = mbedtls_err_to_str(use_ret, mbedtls_high_level_error_tab, + ARRAY_SIZE(mbedtls_high_level_error_tab), buf, buflen); + + buf += len; + buflen -= len; + if (buflen == 0) return; + } + + // + // Low-level error codes + // + use_ret = ret & ~0xFF80; + + if (use_ret == 0) return; + + // If high level code is present, make a concatenation between both error strings. + if (got_hl) { + if (buflen < 2) return; + *buf++ = '+'; + buflen--; + } + + mbedtls_err_to_str(use_ret, mbedtls_low_level_error_tab, + ARRAY_SIZE(mbedtls_low_level_error_tab), buf, buflen); +} + +#else /* MBEDTLS_ERROR_C */ + +#if defined(MBEDTLS_ERROR_STRERROR_DUMMY) + +/* + * Provide an non-function in case MBEDTLS_ERROR_C is not defined + */ +void mbedtls_strerror( int ret, char *buf, size_t buflen ) +{ + ((void) ret); + + if( buflen > 0 ) + buf[0] = '\0'; +} + +#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */ + +#endif /* MBEDTLS_ERROR_C */ diff --git a/lib/mbedtls_errors/tester.c b/lib/mbedtls_errors/tester.c new file mode 100644 index 0000000000000..6f1c788f50ad8 --- /dev/null +++ b/lib/mbedtls_errors/tester.c @@ -0,0 +1,58 @@ +#include "mbedtls/error.h" +#include +#include + +// test_code checks that the provided code results in the provided error string for any size +// buffer. It calls mbedtls_strerror() to fill a buffer that is from 1 to 100 bytes in length +// and then checks that the buffer contents is OK and that a few guard bytes before and after +// the buffer were not overwritten. +int test_code(int code, char *str) { + char buf[100]; + int ok = 1; + int res; + + // test zero-length buffer + memset(buf, -3, 100); + mbedtls_strerror(code, buf + 4, 0); + for (int i = 0; i < 10; i++) { + if (buf[i] != -3) { + printf("Error: guard overwritten buflen=0 i=%d buf[i]=%d\n", i, buf[i]); + ok = 0; + } + } + + // test + for (size_t buflen = 1; buflen < 90; buflen++) { + memset(buf, -3, 100); + mbedtls_strerror(code, buf + 4, buflen); + for (int i = 0; i < 4; i++) { + if (buf[i] != -3) { + printf("Error: pre-guard overwritten buflen=%d i=%d buf[i]=%d\n", buflen, i, buf[i]); + ok = 0; + } + } + for (int i = 4 + buflen; i < 100; i++) { + if (buf[i] != -3) { + printf("Error: post-guard overwritten buflen=%d i=%d buf[i]=%d\n", buflen, i, buf[i]); + ok = 0; + } + } + char exp[100]; + strncpy(exp, str, buflen); + exp[buflen - 1] = 0; + if (strcmp(buf + 4, exp) != 0) { + printf("Error: expected %s, got %s\n", exp, buf); + ok = 0; + } + } + + printf("Test %x -> %s is %s\n", code, str, ok?"OK":"*** BAD ***"); +} + +int main() { + test_code(0x7200, "MBEDTLS_ERR_SSL_INVALID_RECORD"); + test_code(0x7780, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE"); + test_code(0x0074, "MBEDTLS_ERR_SHA256_BAD_INPUT_DATA"); + test_code(0x6600 | 0x0074, "MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH+MBEDTLS_ERR_SHA256_BAD_INPUT_DATA"); + test_code(103, "MBEDTLS_ERR_UNKNOWN (0x0067)"); +} diff --git a/tools/codeformat.py b/tools/codeformat.py index 653529460c079..81a3cdcf8e5a2 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -38,6 +38,7 @@ "extmod/*.[ch]", "extmod/btstack/*.[ch]", "extmod/nimble/*.[ch]", + "lib/mbedtls_errors/tester.c", "lib/netutils/*.[ch]", "lib/timeutils/*.[ch]", "lib/utils/*.[ch]", From 5264478007c78b7737972404bda18ab39d792e17 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Thu, 2 Jul 2020 12:34:36 -0700 Subject: [PATCH 178/352] extmod/modussl_mbedtls: Integrate shorter error strings. The stm32 and esp32 ports now use shorter error strings for mbedtls errors. Also, MBEDTLS_ERROR_C is enabled on stm32 by default to get these strings. --- extmod/modussl_mbedtls.c | 20 ++++++++++++-------- ports/esp32/Makefile | 6 ++++-- ports/stm32/Makefile | 3 +++ ports/stm32/mbedtls/mbedtls_config.h | 1 + tests/net_inet/test_tls_sites.py | 2 +- tests/net_inet/tls_text_errors.py | 4 ++-- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 94061ddc8b946..1677dc6e1ca70 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -77,17 +77,21 @@ STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, cons #endif STATIC NORETURN void mbedtls_raise_error(int err) { - #if defined(MBEDTLS_ERROR_C) - // Including mbedtls_strerror takes about 16KB on the esp32 due to all the 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. - // "small" negative integer error codes come from underlying stream/sockets, not mbedtls + // _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 100 // mbedtls_strerror truncates if it doesn't fit + #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) { @@ -96,7 +100,7 @@ STATIC NORETURN void mbedtls_raise_error(int err) { // print the error message into the allocated buffer mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); - size_t len = strnlen((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; @@ -108,7 +112,7 @@ STATIC NORETURN void mbedtls_raise_error(int err) { 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); // typ. err is negative + mp_raise_OSError(err); // err is typically a large negative number #endif } diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 0f6a1969a6208..2cbe9f6be57f9 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -362,6 +362,7 @@ EXTMOD_SRC_C += $(addprefix extmod/,\ ) LIB_SRC_C = $(addprefix lib/,\ + mbedtls_errors/mp_mbedtls_errors.c \ mp-readline/readline.c \ netutils/netutils.c \ timeutils/timeutils.c \ @@ -506,11 +507,12 @@ ESPIDF_LWIP_O = $(patsubst %.c,%.o,\ $(wildcard $(ESPCOMP)/lwip/port/esp32/*/*.c) \ ) -ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o,\ +# Mbedtls source files, exclude error.c in favor of lib/mbedtls_errors/mp_mbedtls_errors.c +ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o, $(filter-out %/error.c,\ $(wildcard $(ESPCOMP)/mbedtls/mbedtls/library/*.c) \ $(wildcard $(ESPCOMP)/mbedtls/port/*.c) \ $(wildcard $(ESPCOMP)/mbedtls/port/esp32/*.c) \ - ) + )) ESPIDF_MDNS_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/mdns/*.c)) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 2614d4aa0fcf0..fe8f0b8711f0f 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -472,6 +472,9 @@ endif ifeq ($(MICROPY_SSL_MBEDTLS),1) CFLAGS_MOD += -DMBEDTLS_CONFIG_FILE='"mbedtls/mbedtls_config.h"' SRC_MOD += mbedtls/mbedtls_port.c +# replace mbedtls' error.c by ours +SRC_MOD := $(filter-out %/mbedtls/library/error.c, $(SRC_MOD)) +LIB_SRC_C += lib/mbedtls_errors/mp_mbedtls_errors.c endif ifeq ($(MICROPY_PY_BLUETOOTH),1) diff --git a/ports/stm32/mbedtls/mbedtls_config.h b/ports/stm32/mbedtls/mbedtls_config.h index 338c8b354146b..56fbbf3aaf086 100644 --- a/ports/stm32/mbedtls/mbedtls_config.h +++ b/ports/stm32/mbedtls/mbedtls_config.h @@ -67,6 +67,7 @@ #define MBEDTLS_CTR_DRBG_C //#define MBEDTLS_ECP_C #define MBEDTLS_ENTROPY_C +#define MBEDTLS_ERROR_C #define MBEDTLS_MD_C #define MBEDTLS_MD5_C #define MBEDTLS_OID_C diff --git a/tests/net_inet/test_tls_sites.py b/tests/net_inet/test_tls_sites.py index 876343acfc1d3..d2cb928c8d5b9 100644 --- a/tests/net_inet/test_tls_sites.py +++ b/tests/net_inet/test_tls_sites.py @@ -54,7 +54,7 @@ def main(): test_one(site, opts) print(site, "ok") except Exception as e: - print(site, repr(e)) + print(site, e) main() diff --git a/tests/net_inet/tls_text_errors.py b/tests/net_inet/tls_text_errors.py index 2ba167b868ef8..9e8ccfaf9ec77 100644 --- a/tests/net_inet/tls_text_errors.py +++ b/tests/net_inet/tls_text_errors.py @@ -14,10 +14,10 @@ def test(addr): print("wrap: no exception") except OSError as e: # mbedtls produces "mbedtls -0x7200: SSL - An invalid SSL record was received" - # axtls produces "RECORD_OVERFLOW" + # axtls produces "RECORD_OVERFLOW" but also prints "TLS buffer overflow,..." # CPython produces "[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1108)" ok = ( - "invalid SSL record" in str(e) + "SSL_INVALID_RECORD" in str(e) or "RECORD_OVERFLOW" in str(e) or "wrong version" in str(e) ) From 7dbef5377cd86a4f20a16acd3ad7798c4160aabc Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Fri, 26 Jun 2020 14:55:07 +0100 Subject: [PATCH 179/352] esp32/esp32_rmt: Properly fix looping behaviour of RMT. A previous commit 3a9d948032e27f690e1fb09084c36bd47b1a75a0 can cause lock-ups of the RMT driver, so this commit reverses that, adds a loop_en flag, and explicitly controls the TX interrupt in write_pulses(). This provides correct looping, non-blocking writes and sensible behaviour for wait_done(). See also #6167. --- ports/esp32/esp32_rmt.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 2ed4c9f6968c4..7971ca5d1c5af 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -56,6 +56,7 @@ typedef struct _esp32_rmt_obj_t { uint32_t carrier_freq; mp_uint_t num_items; rmt_item32_t *items; + bool loop_en; } esp32_rmt_obj_t; STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -93,6 +94,7 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz self->clock_div = clock_div; self->carrier_duty_percent = carrier_duty_percent; self->carrier_freq = carrier_freq; + self->loop_en = false; rmt_config_t config; config.rmt_mode = RMT_MODE_TX; @@ -110,8 +112,8 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz config.clk_div = self->clock_div; - check_esp_err(rmt_driver_install(config.channel, 0, 0)); check_esp_err(rmt_config(&config)); + check_esp_err(rmt_driver_install(config.channel, 0, 0)); return MP_OBJ_FROM_PTR(self); } @@ -180,7 +182,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_rmt_wait_done_obj, 1, esp32_rmt_wait_don STATIC mp_obj_t esp32_rmt_loop(mp_obj_t self_in, mp_obj_t loop) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_esp_err(rmt_set_tx_loop_mode(self->channel_id, mp_obj_get_int(loop))); + self->loop_en = mp_obj_get_int(loop); + if (!self->loop_en) { + bool loop_en; + check_esp_err(rmt_get_tx_loop_mode(self->channel_id, &loop_en)); + if (loop_en) { + check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false)); + check_esp_err(rmt_set_tx_intr_en(self->channel_id, true)); + } + } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp32_rmt_loop_obj, esp32_rmt_loop); @@ -222,8 +232,24 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, self->items[item_index].level1 = start++; } } + + if (self->loop_en) { + bool loop_en; + check_esp_err(rmt_get_tx_loop_mode(self->channel_id, &loop_en)); + if (loop_en) { + check_esp_err(rmt_set_tx_intr_en(self->channel_id, true)); + check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false)); + } + check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY)); + check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); + } + check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false /* non-blocking */)); + if (self->loop_en) { + check_esp_err(rmt_set_tx_loop_mode(self->channel_id, true)); + } + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_rmt_write_pulses_obj, 2, esp32_rmt_write_pulses); From 5f0e9d1bace44715e906cd0dbcb30f455b042ec2 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Fri, 3 Jul 2020 14:14:33 +0100 Subject: [PATCH 180/352] docs/library: Update documentation of esp32's RMT. This explains how looping now works, and removes the warning about calling wait_done(). --- docs/library/esp32.rst | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 715afdddeb169..dfde840a2141a 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -209,19 +209,21 @@ For more details see Espressif's `ESP-IDF RMT documentation. .. method:: RMT.wait_done(timeout=0) - Returns True if `RMT.write_pulses` has completed. + Returns ``True`` if the channel is currently transmitting a stream of pulses + started with a call to `RMT.write_pulses`. If *timeout* (defined in ticks of ``source_freq / clock_div``) is specified - the method will wait for *timeout* or until `RMT.write_pulses` is complete, - returning ``False`` if the channel continues to transmit. - -.. Warning:: - Avoid using ``wait_done()`` if looping is enabled. + the method will wait for *timeout* or until transmission is complete, + returning ``False`` if the channel continues to transmit. If looping is + enabled with `RMT.loop` and a stream has started, then this method will + always (wait and) return ``False``. .. method:: RMT.loop(enable_loop) - Configure looping on the channel, allowing a stream of pulses to be - indefinitely repeated. *enable_loop* is bool, set to True to enable looping. + Configure looping on the channel. *enable_loop* is bool, set to ``True`` to + enable looping on the *next* call to `RMT.write_pulses`. If called with + ``False`` while a looping stream is currently being transmitted then the + current set of pulses will be completed before transmission stops. .. method:: RMT.write_pulses(pulses, start) @@ -230,6 +232,15 @@ For more details see Espressif's `ESP-IDF RMT documentation. resolution ``(1 / (source_freq / clock_div))``. *start* defines whether the stream starts at 0 or 1. + If transmission of a stream is currently in progress then this method will + block until transmission of that stream has ended before beginning sending + *pulses*. + + If looping is enabled with `RMT.loop`, the stream of pulses will be repeated + indefinitely. Further calls to `RMT.write_pulses` will end the previous + stream - blocking until the last set of pulses has been transmitted - + before starting the next stream. + Ultra-Low-Power co-processor ---------------------------- From 76fefad18bb3f29d747d6f20adc722f7c8e44f84 Mon Sep 17 00:00:00 2001 From: Kenneth Ryerson Date: Mon, 20 Apr 2020 09:51:11 -0500 Subject: [PATCH 181/352] esp32/network_lan: Add support for IP101 PHY. Signed-off-by: Kenneth Ryerson --- ports/esp32/modnetwork.c | 1 + ports/esp32/modnetwork.h | 2 +- ports/esp32/network_lan.c | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 975029121ed0b..325b27f74b262 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -776,6 +776,7 @@ STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PHY_LAN8720), MP_ROM_INT(PHY_LAN8720) }, { MP_ROM_QSTR(MP_QSTR_PHY_TLK110), MP_ROM_INT(PHY_TLK110) }, + { MP_ROM_QSTR(MP_QSTR_PHY_IP101), MP_ROM_INT(PHY_IP101) }, // ETH Clock modes from ESP-IDF #if !MICROPY_ESP_IDF_4 diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index 64d2da018c056..db1b3d1300932 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_ESP32_MODNETWORK_H #define MICROPY_INCLUDED_ESP32_MODNETWORK_H -enum { PHY_LAN8720, PHY_TLK110 }; +enum { PHY_LAN8720, PHY_TLK110, PHY_IP101 }; MP_DECLARE_CONST_FUN_OBJ_KW(get_lan_obj); MP_DECLARE_CONST_FUN_OBJ_1(ppp_make_new_obj); diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 1f9a733a7aef1..7a4ad49e01ceb 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -33,6 +33,7 @@ #include "eth_phy/phy.h" #include "eth_phy/phy_tlk110.h" #include "eth_phy/phy_lan8720.h" +#include "eth_phy/phy_ip101.h" #include "tcpip_adapter.h" #include "modnetwork.h" @@ -123,7 +124,9 @@ STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar mp_raise_ValueError(MP_ERROR_TEXT("invalid phy address")); } - if (args[ARG_phy_type].u_int != PHY_LAN8720 && args[ARG_phy_type].u_int != PHY_TLK110) { + if (args[ARG_phy_type].u_int != PHY_LAN8720 && + args[ARG_phy_type].u_int != PHY_TLK110 && + args[ARG_phy_type].u_int != PHY_IP101) { mp_raise_ValueError(MP_ERROR_TEXT("invalid phy type")); } @@ -145,6 +148,9 @@ STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar case PHY_LAN8720: config = phy_lan8720_default_ethernet_config; break; + case PHY_IP101: + config = phy_ip101_default_ethernet_config; + break; } self->link_func = config.phy_check_link; From f80b1d853599787597abe6ead9e04ef8e775818b Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 19 Jul 2020 14:45:47 +1000 Subject: [PATCH 182/352] lib/stm32lib: Update library for H7 v1.6.0 and WB v1.6.0. Changes in this new library version are: - Update H7 HAL to v1.6.0. - Update WB HAL to v1.6.0. - Add patches to fix F4 ll_uart clock selection for UART9/UART10. Signed-off-by: Damien George --- lib/stm32lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stm32lib b/lib/stm32lib index 668d7a9e54aea..58fee7c92bd57 160000 --- a/lib/stm32lib +++ b/lib/stm32lib @@ -1 +1 @@ -Subproject commit 668d7a9e54aea98f8fe8a858eac1d3daa80fa824 +Subproject commit 58fee7c92bd576814d3f2afd92fbc62990270ecc From 895b1dbdda132b115bb0b5102962f4f46adb7adb Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Jul 2020 23:20:30 +1000 Subject: [PATCH 183/352] tests/basics: Split out memoryview slice-assign tests to separate file. Signed-off-by: Damien George --- tests/basics/memoryview1.py | 48 ------------------- tests/basics/memoryview_slice_assign.py | 63 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 48 deletions(-) create mode 100644 tests/basics/memoryview_slice_assign.py diff --git a/tests/basics/memoryview1.py b/tests/basics/memoryview1.py index 1bfeabdfdc824..4c20c91f4947f 100644 --- a/tests/basics/memoryview1.py +++ b/tests/basics/memoryview1.py @@ -54,54 +54,6 @@ m[2] = 6 print(a) -# test slice assignment between memoryviews -b1 = bytearray(b'1234') -b2 = bytearray(b'5678') -b3 = bytearray(b'5678') -m1 = memoryview(b1) -m2 = memoryview(b2) -m3 = memoryview(b3) -m2[1:3] = m1[0:2] -print(b2) -b3[1:3] = m1[0:2] -print(b3) -m1[2:4] = b3[1:3] -print(b1) - -try: - m2[1:3] = b1[0:4] -except ValueError: - print("ValueError") - -try: - m2[1:3] = m1[0:4] -except ValueError: - print("ValueError") - -try: - m2[0:4] = m1[1:3] -except ValueError: - print("ValueError") - -# test memoryview of arrays with items sized larger than 1 -a1 = array.array('i', [0]*5) -m4 = memoryview(a1) -a2 = array.array('i', [3]*5) -m5 = memoryview(a2) -m4[1:3] = m5[1:3] -print(a1) - -try: - m4[1:3] = m2[1:3] -except ValueError: - print("ValueError") - -# invalid assignment on RHS -try: - memoryview(array.array('i'))[0:2] = b'1234' -except ValueError: - print('ValueError') - # invalid attribute try: memoryview(b'a').noexist diff --git a/tests/basics/memoryview_slice_assign.py b/tests/basics/memoryview_slice_assign.py new file mode 100644 index 0000000000000..b730dceba6e5d --- /dev/null +++ b/tests/basics/memoryview_slice_assign.py @@ -0,0 +1,63 @@ +# test slice assignment to memoryview + +try: + memoryview(bytearray(1))[:] = memoryview(bytearray(1)) +except (NameError, TypeError): + print("SKIP") + raise SystemExit + +try: + import uarray as array +except ImportError: + try: + import array + except ImportError: + print("SKIP") + raise SystemExit + +# test slice assignment between memoryviews +b1 = bytearray(b'1234') +b2 = bytearray(b'5678') +b3 = bytearray(b'5678') +m1 = memoryview(b1) +m2 = memoryview(b2) +m3 = memoryview(b3) +m2[1:3] = m1[0:2] +print(b2) +b3[1:3] = m1[0:2] +print(b3) +m1[2:4] = b3[1:3] +print(b1) + +# invalid slice assignments +try: + m2[1:3] = b1[0:4] +except ValueError: + print("ValueError") +try: + m2[1:3] = m1[0:4] +except ValueError: + print("ValueError") +try: + m2[0:4] = m1[1:3] +except ValueError: + print("ValueError") + +# test memoryview of arrays with items sized larger than 1 +a1 = array.array('i', [0]*5) +m4 = memoryview(a1) +a2 = array.array('i', [3]*5) +m5 = memoryview(a2) +m4[1:3] = m5[1:3] +print(a1) + +try: + m4[1:3] = m2[1:3] +except ValueError: + print("ValueError") + +# invalid assignment on RHS +try: + memoryview(array.array('i'))[0:2] = b'1234' +except ValueError: + print('ValueError') From a853fff838c1f995b9eeb0ebef5f117cfa3d5f0a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Jul 2020 23:23:42 +1000 Subject: [PATCH 184/352] py/obj.h: Fix mp_seq_replace_slice_no_grow to use memmove not memcpy. Because the argument arrays may overlap, as show by the new tests in this commit. Also remove the debugging comments for these macros, add a new comment about overlapping regions, and separate the macros by blank lines to make them easier to read. Fixes issue #6244. Signed-off-by: Damien George --- py/obj.h | 8 ++++---- tests/basics/memoryview_slice_assign.py | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/py/obj.h b/py/obj.h index f621b1dadd492..31542a7f9bd7c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -992,17 +992,17 @@ bool mp_seq_cmp_objs(mp_uint_t op, const mp_obj_t *items1, size_t len1, const mp mp_obj_t mp_seq_index_obj(const mp_obj_t *items, size_t len, size_t n_args, const mp_obj_t *args); mp_obj_t mp_seq_count_obj(const mp_obj_t *items, size_t len, mp_obj_t value); mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes); + // Helper to clear stale pointers from allocated, but unused memory, to preclude GC problems #define mp_seq_clear(start, len, alloc_len, item_sz) memset((byte *)(start) + (len) * (item_sz), 0, ((alloc_len) - (len)) * (item_sz)) + +// Note: dest and slice regions may overlap #define mp_seq_replace_slice_no_grow(dest, dest_len, beg, end, slice, slice_len, item_sz) \ - /*printf("memcpy(%p, %p, %d)\n", dest + beg, slice, slice_len * (item_sz));*/ \ - memcpy(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); \ - /*printf("memmove(%p, %p, %d)\n", dest + (beg + slice_len), dest + end, (dest_len - end) * (item_sz));*/ \ + memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); \ memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), (dest_len - end) * (item_sz)); // Note: dest and slice regions may overlap #define mp_seq_replace_slice_grow_inplace(dest, dest_len, beg, end, slice, slice_len, len_adj, item_sz) \ - /*printf("memmove(%p, %p, %d)\n", dest + beg + len_adj, dest + beg, (dest_len - beg) * (item_sz));*/ \ memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), ((dest_len) + (len_adj) - ((beg) + (slice_len))) * (item_sz)); \ memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); diff --git a/tests/basics/memoryview_slice_assign.py b/tests/basics/memoryview_slice_assign.py index b730dceba6e5d..74f6fae6f788d 100644 --- a/tests/basics/memoryview_slice_assign.py +++ b/tests/basics/memoryview_slice_assign.py @@ -61,3 +61,27 @@ memoryview(array.array('i'))[0:2] = b'1234' except ValueError: print('ValueError') + +# test shift left of bytearray +b = bytearray(range(10)) +mv = memoryview(b) +mv[1:] = mv[:-1] +print(b) + +# test shift right of bytearray +b = bytearray(range(10)) +mv = memoryview(b) +mv[:-1] = mv[1:] +print(b) + +# test shift left of array +a = array.array('I', range(10)) +mv = memoryview(a) +mv[1:] = mv[:-1] +print(a) + +# test shift right of array +a = array.array('I', range(10)) +mv = memoryview(a) +mv[:-1] = mv[1:] +print(a) From 27767aafa21754e6cdbf52a719e7ce395f0fe672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Sat, 15 Feb 2020 19:28:56 +0100 Subject: [PATCH 185/352] lib/libm_dbl: Add round.c source code. This code is imported from musl, to match existing code in libm_dbl. The file is also added to the build in stm32/Makefile. It's not needed by the core code but, similar to c5cc64175be32cb1e4f3f1a249667bc9f5a12613, allows round() to be used by user C modules or board extensions. --- lib/libm_dbl/round.c | 35 +++++++++++++++++++++++++++++++++++ ports/stm32/Makefile | 1 + 2 files changed, 36 insertions(+) create mode 100644 lib/libm_dbl/round.c diff --git a/lib/libm_dbl/round.c b/lib/libm_dbl/round.c new file mode 100644 index 0000000000000..130d58d2571e7 --- /dev/null +++ b/lib/libm_dbl/round.c @@ -0,0 +1,35 @@ +#include "libm.h" + +#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 +#define EPS DBL_EPSILON +#elif FLT_EVAL_METHOD==2 +#define EPS LDBL_EPSILON +#endif +static const double_t toint = 1/EPS; + +double round(double x) +{ + union {double f; uint64_t i;} u = {x}; + int e = u.i >> 52 & 0x7ff; + double_t y; + + if (e >= 0x3ff+52) + return x; + if (u.i >> 63) + x = -x; + if (e < 0x3ff-1) { + /* raise inexact if x!=0 */ + FORCE_EVAL(x + toint); + return 0*u.f; + } + y = x + toint - toint - x; + if (y > 0.5) + y = y + x - 1; + else if (y <= -0.5) + y = y + x + 1; + else + y = y + x; + if (u.i >> 63) + y = -y; + return y; +} diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index fe8f0b8711f0f..e7d2e2abc2caf 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -191,6 +191,7 @@ LIBM_SRC_C = $(addprefix lib/libm_dbl/,\ nearbyint.c \ pow.c \ rint.c \ + round.c \ scalbn.c \ sin.c \ sinh.c \ From d9b726102429f555aec12275bb740f315ae9a795 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Jul 2020 16:28:46 +1000 Subject: [PATCH 186/352] lib/libc: Fix string0's implementation of strncpy. Fixing 98e583430fb7b793119db27bad9f98119e81579f, the semantics of strncpy require that the remainder of dst be filled with null bytes. Signed-off-by: Damien George --- lib/libc/string0.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/libc/string0.c b/lib/libc/string0.c index 5f40a71e3e284..19ad14d0f7a77 100644 --- a/lib/libc/string0.c +++ b/lib/libc/string0.c @@ -178,8 +178,10 @@ char *strncpy(char *s1, const char *s2, size_t n) { while (n > 0) { n--; if ((*dst++ = *src++) == '\0') { - /* If we get here, we found a null character at the end of s2 */ - *dst = '\0'; + /* If we get here, we found a null character at the end + of s2, so use memset to put null bytes at the end of + s1. */ + memset(dst, '\0', n); break; } } From 0a79e183984fabb3d33b7879a2d0dc79948dd0de Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sun, 19 Jul 2020 12:42:09 +0200 Subject: [PATCH 187/352] nrf: Split mpconfigport.h into multiple files. Splitting mpconfigport.h into multiple device specific files in order to facilitate variations between devices. Due to the fact that the devices might have variations in features and also variations in flash size it makes sense that some devices offers more functionality than others without being limited by restricted devices. For example more micropython features can be activated for nrf52840 with 1MB flash, compared to nrf51 with 256KB. --- ports/nrf/mpconfigdevice_nrf51822.h | 37 +++++++++++++++++++++++++++++ ports/nrf/mpconfigdevice_nrf52832.h | 37 +++++++++++++++++++++++++++++ ports/nrf/mpconfigdevice_nrf52840.h | 37 +++++++++++++++++++++++++++++ ports/nrf/mpconfigdevice_nrf9160.h | 37 +++++++++++++++++++++++++++++ ports/nrf/mpconfigport.h | 12 ++++++++++ 5 files changed, 160 insertions(+) create mode 100644 ports/nrf/mpconfigdevice_nrf51822.h create mode 100644 ports/nrf/mpconfigdevice_nrf52832.h create mode 100644 ports/nrf/mpconfigdevice_nrf52840.h create mode 100644 ports/nrf/mpconfigdevice_nrf9160.h diff --git a/ports/nrf/mpconfigdevice_nrf51822.h b/ports/nrf/mpconfigdevice_nrf51822.h new file mode 100644 index 0000000000000..55d540be98b90 --- /dev/null +++ b/ports/nrf/mpconfigdevice_nrf51822.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Glenn Ruben Bakke + * + * 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. + */ + +// Board overridable build configuration. + +#ifndef MICROPY_MBFS +#define MICROPY_MBFS (1) +#endif + +#ifndef MICROPY_VFS +#define MICROPY_VFS (0) +#endif + +// Board overridable feature configuration. diff --git a/ports/nrf/mpconfigdevice_nrf52832.h b/ports/nrf/mpconfigdevice_nrf52832.h new file mode 100644 index 0000000000000..55d540be98b90 --- /dev/null +++ b/ports/nrf/mpconfigdevice_nrf52832.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Glenn Ruben Bakke + * + * 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. + */ + +// Board overridable build configuration. + +#ifndef MICROPY_MBFS +#define MICROPY_MBFS (1) +#endif + +#ifndef MICROPY_VFS +#define MICROPY_VFS (0) +#endif + +// Board overridable feature configuration. diff --git a/ports/nrf/mpconfigdevice_nrf52840.h b/ports/nrf/mpconfigdevice_nrf52840.h new file mode 100644 index 0000000000000..55d540be98b90 --- /dev/null +++ b/ports/nrf/mpconfigdevice_nrf52840.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Glenn Ruben Bakke + * + * 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. + */ + +// Board overridable build configuration. + +#ifndef MICROPY_MBFS +#define MICROPY_MBFS (1) +#endif + +#ifndef MICROPY_VFS +#define MICROPY_VFS (0) +#endif + +// Board overridable feature configuration. diff --git a/ports/nrf/mpconfigdevice_nrf9160.h b/ports/nrf/mpconfigdevice_nrf9160.h new file mode 100644 index 0000000000000..55d540be98b90 --- /dev/null +++ b/ports/nrf/mpconfigdevice_nrf9160.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Glenn Ruben Bakke + * + * 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. + */ + +// Board overridable build configuration. + +#ifndef MICROPY_MBFS +#define MICROPY_MBFS (1) +#endif + +#ifndef MICROPY_VFS +#define MICROPY_VFS (0) +#endif + +// Board overridable feature configuration. diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index c5304b20d0d3e..e84709c49dda8 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -29,6 +29,18 @@ #include +#if defined(NRF51822) + #include "mpconfigdevice_nrf51822.h" +#elif defined(NRF52832) + #include "mpconfigdevice_nrf52832.h" +#elif defined(NRF52840) + #include "mpconfigdevice_nrf52840.h" +#elif defined(NRF9160) + #include "mpconfigdevice_nrf9160.h" +#else + #pragma error "Device not defined" +#endif + // options to control how MicroPython is built #ifndef MICROPY_VFS #define MICROPY_VFS (0) From caaaa2b1f4645b72929a957cf5ceed182e619f4c Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sun, 19 Jul 2020 12:49:30 +0200 Subject: [PATCH 188/352] nrf: Enable more features for all targets. Enabling the following features for all targets, except for nrf51 targets compiled to be used with SoftDevice: - MICROPY_PY_ARRAY_SLICE_ASSIGN - MICROPY_PY_SYS_STDFILES - MICROPY_PY_UBINASCII --- ports/nrf/mpconfigdevice_nrf51822.h | 24 ++++++++++++++++++++++++ ports/nrf/mpconfigdevice_nrf52832.h | 12 ++++++++++++ ports/nrf/mpconfigdevice_nrf52840.h | 12 ++++++++++++ ports/nrf/mpconfigdevice_nrf9160.h | 12 ++++++++++++ ports/nrf/mpconfigport.h | 3 --- 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/ports/nrf/mpconfigdevice_nrf51822.h b/ports/nrf/mpconfigdevice_nrf51822.h index 55d540be98b90..2f85c9f4c5468 100644 --- a/ports/nrf/mpconfigdevice_nrf51822.h +++ b/ports/nrf/mpconfigdevice_nrf51822.h @@ -35,3 +35,27 @@ #endif // Board overridable feature configuration. + +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#if defined(BLUETOOTH_SD) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) +#else +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#endif +#endif + +#ifndef MICROPY_PY_SYS_STDFILES +#if defined(BLUETOOTH_SD) +#define MICROPY_PY_SYS_STDFILES (0) +#else +#define MICROPY_PY_SYS_STDFILES (1) +#endif +#endif + +#ifndef MICROPY_PY_UBINASCII +#if defined(BLUETOOTH_SD) +#define MICROPY_PY_UBINASCII (0) +#else +#define MICROPY_PY_UBINASCII (1) +#endif +#endif diff --git a/ports/nrf/mpconfigdevice_nrf52832.h b/ports/nrf/mpconfigdevice_nrf52832.h index 55d540be98b90..2bfd047ca1456 100644 --- a/ports/nrf/mpconfigdevice_nrf52832.h +++ b/ports/nrf/mpconfigdevice_nrf52832.h @@ -35,3 +35,15 @@ #endif // Board overridable feature configuration. + +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#endif + +#ifndef MICROPY_PY_SYS_STDFILES +#define MICROPY_PY_SYS_STDFILES (1) +#endif + +#ifndef MICROPY_PY_UBINASCII +#define MICROPY_PY_UBINASCII (1) +#endif diff --git a/ports/nrf/mpconfigdevice_nrf52840.h b/ports/nrf/mpconfigdevice_nrf52840.h index 55d540be98b90..2bfd047ca1456 100644 --- a/ports/nrf/mpconfigdevice_nrf52840.h +++ b/ports/nrf/mpconfigdevice_nrf52840.h @@ -35,3 +35,15 @@ #endif // Board overridable feature configuration. + +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#endif + +#ifndef MICROPY_PY_SYS_STDFILES +#define MICROPY_PY_SYS_STDFILES (1) +#endif + +#ifndef MICROPY_PY_UBINASCII +#define MICROPY_PY_UBINASCII (1) +#endif diff --git a/ports/nrf/mpconfigdevice_nrf9160.h b/ports/nrf/mpconfigdevice_nrf9160.h index 55d540be98b90..2bfd047ca1456 100644 --- a/ports/nrf/mpconfigdevice_nrf9160.h +++ b/ports/nrf/mpconfigdevice_nrf9160.h @@ -35,3 +35,15 @@ #endif // Board overridable feature configuration. + +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#endif + +#ifndef MICROPY_PY_SYS_STDFILES +#define MICROPY_PY_SYS_STDFILES (1) +#endif + +#ifndef MICROPY_PY_UBINASCII +#define MICROPY_PY_UBINASCII (1) +#endif diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index e84709c49dda8..1197df016cce9 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -117,11 +117,9 @@ #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (0) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) #define MICROPY_PY_BUILTINS_SLICE_ATTRS (0) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_MAXSIZE (1) -#define MICROPY_PY_SYS_STDFILES (0) #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (0) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0) @@ -129,7 +127,6 @@ #define MICROPY_PY_IO (0) #define MICROPY_PY_IO_FILEIO (0) #define MICROPY_PY_UERRNO (0) -#define MICROPY_PY_UBINASCII (0) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_UCTYPES (0) From cf9be201d780eedcdb8a6c05f5ea7c796f892f07 Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Thu, 2 Jul 2020 11:08:25 +1000 Subject: [PATCH 189/352] docs/library: Update pyb.SPI init method to add descr about "ti" arg. --- docs/library/pyb.SPI.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/library/pyb.SPI.rst b/docs/library/pyb.SPI.rst index a1910be49b7fb..181bdd3b6ec5d 100644 --- a/docs/library/pyb.SPI.rst +++ b/docs/library/pyb.SPI.rst @@ -64,6 +64,7 @@ Methods respectively. - ``bits`` can be 8 or 16, and is the number of bits in each transferred word. - ``firstbit`` can be ``SPI.MSB`` or ``SPI.LSB``. + - ``ti`` True indicates Texas Instruments, as opposed to Motorola, signal conventions. - ``crc`` can be None for no CRC, or a polynomial specifier. Note that the SPI clock frequency will not always be the requested baudrate. From 4a7c2731c5190d3bb7781f91a24a273d52acb289 Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Thu, 2 Jul 2020 12:34:45 +1000 Subject: [PATCH 190/352] docs/library: Update pyb.Timer to add missing args and defaults to init. --- docs/library/pyb.Timer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/pyb.Timer.rst b/docs/library/pyb.Timer.rst index bb7e7e52d5648..532b64da8e93c 100644 --- a/docs/library/pyb.Timer.rst +++ b/docs/library/pyb.Timer.rst @@ -62,7 +62,7 @@ Constructors Methods ------- -.. method:: Timer.init(\*, freq, prescaler, period) +.. method:: Timer.init(\*, freq, prescaler, period, mode=Timer.UP, div=1, callback=None, deadtime=0) Initialise the timer. Initialisation must be either by frequency (in Hz) or by prescaler and period:: From 47289f4bc94c02acd49144ba15d91e58342807ee Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Fri, 3 Jul 2020 15:17:50 +1000 Subject: [PATCH 191/352] docs/library: Update pyb.UART to correct pyboard UART availability. On original pyboard UART 5 isn't available; added pyboard D availability. --- docs/library/pyb.UART.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/library/pyb.UART.rst b/docs/library/pyb.UART.rst index ab7ab2fb882a4..0a611a72566da 100644 --- a/docs/library/pyb.UART.rst +++ b/docs/library/pyb.UART.rst @@ -48,13 +48,16 @@ Constructors .. class:: pyb.UART(bus, ...) - Construct a UART object on the given bus. ``bus`` can be 1-6, or 'XA', 'XB', 'YA', or 'YB'. + Construct a UART object on the given bus. + For Pyboard ``bus`` can be 1-4, 6, 'XA', 'XB', 'YA', or 'YB'. + For Pyboard Lite ``bus`` can be 1, 2, 6, 'XB', or 'YA'. + For Pyboard D ``bus`` can be 1-4, 'XA', 'YA' or 'YB'. With no additional parameters, the UART object is created but not initialised (it has the settings from the last initialisation of the bus, if any). If extra arguments are given, the bus is initialised. See ``init`` for parameters of initialisation. - The physical pins of the UART busses are: + The physical pins of the UART busses on Pyboard are: - ``UART(4)`` is on ``XA``: ``(TX, RX) = (X1, X2) = (PA0, PA1)`` - ``UART(1)`` is on ``XB``: ``(TX, RX) = (X9, X10) = (PB6, PB7)`` @@ -62,10 +65,22 @@ Constructors - ``UART(3)`` is on ``YB``: ``(TX, RX) = (Y9, Y10) = (PB10, PB11)`` - ``UART(2)`` is on: ``(TX, RX) = (X3, X4) = (PA2, PA3)`` - The Pyboard Lite supports UART(1), UART(2) and UART(6) only. Pins are as above except: + The Pyboard Lite supports UART(1), UART(2) and UART(6) only, pins are: + - ``UART(1)`` is on ``XB``: ``(TX, RX) = (X9, X10) = (PB6, PB7)`` + - ``UART(6)`` is on ``YA``: ``(TX, RX) = (Y1, Y2) = (PC6, PC7)`` - ``UART(2)`` is on: ``(TX, RX) = (X1, X2) = (PA2, PA3)`` + The Pyboard D supports UART(1), UART(2), UART(3) and UART(4) only, pins are: + + - ``UART(4)`` is on ``XA``: ``(TX, RX) = (X1, X2) = (PA0, PA1)`` + - ``UART(1)`` is on ``YA``: ``(TX, RX) = (Y1, Y2) = (PA9, PA10)`` + - ``UART(3)`` is on ``YB``: ``(TX, RX) = (Y9, Y10) = (PB10, PB11)`` + - ``UART(2)`` is on: ``(TX, RX) = (X3, X4) = (PA2, PA3)`` + + *Note:* Pyboard D has ``UART(1)`` on ``YA``, unlike Pyboard and Pyboard Lite that both + have ``UART(1)`` on ``XB`` and ``UART(6)`` on ``YA``. + Methods ------- From fe7d47971f7de0cc094b28ae34e49f40b41d456d Mon Sep 17 00:00:00 2001 From: Josh Lloyd Date: Mon, 20 Jul 2020 11:06:12 +1200 Subject: [PATCH 192/352] docs/esp32: Fix machine.Timer quickref to specify HW timers. Also remove trailing spaces on other lines. --- docs/esp32/quickref.rst | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index b4f443a910009..d5c222f3a1eef 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -118,17 +118,21 @@ Use the :mod:`time ` module:: Timers ------ -Virtual (RTOS-based) timers are supported. Use the :ref:`machine.Timer ` class -with timer ID of -1:: +The ESP32 port has four hardware timers. Use the :ref:`machine.Timer ` class +with a timer ID from 0 to 3 (inclusive):: from machine import Timer - tim = Timer(-1) - tim.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1)) - tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2)) + tim0 = Timer(0) + tim0.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(0)) + + tim1 = Timer(1) + tim1.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(1)) The period is in milliseconds. +Virtual timers are not currently supported on this port. + .. _Pins_and_GPIO: Pins and GPIO @@ -172,7 +176,7 @@ PWM (pulse width modulation) PWM can be enabled on all output-enabled pins. The base frequency can range from 1Hz to 40MHz but there is a tradeoff; as the base frequency -*increases* the duty resolution *decreases*. See +*increases* the duty resolution *decreases*. See `LED Control `_ for more details. Currently the duty cycle has to be in the range of 0-1023. @@ -273,7 +277,7 @@ class:: .. Warning:: Currently *all* of ``sck``, ``mosi`` and ``miso`` *must* be specified when - initialising Software SPI. + initialising Software SPI. Hardware SPI bus ---------------- @@ -445,11 +449,11 @@ Use the ``TouchPad`` class in the ``machine`` module:: from machine import TouchPad, Pin t = TouchPad(Pin(14)) - t.read() # Returns a smaller number when touched + t.read() # Returns a smaller number when touched ``TouchPad.read`` returns a value relative to the capacitive variation. Small numbers (typically in -the *tens*) are common when a pin is touched, larger numbers (above *one thousand*) when -no touch is present. However the values are *relative* and can vary depending on the board +the *tens*) are common when a pin is touched, larger numbers (above *one thousand*) when +no touch is present. However the values are *relative* and can vary depending on the board and surrounding composition so some calibration may be required. There are ten capacitive touch-enabled pins that can be used on the ESP32: 0, 2, 4, 12, 13 From 37e1b5c891f9964bb6c95228bc2d718511507a69 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Tue, 21 Jul 2020 18:47:28 +0100 Subject: [PATCH 193/352] py/compile: Don't await __aiter__ special method in async-for. MicroPython's original implementation of __aiter__ was correct for an earlier (provisional) version of PEP492 (CPython 3.5), where __aiter__ was an async-def function. But that changed in the final version of PEP492 (in CPython 3.5.2) where the function was changed to a normal one. See https://www.python.org/dev/peps/pep-0492/#why-aiter-does-not-return-an-awaitable See also the note at the end of this subsection in the docs: https://docs.python.org/3.5/reference/datamodel.html#asynchronous-iterators And for completeness the BPO: https://bugs.python.org/issue27243 To be consistent with the Python spec as it stands today (and now that PEP492 is final) this commit changes MicroPython's behaviour to match CPython: __aiter__ should return an async-iterable object, but is not itself awaitable. The relevant tests are updated to match. See #6267. --- py/compile.c | 3 ++- tests/basics/async_for.py | 2 +- tests/basics/async_for2.py | 5 ++--- tests/basics/async_for2.py.exp | 4 ---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/py/compile.c b/py/compile.c index 53108b7062b78..d1a4d65c8bb51 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1798,7 +1798,8 @@ STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns uint try_finally_label = comp_next_label(comp); compile_node(comp, pns->nodes[1]); // iterator - compile_await_object_method(comp, MP_QSTR___aiter__); + EMIT_ARG(load_method, MP_QSTR___aiter__, false); + EMIT_ARG(call_method, 0, 0, 0); compile_store_id(comp, context); START_BREAK_CONTINUE_BLOCK diff --git a/tests/basics/async_for.py b/tests/basics/async_for.py index 6b4e136d593d4..5fd0540828afe 100644 --- a/tests/basics/async_for.py +++ b/tests/basics/async_for.py @@ -6,7 +6,7 @@ def __init__(self, obj): print('init') self._it = iter(obj) - async def __aiter__(self): + def __aiter__(self): print('aiter') return self diff --git a/tests/basics/async_for2.py b/tests/basics/async_for2.py index 89584fcb100f3..aad23a3e5ad8c 100644 --- a/tests/basics/async_for2.py +++ b/tests/basics/async_for2.py @@ -1,4 +1,4 @@ -# test waiting within "async for" aiter/anext functions +# test waiting within "async for" __anext__ function import sys if sys.implementation.name == 'micropython': @@ -21,9 +21,8 @@ def __init__(self, high): self.cur = 0 self.high = high - async def __aiter__(self): + def __aiter__(self): print('aiter') - print('f returned:', await f(10)) return self async def __anext__(self): diff --git a/tests/basics/async_for2.py.exp b/tests/basics/async_for2.py.exp index 886232f7bab73..52bbe90c85376 100644 --- a/tests/basics/async_for2.py.exp +++ b/tests/basics/async_for2.py.exp @@ -1,9 +1,5 @@ init aiter -f start: 10 -coro yielded: 11 -coro yielded: 12 -f returned: 13 anext f start: 20 coro yielded: 21 From fd2ff867a08fd174f0bbf7a03aa59547634e91ac Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Jul 2020 23:45:08 +1000 Subject: [PATCH 194/352] stm32/usbdev: Fix calculation of SCSI LUN size with multiple LUNs. The SCSI driver calls GetCapacity to get the block size and number of blocks of the underlying block-device/LUN. It caches these values and uses them later on to verify that reads/writes are within the bounds of the LUN. But, prior to this commit, there was only one set of cached values for all LUNs, so the bounds checking for a LUN could use incorrect values, values from one of the other LUNs that most recently updated the cached values. This would lead to failed SCSI requests. This commit fixes this issue by having separate cached values for each LUN. Signed-off-by: Damien George --- ports/stm32/usbd_msc_interface.h | 2 -- .../stm32/usbdev/class/inc/usbd_cdc_msc_hid.h | 7 ++-- ports/stm32/usbdev/class/src/usbd_msc_scsi.c | 34 ++++++++++--------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/ports/stm32/usbd_msc_interface.h b/ports/stm32/usbd_msc_interface.h index 411c707cab7df..9d25a72a3a4ba 100644 --- a/ports/stm32/usbd_msc_interface.h +++ b/ports/stm32/usbd_msc_interface.h @@ -26,8 +26,6 @@ #ifndef MICROPY_INCLUDED_STM32_USBD_MSC_INTERFACE_H #define MICROPY_INCLUDED_STM32_USBD_MSC_INTERFACE_H -#define USBD_MSC_MAX_LUN (2) - extern const USBD_StorageTypeDef usbd_msc_fops; void usbd_msc_init_lu(size_t lu_n, const void *lu_data); diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h index d934f4676c4df..d6c38bbd07e34 100644 --- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h +++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h @@ -34,6 +34,9 @@ #define MSC_MEDIA_PACKET (2048) // was 8192; how low can it go whilst still working? #define HID_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size +// Maximum number of LUN that can be exposed on the MSC interface +#define USBD_MSC_MAX_LUN (2) + // Need to define here for BOT and SCSI layers #define MSC_IN_EP (0x81) #define MSC_OUT_EP (0x01) @@ -78,8 +81,8 @@ typedef struct { uint8_t scsi_sense_head; uint8_t scsi_sense_tail; - uint16_t scsi_blk_size; - uint32_t scsi_blk_nbr; + uint16_t scsi_blk_size[USBD_MSC_MAX_LUN]; + uint32_t scsi_blk_nbr[USBD_MSC_MAX_LUN]; uint32_t scsi_blk_addr_in_blks; uint32_t scsi_blk_len; diff --git a/ports/stm32/usbdev/class/src/usbd_msc_scsi.c b/ports/stm32/usbdev/class/src/usbd_msc_scsi.c index d0413b758a5d6..2eb716ccde438 100644 --- a/ports/stm32/usbdev/class/src/usbd_msc_scsi.c +++ b/ports/stm32/usbdev/class/src/usbd_msc_scsi.c @@ -247,7 +247,7 @@ static int8_t SCSI_ReadCapacity10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_ { USBD_MSC_BOT_HandleTypeDef *hmsc = &((usbd_cdc_msc_hid_state_t*)pdev->pClassData)->MSC_BOT_ClassData; - if(hmsc->bdev_ops->GetCapacity(lun, &hmsc->scsi_blk_nbr, &hmsc->scsi_blk_size) != 0) + if(hmsc->bdev_ops->GetCapacity(lun, &hmsc->scsi_blk_nbr[lun], &hmsc->scsi_blk_size[lun]) != 0) { SCSI_SenseCode(pdev, lun, @@ -258,15 +258,17 @@ static int8_t SCSI_ReadCapacity10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_ else { - hmsc->bot_data[0] = (uint8_t)((hmsc->scsi_blk_nbr - 1) >> 24); - hmsc->bot_data[1] = (uint8_t)((hmsc->scsi_blk_nbr - 1) >> 16); - hmsc->bot_data[2] = (uint8_t)((hmsc->scsi_blk_nbr - 1) >> 8); - hmsc->bot_data[3] = (uint8_t)(hmsc->scsi_blk_nbr - 1); + uint32_t blk_nbr = hmsc->scsi_blk_nbr[lun]; + hmsc->bot_data[0] = (uint8_t)((blk_nbr - 1) >> 24); + hmsc->bot_data[1] = (uint8_t)((blk_nbr - 1) >> 16); + hmsc->bot_data[2] = (uint8_t)((blk_nbr - 1) >> 8); + hmsc->bot_data[3] = (uint8_t)(blk_nbr - 1); - hmsc->bot_data[4] = (uint8_t)(hmsc->scsi_blk_size >> 24); - hmsc->bot_data[5] = (uint8_t)(hmsc->scsi_blk_size >> 16); - hmsc->bot_data[6] = (uint8_t)(hmsc->scsi_blk_size >> 8); - hmsc->bot_data[7] = (uint8_t)(hmsc->scsi_blk_size); + uint32_t blk_size = hmsc->scsi_blk_size[lun]; + hmsc->bot_data[4] = (uint8_t)(blk_size >> 24); + hmsc->bot_data[5] = (uint8_t)(blk_size >> 16); + hmsc->bot_data[6] = (uint8_t)(blk_size >> 8); + hmsc->bot_data[7] = (uint8_t)(blk_size); hmsc->bot_data_length = 8; return 0; @@ -516,7 +518,7 @@ static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *para } hmsc->bot_state = USBD_BOT_DATA_IN; - hmsc->scsi_blk_len *= hmsc->scsi_blk_size; + hmsc->scsi_blk_len *= hmsc->scsi_blk_size[lun]; /* cases 4,5 : Hi <> Dn */ if (hmsc->cbw.dDataLength != hmsc->scsi_blk_len) @@ -596,7 +598,7 @@ static int8_t SCSI_Write10 (USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *pa return -1; /* error */ } - hmsc->scsi_blk_len *= hmsc->scsi_blk_size; + hmsc->scsi_blk_len *= hmsc->scsi_blk_size[lun]; /* cases 3,11,13 : Hn,Ho <> D0 */ if (hmsc->cbw.dDataLength != hmsc->scsi_blk_len) @@ -670,7 +672,7 @@ static int8_t SCSI_CheckAddressRange (USBD_HandleTypeDef *pdev, uint8_t lun , u { USBD_MSC_BOT_HandleTypeDef *hmsc = &((usbd_cdc_msc_hid_state_t*)pdev->pClassData)->MSC_BOT_ClassData; - if ((blk_offset + blk_nbr) > hmsc->scsi_blk_nbr ) + if ((blk_offset + blk_nbr) > hmsc->scsi_blk_nbr[lun]) { SCSI_SenseCode(pdev, lun, @@ -697,7 +699,7 @@ static int8_t SCSI_ProcessRead (USBD_HandleTypeDef *pdev, uint8_t lun) if( hmsc->bdev_ops->Read(lun , hmsc->bot_data, hmsc->scsi_blk_addr_in_blks, - len / hmsc->scsi_blk_size) < 0) + len / hmsc->scsi_blk_size[lun]) < 0) { SCSI_SenseCode(pdev, @@ -714,7 +716,7 @@ static int8_t SCSI_ProcessRead (USBD_HandleTypeDef *pdev, uint8_t lun) len); - hmsc->scsi_blk_addr_in_blks += len / hmsc->scsi_blk_size; + hmsc->scsi_blk_addr_in_blks += len / hmsc->scsi_blk_size[lun]; hmsc->scsi_blk_len -= len; /* case 6 : Hi = Di */ @@ -744,7 +746,7 @@ static int8_t SCSI_ProcessWrite (USBD_HandleTypeDef *pdev, uint8_t lun) if(hmsc->bdev_ops->Write(lun , hmsc->bot_data, hmsc->scsi_blk_addr_in_blks, - len / hmsc->scsi_blk_size) < 0) + len / hmsc->scsi_blk_size[lun]) < 0) { SCSI_SenseCode(pdev, lun, @@ -754,7 +756,7 @@ static int8_t SCSI_ProcessWrite (USBD_HandleTypeDef *pdev, uint8_t lun) } - hmsc->scsi_blk_addr_in_blks += len / hmsc->scsi_blk_size; + hmsc->scsi_blk_addr_in_blks += len / hmsc->scsi_blk_size[lun]; hmsc->scsi_blk_len -= len; /* case 12 : Ho = Do */ From 441460d81ff2b1faee7d044d859896f754361b93 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 25 Jul 2020 23:05:41 +1000 Subject: [PATCH 195/352] extmod/uasyncio: Add StreamReader.readexactly(n) method. It raises on EOFError instead of an IncompleteReadError (which is what CPython does). But the latter is derived from EOFError so code compatible with MicroPython and CPython can be written by catching EOFError (eg see included test). Fixes issue #6156. Signed-off-by: Damien George --- extmod/uasyncio/stream.py | 12 ++++ tests/multi_net/uasyncio_tcp_readexactly.py | 68 +++++++++++++++++++ .../multi_net/uasyncio_tcp_readexactly.py.exp | 10 +++ 3 files changed, 90 insertions(+) create mode 100644 tests/multi_net/uasyncio_tcp_readexactly.py create mode 100644 tests/multi_net/uasyncio_tcp_readexactly.py.exp diff --git a/extmod/uasyncio/stream.py b/extmod/uasyncio/stream.py index 2a1efd1a17bfc..b6d787e4f0336 100644 --- a/extmod/uasyncio/stream.py +++ b/extmod/uasyncio/stream.py @@ -30,6 +30,18 @@ async def read(self, n): yield core._io_queue.queue_read(self.s) return self.s.read(n) + async def readexactly(self, n): + r = b"" + while n: + yield core._io_queue.queue_read(self.s) + r2 = self.s.read(n) + if r2 is not None: + if not len(r2): + raise EOFError + r += r2 + n -= len(r2) + return r + async def readline(self): l = b"" while True: diff --git a/tests/multi_net/uasyncio_tcp_readexactly.py b/tests/multi_net/uasyncio_tcp_readexactly.py new file mode 100644 index 0000000000000..71d8c6d0e9323 --- /dev/null +++ b/tests/multi_net/uasyncio_tcp_readexactly.py @@ -0,0 +1,68 @@ +# Test uasyncio stream readexactly() method using TCP server/client + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + + +async def handle_connection(reader, writer): + writer.write(b"a") + await writer.drain() + + # Split the first 2 bytes up so the client must wait for the second one + await asyncio.sleep(0.1) + + writer.write(b"b") + await writer.drain() + + writer.write(b"c") + await writer.drain() + + writer.write(b"d") + await writer.drain() + + print("close") + writer.close() + await writer.wait_closed() + + print("done") + ev.set() + + +async def tcp_server(): + global ev + ev = asyncio.Event() + server = await asyncio.start_server(handle_connection, "0.0.0.0", PORT) + print("server running") + multitest.next() + async with server: + await asyncio.wait_for(ev.wait(), 10) + + +async def tcp_client(): + reader, writer = await asyncio.open_connection(IP, PORT) + print(await reader.readexactly(2)) + print(await reader.readexactly(0)) + print(await reader.readexactly(1)) + try: + print(await reader.readexactly(2)) + except EOFError as er: + print("EOFError") + print(await reader.readexactly(0)) + + +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + asyncio.run(tcp_server()) + + +def instance1(): + multitest.next() + asyncio.run(tcp_client()) diff --git a/tests/multi_net/uasyncio_tcp_readexactly.py.exp b/tests/multi_net/uasyncio_tcp_readexactly.py.exp new file mode 100644 index 0000000000000..65ce6d628dd4f --- /dev/null +++ b/tests/multi_net/uasyncio_tcp_readexactly.py.exp @@ -0,0 +1,10 @@ +--- instance0 --- +server running +close +done +--- instance1 --- +b'ab' +b'' +b'c' +EOFError +b'' From 952de5cb77208f519a88752cd5de43337686f7f4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Jul 2020 10:56:24 +1000 Subject: [PATCH 196/352] tools/makemanifest.py: Use errno.EEXIST instead of number 17. To make this code more portable, across different platforms. Signed-off-by: Damien George --- tools/makemanifest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index babce7fa73f4b..6779198c47c00 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -25,6 +25,7 @@ # THE SOFTWARE. from __future__ import print_function +import errno import sys import os import subprocess @@ -171,7 +172,7 @@ def mkdir(path): try: os.mkdir(cur_path) except OSError as er: - if er.args[0] == 17: # file exists + if er.args[0] == errno.EEXIST: pass else: raise er From 0c0cef9870e8215d67c79fefa6582849842001f9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Jul 2020 14:30:56 +1000 Subject: [PATCH 197/352] tests: Move .mpy import tests from import/ to micropython/ dir. These tests are specific to MicroPython so have a better home in the micropython/ test subdir, and putting them here allows them to be run by all targets, not just those that have access to the local filesystem (eg the unix port). Signed-off-by: Damien George --- .../{import/mpy_invalid.py => micropython/import_mpy_invalid.py} | 0 .../mpy_invalid.py.exp => micropython/import_mpy_invalid.py.exp} | 0 .../mpy_native.py => micropython/import_mpy_native_x64.py} | 0 .../import_mpy_native_x64.py.exp} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/{import/mpy_invalid.py => micropython/import_mpy_invalid.py} (100%) rename tests/{import/mpy_invalid.py.exp => micropython/import_mpy_invalid.py.exp} (100%) rename tests/{import/mpy_native.py => micropython/import_mpy_native_x64.py} (100%) rename tests/{import/mpy_native.py.exp => micropython/import_mpy_native_x64.py.exp} (100%) diff --git a/tests/import/mpy_invalid.py b/tests/micropython/import_mpy_invalid.py similarity index 100% rename from tests/import/mpy_invalid.py rename to tests/micropython/import_mpy_invalid.py diff --git a/tests/import/mpy_invalid.py.exp b/tests/micropython/import_mpy_invalid.py.exp similarity index 100% rename from tests/import/mpy_invalid.py.exp rename to tests/micropython/import_mpy_invalid.py.exp diff --git a/tests/import/mpy_native.py b/tests/micropython/import_mpy_native_x64.py similarity index 100% rename from tests/import/mpy_native.py rename to tests/micropython/import_mpy_native_x64.py diff --git a/tests/import/mpy_native.py.exp b/tests/micropython/import_mpy_native_x64.py.exp similarity index 100% rename from tests/import/mpy_native.py.exp rename to tests/micropython/import_mpy_native_x64.py.exp From 8da40baa47ee9fe7aac228af2c0addd1f4ce3646 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Jul 2020 14:43:13 +1000 Subject: [PATCH 198/352] tests/micropython: Improve .mpy import tests to run on more targets. All imports are now tested to see if the test should be skipped, UserFile.read is removed, and UserFile.readinto is made more efficient. Signed-off-by: Damien George --- tests/micropython/import_mpy_invalid.py | 19 ++++++------------- tests/micropython/import_mpy_native_x64.py | 19 ++++++------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/tests/micropython/import_mpy_invalid.py b/tests/micropython/import_mpy_invalid.py index d6d01e7f1fc0c..00973fe3f9f12 100644 --- a/tests/micropython/import_mpy_invalid.py +++ b/tests/micropython/import_mpy_invalid.py @@ -1,11 +1,9 @@ # test importing of invalid .mpy files -import sys, uio - try: - uio.IOBase - import uos + import sys, uio, uos + uio.IOBase uos.mount except (ImportError, AttributeError): print("SKIP") @@ -14,18 +12,13 @@ class UserFile(uio.IOBase): def __init__(self, data): - self.data = data + self.data = memoryview(data) self.pos = 0 - def read(self): - return self.data - def readinto(self, buf): - n = 0 - while n < len(buf) and self.pos < len(self.data): - buf[n] = self.data[self.pos] - n += 1 - self.pos += 1 + n = min(len(buf), len(self.data) - self.pos) + buf[:n] = self.data[self.pos : self.pos + n] + self.pos += n return n def ioctl(self, req, arg): diff --git a/tests/micropython/import_mpy_native_x64.py b/tests/micropython/import_mpy_native_x64.py index 5d7bdab4a2aaa..d0de507d44234 100644 --- a/tests/micropython/import_mpy_native_x64.py +++ b/tests/micropython/import_mpy_native_x64.py @@ -1,11 +1,9 @@ # test importing of .mpy files with native code (x64 only) -import sys, uio - try: - uio.IOBase - import uos + import sys, uio, uos + uio.IOBase uos.mount except (ImportError, AttributeError): print("SKIP") @@ -18,18 +16,13 @@ class UserFile(uio.IOBase): def __init__(self, data): - self.data = data + self.data = memoryview(data) self.pos = 0 - def read(self): - return self.data - def readinto(self, buf): - n = 0 - while n < len(buf) and self.pos < len(self.data): - buf[n] = self.data[self.pos] - n += 1 - self.pos += 1 + n = min(len(buf), len(self.data) - self.pos) + buf[:n] = self.data[self.pos : self.pos + n] + self.pos += n return n def ioctl(self, req, arg): From 9883d8e818feba112935676eb5aa4ce211d7779c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 27 Jul 2020 23:52:38 +1000 Subject: [PATCH 199/352] py/persistentcode: Maintain root ptr list of imported native .mpy code. On ports where normal heap memory can contain executable code (eg ARM-based ports such as stm32), native code loaded from an .mpy file may be reclaimed by the GC because there's no reference to the very start of the native machine code block that is reachable from root pointers (only pointers to internal parts of the machine code block are reachable, but that doesn't help the GC find the memory). This commit fixes this issue by maintaining an explicit list of root pointers pointing to native code that is loaded from an .mpy file. This is not needed for all ports so is selectable by the new configuration option MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE. It's enabled by default if a port does not specify any special functions to allocate or commit executable memory. A test is included to test that native code loaded from an .mpy file does not get reclaimed by the GC. Fixes #6045. Signed-off-by: Damien George --- py/mpconfig.h | 12 +++ py/mpstate.h | 5 + py/persistentcode.c | 14 ++- py/runtime.c | 4 + tests/micropython/import_mpy_native_gc.py | 91 +++++++++++++++++++ tests/micropython/import_mpy_native_gc.py.exp | 1 + 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/micropython/import_mpy_native_gc.py create mode 100644 tests/micropython/import_mpy_native_gc.py.exp diff --git a/py/mpconfig.h b/py/mpconfig.h index 287b15aaef32f..3d9ce8bb55c95 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -351,6 +351,18 @@ // Convenience definition for whether any native or inline assembler emitter is enabled #define MICROPY_EMIT_MACHINE_CODE (MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM) +// Whether native relocatable code loaded from .mpy files is explicitly tracked +// so that the GC cannot reclaim it. Needed on architectures that allocate +// executable memory on the MicroPython heap and don't explicitly track this +// data some other way. +#ifndef MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE +#if !MICROPY_EMIT_MACHINE_CODE || defined(MP_PLAT_ALLOC_EXEC) || defined(MP_PLAT_COMMIT_EXEC) +#define MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE (0) +#else +#define MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE (1) +#endif +#endif + /*****************************************************************************/ /* Compiler configuration */ diff --git a/py/mpstate.h b/py/mpstate.h index 5f6cf55936a32..2519c77e2d647 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -167,6 +167,11 @@ typedef struct _mp_state_vm_t { mp_obj_dict_t *mp_module_builtins_override_dict; #endif + #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE + // An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them. + mp_obj_t track_reloc_code_list; + #endif + // include any root pointers defined by a port MICROPY_PORT_ROOT_POINTERS diff --git a/py/persistentcode.c b/py/persistentcode.c index 386ea49477f6b..da3234a5fe9cd 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2013-2020 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 @@ -516,6 +516,18 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); #else if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE + // If native code needs relocations then it's not guaranteed that a pointer to + // the head of `buf` (containing the machine code) will be retained for the GC + // to trace. This is because native functions can start inside `buf` and so + // it's possible that the only GC-reachable pointers are pointers inside `buf`. + // So put this `buf` on a list of reachable root pointers. + if (MP_STATE_PORT(track_reloc_code_list) == MP_OBJ_NULL) { + MP_STATE_PORT(track_reloc_code_list) = mp_obj_new_list(0, NULL); + } + mp_obj_list_append(MP_STATE_PORT(track_reloc_code_list), MP_OBJ_FROM_PTR(fun_data)); + #endif + // Do the relocations. mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); } #endif diff --git a/py/runtime.c b/py/runtime.c index 78da386919532..38da2f4538e97 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -106,6 +106,10 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif + #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE + MP_STATE_VM(track_reloc_code_list) = MP_OBJ_NULL; + #endif + #if MICROPY_PY_OS_DUPTERM for (size_t i = 0; i < MICROPY_PY_OS_DUPTERM; ++i) { MP_STATE_VM(dupterm_objs[i]) = MP_OBJ_NULL; diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py new file mode 100644 index 0000000000000..e8fac8f179371 --- /dev/null +++ b/tests/micropython/import_mpy_native_gc.py @@ -0,0 +1,91 @@ +# Test that native code loaded from a .mpy file is retained after a GC. + +try: + import gc, sys, uio, uos + + sys.implementation.mpy + uio.IOBase + uos.mount +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class UserFile(uio.IOBase): + def __init__(self, data): + self.data = memoryview(data) + self.pos = 0 + + def readinto(self, buf): + n = min(len(buf), len(self.data) - self.pos) + buf[:n] = self.data[self.pos : self.pos + n] + self.pos += n + return n + + def ioctl(self, req, arg): + return 0 + + +class UserFS: + def __init__(self, files): + self.files = files + + def mount(self, readonly, mksfs): + pass + + def umount(self): + pass + + def stat(self, path): + if path in self.files: + return (32768, 0, 0, 0, 0, 0, 0, 0, 0, 0) + raise OSError + + def open(self, path, mode): + return UserFile(self.files[path]) + + +# Pre-compiled examples/natmod/features0 example for various architectures, keyed +# by the required value of sys.implementation.mpy. +features0_file_contents = { + # -march=x64 -mcache-lookup-bc + 0xB05: b'M\x05\x0b\x1f \x84b\xe9/\x00\x00\x00SH\x8b\x1ds\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dA\x00\x00\x00H\x8b\x7f\x08L\x8bc(A\xff\xd4H\x8d5\x1f\x00\x00\x00H\x89\xc5H\x8b\x05-\x00\x00\x00\x0f\xb78\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x84@\x12factorial\x10\x00\x00\r \x01"\x9f\x1c\x01\x1e\xff', + # -march=armv7m + 0x1605: b"M\x05\x16\x1f \x84\x12\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\tN\tK~D\xf4X@hgi\xb8G\x05F\x07K\x07I\xf2XyD\x10\x88ck\x98G(F\xb8G h\xf8\xbd6\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x01\x84\x00\x12factorial\x10\x00\x00\r<\x01>\x9f8\x01:\xff", +} + +# Populate other armv7m-derived archs based on armv7m. +for arch in (0x1A05, 0x1E05, 0x2205): + features0_file_contents[arch] = features0_file_contents[0x1605] + +if sys.implementation.mpy not in features0_file_contents: + print("SKIP") + raise SystemExit + +# These are the test .mpy files. +user_files = {"/features0.mpy": features0_file_contents[sys.implementation.mpy]} + +# Create and mount a user filesystem. +uos.mount(UserFS(user_files), "/userfs") +sys.path.append("/userfs") + +# Import the native function. +gc.collect() +from features0 import factorial + +# Free the module that contained the function. +del sys.modules["features0"] + +# Run a GC cycle which should reclaim the module but not the function. +gc.collect() + +# Allocate lots of fragmented memory to overwrite anything that was just freed by the GC. +for i in range(1000): + [] + +# Run the native function, it should not have been freed or overwritten. +print(factorial(10)) + +# Unmount and undo path addition. +uos.umount("/userfs") +sys.path.pop() diff --git a/tests/micropython/import_mpy_native_gc.py.exp b/tests/micropython/import_mpy_native_gc.py.exp new file mode 100644 index 0000000000000..3fbd4a8698fa3 --- /dev/null +++ b/tests/micropython/import_mpy_native_gc.py.exp @@ -0,0 +1 @@ +3628800 From b731bd0ce6f05796dd642804bf2469646d7a063c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 8 Aug 2020 14:46:05 +1000 Subject: [PATCH 200/352] tools/makemanifest.py: Print nicely formatted errors from mpy-cross. If mpy-cross exits with an error be sure to print that error in a way that is readable, instead of a long bytes object. Signed-off-by: Damien George --- tools/makemanifest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 6779198c47c00..b7d4a4d4a68af 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -288,7 +288,8 @@ def main(): + ["-o", outfile, "-s", script, "-O{}".format(opt), infile] ) if res != 0: - print("error compiling {}: {}".format(infile, out)) + print("error compiling {}:".format(infile)) + sys.stdout.buffer.write(out) raise SystemExit(1) ts_outfile = get_timestamp(outfile) mpy_files.append(outfile) From 60f5b941e096aba270a7d4af8faa33169de8364f Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 8 Aug 2020 15:39:56 +1000 Subject: [PATCH 201/352] extmod/vfs_reader: Fix mp_reader_new_file to open file in "rb" mode. mp_reader_new_file() is used to read in files for importing, either .py or .mpy files, for the lexer and persistent code loader respectively. In both cases the file should be opened in raw bytes mode: the lexer handles unicode characters itself, and .mpy files contain 8-bit bytes by nature. Before this commit importing was working correctly because, although the file was opened in text mode, all native filesystem implementations (POSIX, FAT, LFS) would access the file in raw bytes mode via mp_stream_rw() calling mp_stream_p_t.read(). So it was only an issue for non-native filesystems, such as those implemented in Python. For Python-based filesystem implementations, a call to mp_stream_rw() would go via IOBase and then to readinto() at the Python level, and readinto() is only defined on files opened in raw bytes mode. Signed-off-by: Damien George --- extmod/vfs_reader.c | 7 +++++-- tests/extmod/vfs_userfs.py | 14 ++++++++++---- tests/extmod/vfs_userfs.py.exp | 6 +++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index db13ce3c33141..d3904c5c50693 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -71,8 +71,11 @@ STATIC void mp_reader_vfs_close(void *data) { void mp_reader_new_file(mp_reader_t *reader, const char *filename) { mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); - mp_obj_t arg = mp_obj_new_str(filename, strlen(filename)); - rf->file = mp_vfs_open(1, &arg, (mp_map_t *)&mp_const_empty_map); + mp_obj_t args[2] = { + mp_obj_new_str(filename, strlen(filename)), + MP_OBJ_NEW_QSTR(MP_QSTR_rb), + }; + rf->file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); int errcode; rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { diff --git a/tests/extmod/vfs_userfs.py b/tests/extmod/vfs_userfs.py index 06e546b081a97..3cdfe82eea8dc 100644 --- a/tests/extmod/vfs_userfs.py +++ b/tests/extmod/vfs_userfs.py @@ -16,14 +16,20 @@ class UserFile(uio.IOBase): - def __init__(self, data): + def __init__(self, mode, data): + assert isinstance(data, bytes) + self.is_text = mode.find("b") == -1 self.data = data self.pos = 0 def read(self): - return self.data + if self.is_text: + return str(self.data, "utf8") + else: + return self.data def readinto(self, buf): + assert not self.is_text n = 0 while n < len(buf) and self.pos < len(self.data): buf[n] = self.data[self.pos] @@ -54,12 +60,12 @@ def stat(self, path): def open(self, path, mode): print("open", path, mode) - return UserFile(self.files[path]) + return UserFile(mode, self.files[path]) # create and mount a user filesystem user_files = { - "/data.txt": b"some data in a text file\n", + "/data.txt": b"some data in a text file", "/usermod1.py": b"print('in usermod1')\nimport usermod2", "/usermod2.py": b"print('in usermod2')", } diff --git a/tests/extmod/vfs_userfs.py.exp b/tests/extmod/vfs_userfs.py.exp index 6a4d925b91e8b..00ddd95fca508 100644 --- a/tests/extmod/vfs_userfs.py.exp +++ b/tests/extmod/vfs_userfs.py.exp @@ -1,12 +1,12 @@ open /data.txt r -b'some data in a text file\n' +some data in a text file stat /usermod1 stat /usermod1.py -open /usermod1.py r +open /usermod1.py rb ioctl 4 0 in usermod1 stat /usermod2 stat /usermod2.py -open /usermod2.py r +open /usermod2.py rb ioctl 4 0 in usermod2 From ac94e06f0ba818157490587af1e4d0fb4cf3e962 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 7 Aug 2020 14:39:13 -0500 Subject: [PATCH 202/352] zephyr: Include storage/flash_map.h unconditionally. Include storage/flash_map.h unconditionally so we always have access to the FLASH_AREA_LABEL_EXISTS macro, even if CONFIG_FLASH_MAP is not defined. This fixes a build error for the qemu_x86 board: main.c:108:63: error: missing binary operator before token "(" 108 | #elif defined(CONFIG_FLASH_MAP) && FLASH_AREA_LABEL_EXISTS(storage) | ^ ../../py/mkrules.mk:88: recipe for target 'build/genhdr/qstr.i.last' failed Signed-off-by: Maureen Helm --- ports/zephyr/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 78b947d7af9b9..74785cc830420 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -38,9 +38,7 @@ #include #endif -#ifdef CONFIG_FLASH_MAP #include -#endif #include "py/mperrno.h" #include "py/compile.h" From 17689a71f66bcf96c09f8700fa3e0f5aa5be225c Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Mon, 27 Jul 2020 12:58:02 -0500 Subject: [PATCH 203/352] travis: Add zephyr build to CI. Adds a job to build the zephyr port in CI using the same docker container that the zephyr project uses for its own CI. Always make clean zephyr builds to ensure we don't just rebuild C code, but we also rebuild Kconfig and dts. This is required when switching between boards, which have different Kconfigs and device trees. Uses the tagged zephyr 2.3.0 release. Signed-off-by: Maureen Helm --- .travis.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.travis.yml b/.travis.yml index 48ce39f60ad89..ebf9b55899dc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,33 @@ jobs: - tools/codeformat.py - git diff --exit-code + # zephyr port + - stage: test + name: "zephyr port build" + services: + - docker + before_install: + - docker pull zephyrprojectrtos/ci:v0.11.8 + - > + docker run --name zephyr-ci -d -it + -v "$(pwd)":/micropython + -e ZEPHYR_SDK_INSTALL_DIR=/opt/sdk/zephyr-sdk-0.11.3 + -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr + -w /micropython/ports/zephyr + zephyrprojectrtos/ci:v0.11.8 + - docker ps -a + install: + - docker exec zephyr-ci west init --mr v2.3.0 /zephyrproject + - docker exec -w /zephyrproject zephyr-ci west update + - docker exec -w /zephyrproject zephyr-ci west zephyr-export + script: + - docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS}" + - docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS} BOARD=frdm_k64f" + - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS}" + - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=frdm_k64f" + - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=mimxrt1050_evk" + - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=reel_board" + # unix port on OSX (first in list because the build VM takes a long time to start) - stage: test os: osx From e76c7466b62c940150cf1b5000e18f60a5efa2e3 Mon Sep 17 00:00:00 2001 From: Zenix27 <30363000+Zenix27@users.noreply.github.com> Date: Sat, 11 Jul 2020 12:23:26 +0530 Subject: [PATCH 204/352] docs: Change `\*` to `*` in argument lists. Latest versions of Sphinx (at least 3.1.0) do not need the `*` escaped and will render the `\` in the output if it is there, so remove it. Fixes issue #6209. --- docs/library/btree.rst | 2 +- docs/library/esp32.rst | 2 +- docs/library/lcd160cr.rst | 2 +- docs/library/machine.ADCWiPy.rst | 4 ++-- docs/library/machine.I2C.rst | 10 +++++----- docs/library/machine.Pin.rst | 6 +++--- docs/library/machine.RTC.rst | 4 ++-- docs/library/machine.SPI.rst | 2 +- docs/library/machine.Signal.rst | 2 +- docs/library/machine.Timer.rst | 2 +- docs/library/machine.TimerWiPy.rst | 6 +++--- docs/library/machine.UART.rst | 2 +- docs/library/network.CC3K.rst | 2 +- docs/library/network.WLAN.rst | 2 +- docs/library/network.WLANWiPy.rst | 6 +++--- docs/library/network.rst | 4 ++-- docs/library/pyb.CAN.rst | 8 ++++---- docs/library/pyb.DAC.rst | 6 +++--- docs/library/pyb.Flash.rst | 2 +- docs/library/pyb.I2C.rst | 10 +++++----- docs/library/pyb.SPI.rst | 8 ++++---- docs/library/pyb.Timer.rst | 2 +- docs/library/pyb.UART.rst | 2 +- docs/library/pyb.USB_HID.rst | 2 +- docs/library/pyb.USB_VCP.rst | 6 +++--- docs/library/pyb.rst | 2 +- docs/library/uasyncio.rst | 2 +- docs/library/uos.rst | 2 +- 28 files changed, 55 insertions(+), 55 deletions(-) diff --git a/docs/library/btree.rst b/docs/library/btree.rst index a1f7a5420f15e..de52ef7e7e44e 100644 --- a/docs/library/btree.rst +++ b/docs/library/btree.rst @@ -76,7 +76,7 @@ Example:: Functions --------- -.. function:: open(stream, \*, flags=0, pagesize=0, cachesize=0, minkeypage=0) +.. function:: open(stream, *, flags=0, pagesize=0, cachesize=0, minkeypage=0) Open a database from a random-access `stream` (like an open file). All other parameters are optional and keyword-only, and allow to tweak advanced diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index dfde840a2141a..c6777b8a7da33 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -186,7 +186,7 @@ For more details see Espressif's `ESP-IDF RMT documentation. *beta feature* and the interface may change in the future. -.. class:: RMT(channel, \*, pin=None, clock_div=8, carrier_freq=0, carrier_duty_percent=50) +.. class:: RMT(channel, *, pin=None, clock_div=8, carrier_freq=0, carrier_duty_percent=50) This class provides access to one of the eight RMT channels. *channel* is required and identifies which RMT channel (0-7) will be configured. *pin*, diff --git a/docs/library/lcd160cr.rst b/docs/library/lcd160cr.rst index e31ed94651ca5..85e4b8f07a1f6 100644 --- a/docs/library/lcd160cr.rst +++ b/docs/library/lcd160cr.rst @@ -37,7 +37,7 @@ For example:: Constructors ------------ -.. class:: LCD160CR(connect=None, \*, pwr=None, i2c=None, spi=None, i2c_addr=98) +.. class:: LCD160CR(connect=None, *, pwr=None, i2c=None, spi=None, i2c_addr=98) Construct an LCD160CR object. The parameters are: diff --git a/docs/library/machine.ADCWiPy.rst b/docs/library/machine.ADCWiPy.rst index 4a4f0524c8f0b..e500d00890f3a 100644 --- a/docs/library/machine.ADCWiPy.rst +++ b/docs/library/machine.ADCWiPy.rst @@ -22,7 +22,7 @@ Usage:: Constructors ------------ -.. class:: ADCWiPy(id=0, \*, bits=12) +.. class:: ADCWiPy(id=0, *, bits=12) Create an ADC object associated with the given pin. This allows you to then read analog values on that pin. @@ -39,7 +39,7 @@ Constructors Methods ------- -.. method:: ADCWiPy.channel(id, \*, pin) +.. method:: ADCWiPy.channel(id, *, pin) Create an analog pin. If only channel ID is given, the correct pin will be selected. Alternatively, only the pin can be passed and the correct diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index 2cbfec1ba83bf..743554bc76650 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -33,7 +33,7 @@ Example usage:: Constructors ------------ -.. class:: I2C(id=-1, \*, scl, sda, freq=400000) +.. class:: I2C(id=-1, *, scl, sda, freq=400000) Construct and return a new I2C object using the following parameters: @@ -52,7 +52,7 @@ Constructors General Methods --------------- -.. method:: I2C.init(scl, sda, \*, freq=400000) +.. method:: I2C.init(scl, sda, *, freq=400000) Initialise the I2C bus with the given arguments: @@ -153,14 +153,14 @@ from and written to. In this case there are two addresses associated with an I2C transaction: the slave address and the memory address. The following methods are convenience functions to communicate with such devices. -.. method:: I2C.readfrom_mem(addr, memaddr, nbytes, \*, addrsize=8) +.. method:: I2C.readfrom_mem(addr, memaddr, nbytes, *, addrsize=8) Read *nbytes* from the slave specified by *addr* starting from the memory address specified by *memaddr*. The argument *addrsize* specifies the address size in bits. Returns a `bytes` object with the data read. -.. method:: I2C.readfrom_mem_into(addr, memaddr, buf, \*, addrsize=8) +.. method:: I2C.readfrom_mem_into(addr, memaddr, buf, *, addrsize=8) Read into *buf* from the slave specified by *addr* starting from the memory address specified by *memaddr*. The number of bytes read is the @@ -170,7 +170,7 @@ methods are convenience functions to communicate with such devices. The method returns ``None``. -.. method:: I2C.writeto_mem(addr, memaddr, buf, \*, addrsize=8) +.. method:: I2C.writeto_mem(addr, memaddr, buf, *, addrsize=8) Write *buf* to the slave specified by *addr* starting from the memory address specified by *memaddr*. diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index 3055491ebbba8..095240fe1499a 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -42,7 +42,7 @@ Usage Model:: Constructors ------------ -.. class:: Pin(id, mode=-1, pull=-1, \*, value, drive, alt) +.. class:: Pin(id, mode=-1, pull=-1, *, value, drive, alt) Access the pin peripheral (GPIO pin) associated with the given ``id``. If additional arguments are given in the constructor then they are used to initialise @@ -106,7 +106,7 @@ Constructors Methods ------- -.. method:: Pin.init(mode=-1, pull=-1, \*, value, drive, alt) +.. method:: Pin.init(mode=-1, pull=-1, *, value, drive, alt) Re-initialise the pin using the given parameters. Only those arguments that are specified will be set. The rest of the pin peripheral state will remain @@ -179,7 +179,7 @@ Methods Availability: WiPy. -.. method:: Pin.irq(handler=None, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING), \*, priority=1, wake=None, hard=False) +.. method:: Pin.irq(handler=None, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING), *, priority=1, wake=None, hard=False) Configure an interrupt handler to be called when the trigger source of the pin is active. If the pin mode is ``Pin.IN`` then the trigger source is diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index 548ad007e0dc3..d5a4f390b512b 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -38,7 +38,7 @@ Methods Resets the RTC to the time of January 1, 2015 and starts running it again. -.. method:: RTC.alarm(id, time, \*, repeat=False) +.. method:: RTC.alarm(id, time, *, repeat=False) Set the RTC alarm. Time might be either a millisecond value to program the alarm to current time + time_in_ms in the future, or a datetimetuple. If the time passed is in @@ -52,7 +52,7 @@ Methods Cancel a running alarm. -.. method:: RTC.irq(\*, trigger, handler=None, wake=machine.IDLE) +.. method:: RTC.irq(*, trigger, handler=None, wake=machine.IDLE) Create an irq object triggered by a real time clock alarm. diff --git a/docs/library/machine.SPI.rst b/docs/library/machine.SPI.rst index a9fcc719c8956..ea460a7b81282 100644 --- a/docs/library/machine.SPI.rst +++ b/docs/library/machine.SPI.rst @@ -29,7 +29,7 @@ Constructors Methods ------- -.. method:: SPI.init(baudrate=1000000, \*, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=None, mosi=None, miso=None, pins=(SCK, MOSI, MISO)) +.. method:: SPI.init(baudrate=1000000, *, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=None, mosi=None, miso=None, pins=(SCK, MOSI, MISO)) Initialise the SPI bus with the given parameters: diff --git a/docs/library/machine.Signal.rst b/docs/library/machine.Signal.rst index a1a29164b2cfd..651c8c8497fd8 100644 --- a/docs/library/machine.Signal.rst +++ b/docs/library/machine.Signal.rst @@ -75,7 +75,7 @@ Constructors ------------ .. class:: Signal(pin_obj, invert=False) - Signal(pin_arguments..., \*, invert=False) + Signal(pin_arguments..., *, invert=False) Create a Signal object. There're two ways to create it: diff --git a/docs/library/machine.Timer.rst b/docs/library/machine.Timer.rst index b16ad52d597f5..2d1287f325780 100644 --- a/docs/library/machine.Timer.rst +++ b/docs/library/machine.Timer.rst @@ -35,7 +35,7 @@ Constructors Methods ------- -.. method:: Timer.init(\*, mode=Timer.PERIODIC, period=-1, callback=None) +.. method:: Timer.init(*, mode=Timer.PERIODIC, period=-1, callback=None) Initialise the timer. Example:: diff --git a/docs/library/machine.TimerWiPy.rst b/docs/library/machine.TimerWiPy.rst index 904a958c69b16..f5b748c62e1d5 100644 --- a/docs/library/machine.TimerWiPy.rst +++ b/docs/library/machine.TimerWiPy.rst @@ -39,7 +39,7 @@ Constructors Methods ------- -.. method:: TimerWiPy.init(mode, \*, width=16) +.. method:: TimerWiPy.init(mode, *, width=16) Initialise the timer. Example:: @@ -64,7 +64,7 @@ Methods Deinitialises the timer. Stops the timer, and disables the timer peripheral. -.. method:: TimerWiPy.channel(channel, \**, freq, period, polarity=TimerWiPy.POSITIVE, duty_cycle=0) +.. method:: TimerWiPy.channel(channel, **, freq, period, polarity=TimerWiPy.POSITIVE, duty_cycle=0) If only a channel identifier passed, then a previously initialized channel object is returned (or ``None`` if there is no previous channel). @@ -113,7 +113,7 @@ TimerChannel objects are created using the Timer.channel() method. Methods ------- -.. method:: timerchannel.irq(\*, trigger, priority=1, handler=None) +.. method:: timerchannel.irq(*, trigger, priority=1, handler=None) The behavior of this callback is heavily dependent on the operating mode of the timer channel: diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 70984dfb2fff1..957d58ca18256 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -43,7 +43,7 @@ Constructors Methods ------- -.. method:: UART.init(baudrate=9600, bits=8, parity=None, stop=1, \*, ...) +.. method:: UART.init(baudrate=9600, bits=8, parity=None, stop=1, *, ...) Initialise the UART bus with the given parameters: diff --git a/docs/library/network.CC3K.rst b/docs/library/network.CC3K.rst index 212a1e3bd771f..18210e2d26d59 100644 --- a/docs/library/network.CC3K.rst +++ b/docs/library/network.CC3K.rst @@ -51,7 +51,7 @@ Constructors Methods ------- -.. method:: CC3K.connect(ssid, key=None, \*, security=WPA2, bssid=None) +.. method:: CC3K.connect(ssid, key=None, *, security=WPA2, bssid=None) Connect to a WiFi access point using the given SSID, and other security parameters. diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 885a4460c56d4..fcdaa41b36345 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -32,7 +32,7 @@ Methods argument is passed. Otherwise, query current state if no argument is provided. Most other methods require active interface. -.. method:: WLAN.connect(ssid=None, password=None, \*, bssid=None) +.. method:: WLAN.connect(ssid=None, password=None, *, bssid=None) Connect to the specified wireless network, using the specified password. If *bssid* is given then the connection will be restricted to the diff --git a/docs/library/network.WLANWiPy.rst b/docs/library/network.WLANWiPy.rst index b6e3279597ac9..2a5ba118454bc 100644 --- a/docs/library/network.WLANWiPy.rst +++ b/docs/library/network.WLANWiPy.rst @@ -43,7 +43,7 @@ Constructors Methods ------- -.. method:: WLANWiPy.init(mode, \*, ssid, auth, channel, antenna) +.. method:: WLANWiPy.init(mode, *, ssid, auth, channel, antenna) Set or get the WiFi network processor configuration. @@ -69,7 +69,7 @@ Methods # configure as an station wlan.init(mode=WLAN.STA) -.. method:: WLANWiPy.connect(ssid, \*, auth=None, bssid=None, timeout=None) +.. method:: WLANWiPy.connect(ssid, *, auth=None, bssid=None, timeout=None) Connect to a WiFi access point using the given SSID, and other security parameters. @@ -131,7 +131,7 @@ Methods Get or set a 6-byte long bytes object with the MAC address. -.. method:: WLANWiPy.irq(\*, handler, wake) +.. method:: WLANWiPy.irq(*, handler, wake) Create a callback to be triggered when a WLAN event occurs during ``machine.SLEEP`` mode. Events are triggered by socket activity or by WLAN connection/disconnection. diff --git a/docs/library/network.rst b/docs/library/network.rst index 208c58f852e6b..bd3bc6f34ba81 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -58,7 +58,7 @@ parameter should be `id`. interface (behavior of calling them on inactive interface is undefined). -.. method:: AbstractNIC.connect([service_id, key=None, \*, ...]) +.. method:: AbstractNIC.connect([service_id, key=None, *, ...]) Connect the interface to a network. This method is optional, and available only for interfaces which are not "always connected". @@ -82,7 +82,7 @@ parameter should be `id`. Returns ``True`` if connected to network, otherwise returns ``False``. -.. method:: AbstractNIC.scan(\*, ...) +.. method:: AbstractNIC.scan(*, ...) Scan for the available network services/connections. Returns a list of tuples with discovered service parameters. For various diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst index ba935abfd5c4e..8078e29e0cc8d 100644 --- a/docs/library/pyb.CAN.rst +++ b/docs/library/pyb.CAN.rst @@ -49,7 +49,7 @@ Class Methods Methods ------- -.. method:: CAN.init(mode, extframe=False, prescaler=100, \*, sjw=1, bs1=6, bs2=8, auto_restart=False) +.. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False) Initialise the CAN bus with the given parameters: @@ -135,7 +135,7 @@ Methods - number of pending RX messages on fifo 0 - number of pending RX messages on fifo 1 -.. method:: CAN.setfilter(bank, mode, fifo, params, \*, rtr) +.. method:: CAN.setfilter(bank, mode, fifo, params, *, rtr) Configure a filter bank: @@ -187,7 +187,7 @@ Methods Return ``True`` if any message waiting on the FIFO, else ``False``. -.. method:: CAN.recv(fifo, list=None, \*, timeout=5000) +.. method:: CAN.recv(fifo, list=None, *, timeout=5000) Receive data on the bus: @@ -220,7 +220,7 @@ Methods # No heap memory is allocated in the following call can.recv(0, lst) -.. method:: CAN.send(data, id, \*, timeout=0, rtr=False) +.. method:: CAN.send(data, id, *, timeout=0, rtr=False) Send a message on the bus: diff --git a/docs/library/pyb.DAC.rst b/docs/library/pyb.DAC.rst index 9a465b9ce25d8..bf07119ada166 100644 --- a/docs/library/pyb.DAC.rst +++ b/docs/library/pyb.DAC.rst @@ -49,7 +49,7 @@ To output a continuous sine-wave at 12-bit resolution:: Constructors ------------ -.. class:: pyb.DAC(port, bits=8, \*, buffering=None) +.. class:: pyb.DAC(port, bits=8, *, buffering=None) Construct a new DAC object. @@ -76,7 +76,7 @@ Constructors Methods ------- -.. method:: DAC.init(bits=8, \*, buffering=None) +.. method:: DAC.init(bits=8, *, buffering=None) Reinitialise the DAC. *bits* can be 8 or 12. *buffering* can be ``None``, ``False`` or ``True``; see above constructor for the meaning @@ -103,7 +103,7 @@ Methods value is 2\*\*``bits``-1, where ``bits`` is set when creating the DAC object or by using the ``init`` method. -.. method:: DAC.write_timed(data, freq, \*, mode=DAC.NORMAL) +.. method:: DAC.write_timed(data, freq, *, mode=DAC.NORMAL) Initiates a burst of RAM to DAC using a DMA transfer. The input data is treated as an array of bytes in 8-bit mode, and diff --git a/docs/library/pyb.Flash.rst b/docs/library/pyb.Flash.rst index 13f2097871913..4b0c2ce2aaa2f 100644 --- a/docs/library/pyb.Flash.rst +++ b/docs/library/pyb.Flash.rst @@ -25,7 +25,7 @@ Constructors This constructor is deprecated and will be removed in a future version of MicroPython. -.. class:: pyb.Flash(\*, start=-1, len=-1) +.. class:: pyb.Flash(*, start=-1, len=-1) :noindex: Create and return a block device that accesses the flash at the specified offset. The length defaults to the remaining size of the device. diff --git a/docs/library/pyb.I2C.rst b/docs/library/pyb.I2C.rst index d549c1a81207d..641dcb8815873 100644 --- a/docs/library/pyb.I2C.rst +++ b/docs/library/pyb.I2C.rst @@ -84,7 +84,7 @@ Methods Turn off the I2C bus. -.. method:: I2C.init(mode, \*, addr=0x12, baudrate=400000, gencall=False, dma=False) +.. method:: I2C.init(mode, *, addr=0x12, baudrate=400000, gencall=False, dma=False) Initialise the I2C bus with the given parameters: @@ -100,7 +100,7 @@ Methods Check if an I2C device responds to the given address. Only valid when in master mode. -.. method:: I2C.mem_read(data, addr, memaddr, \*, timeout=5000, addr_size=8) +.. method:: I2C.mem_read(data, addr, memaddr, *, timeout=5000, addr_size=8) Read from the memory of an I2C device: @@ -113,7 +113,7 @@ Methods Returns the read data. This is only valid in master mode. -.. method:: I2C.mem_write(data, addr, memaddr, \*, timeout=5000, addr_size=8) +.. method:: I2C.mem_write(data, addr, memaddr, *, timeout=5000, addr_size=8) Write to the memory of an I2C device: @@ -126,7 +126,7 @@ Methods Returns ``None``. This is only valid in master mode. -.. method:: I2C.recv(recv, addr=0x00, \*, timeout=5000) +.. method:: I2C.recv(recv, addr=0x00, *, timeout=5000) Receive data on the bus: @@ -138,7 +138,7 @@ Methods Return value: if ``recv`` is an integer then a new buffer of the bytes received, otherwise the same buffer that was passed in to ``recv``. -.. method:: I2C.send(send, addr=0x00, \*, timeout=5000) +.. method:: I2C.send(send, addr=0x00, *, timeout=5000) Send data on the bus: diff --git a/docs/library/pyb.SPI.rst b/docs/library/pyb.SPI.rst index 181bdd3b6ec5d..24e2ec5a73d2c 100644 --- a/docs/library/pyb.SPI.rst +++ b/docs/library/pyb.SPI.rst @@ -51,7 +51,7 @@ Methods Turn off the SPI bus. -.. method:: SPI.init(mode, baudrate=328125, \*, prescaler, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) +.. method:: SPI.init(mode, baudrate=328125, *, prescaler, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) Initialise the SPI bus with the given parameters: @@ -77,7 +77,7 @@ Methods Printing the SPI object will show you the computed baudrate and the chosen prescaler. -.. method:: SPI.recv(recv, \*, timeout=5000) +.. method:: SPI.recv(recv, *, timeout=5000) Receive data on the bus: @@ -88,7 +88,7 @@ Methods Return value: if ``recv`` is an integer then a new buffer of the bytes received, otherwise the same buffer that was passed in to ``recv``. -.. method:: SPI.send(send, \*, timeout=5000) +.. method:: SPI.send(send, *, timeout=5000) Send data on the bus: @@ -97,7 +97,7 @@ Methods Return value: ``None``. -.. method:: SPI.send_recv(send, recv=None, \*, timeout=5000) +.. method:: SPI.send_recv(send, recv=None, *, timeout=5000) Send and receive data on the bus at the same time: diff --git a/docs/library/pyb.Timer.rst b/docs/library/pyb.Timer.rst index 532b64da8e93c..34fe71155fc15 100644 --- a/docs/library/pyb.Timer.rst +++ b/docs/library/pyb.Timer.rst @@ -62,7 +62,7 @@ Constructors Methods ------- -.. method:: Timer.init(\*, freq, prescaler, period, mode=Timer.UP, div=1, callback=None, deadtime=0) +.. method:: Timer.init(*, freq, prescaler, period, mode=Timer.UP, div=1, callback=None, deadtime=0) Initialise the timer. Initialisation must be either by frequency (in Hz) or by prescaler and period:: diff --git a/docs/library/pyb.UART.rst b/docs/library/pyb.UART.rst index 0a611a72566da..a1d6e59002551 100644 --- a/docs/library/pyb.UART.rst +++ b/docs/library/pyb.UART.rst @@ -84,7 +84,7 @@ Constructors Methods ------- -.. method:: UART.init(baudrate, bits=8, parity=None, stop=1, \*, timeout=0, flow=0, timeout_char=0, read_buf_len=64) +.. method:: UART.init(baudrate, bits=8, parity=None, stop=1, *, timeout=0, flow=0, timeout_char=0, read_buf_len=64) Initialise the UART bus with the given parameters: diff --git a/docs/library/pyb.USB_HID.rst b/docs/library/pyb.USB_HID.rst index 649dc3df4a605..7e23d1313d0da 100644 --- a/docs/library/pyb.USB_HID.rst +++ b/docs/library/pyb.USB_HID.rst @@ -21,7 +21,7 @@ Constructors Methods ------- -.. method:: USB_HID.recv(data, \*, timeout=5000) +.. method:: USB_HID.recv(data, *, timeout=5000) Receive data on the bus: diff --git a/docs/library/pyb.USB_VCP.rst b/docs/library/pyb.USB_VCP.rst index b16924cf60b2e..bbcbc0701b3b4 100644 --- a/docs/library/pyb.USB_VCP.rst +++ b/docs/library/pyb.USB_VCP.rst @@ -21,7 +21,7 @@ Constructors Methods ------- -.. method:: USB_VCP.init(\*, flow=-1) +.. method:: USB_VCP.init(*, flow=-1) Configure the USB VCP port. If the *flow* argument is not -1 then the value sets the flow control, which can be a bitwise-or of ``USB_VCP.RTS`` and ``USB_VCP.CTS``. @@ -89,7 +89,7 @@ Methods Returns the number of bytes written. -.. method:: USB_VCP.recv(data, \*, timeout=5000) +.. method:: USB_VCP.recv(data, *, timeout=5000) Receive data on the bus: @@ -100,7 +100,7 @@ Methods Return value: if ``data`` is an integer then a new buffer of the bytes received, otherwise the number of bytes read into ``data`` is returned. -.. method:: USB_VCP.send(data, \*, timeout=5000) +.. method:: USB_VCP.send(data, *, timeout=5000) Send data over the USB VCP: diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index 34103195ddd1c..addcd20a91aeb 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -210,7 +210,7 @@ Miscellaneous functions It only makes sense to call this function from within boot.py. -.. function:: mount(device, mountpoint, \*, readonly=False, mkfs=False) +.. function:: mount(device, mountpoint, *, readonly=False, mkfs=False) .. note:: This function is deprecated. Mounting and unmounting devices should be performed by :meth:`uos.mount` and :meth:`uos.umount` instead. diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index 31b38a4e05f2f..a81e532d7fd25 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -80,7 +80,7 @@ Additional functions This is a coroutine, and a MicroPython extension. -.. function:: gather(\*awaitables, return_exceptions=False) +.. function:: gather(*awaitables, return_exceptions=False) Run all *awaitables* concurrently. Any *awaitables* that are not tasks are promoted to tasks. diff --git a/docs/library/uos.rst b/docs/library/uos.rst index ad10d9129713d..051247b717419 100644 --- a/docs/library/uos.rst +++ b/docs/library/uos.rst @@ -144,7 +144,7 @@ programs. Ports that have this functionality provide the :func:`mount` and :func:`umount` functions, and possibly various filesystem implementations represented by VFS classes. -.. function:: mount(fsobj, mount_point, \*, readonly) +.. function:: mount(fsobj, mount_point, *, readonly) Mount the filesystem object *fsobj* at the location in the VFS given by the *mount_point* string. *fsobj* can be a a VFS object that has a ``mount()`` From 8727c4e2eca9026494c173061c5905d54fa5f73d Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Tue, 11 Aug 2020 12:44:51 -0700 Subject: [PATCH 205/352] stm32/pin_defs_stm32: Fix pin printing to show IN mode correctly. Prior to this commit, if you configure a pin as an output type (I2C in this example) and then later configure it back as an input, then it will report the type incorrectly. Example: >>> import machine >>> b6 = machine.Pin('B6') >>> b6 Pin(Pin.cpu.B6, mode=Pin.IN) >>> machine.I2C(1) I2C(1, scl=B6, sda=B7, freq=420000) >>> b6 Pin(Pin.cpu.B6, mode=Pin.ALT_OPEN_DRAIN, pull=Pin.PULL_UP, af=Pin.AF4_I2C1) >>> b6.init(machine.Pin.IN) >>> b6 Pin(Pin.cpu.B6, mode=Pin.ALT_OPEN_DRAIN, af=Pin.AF4_I2C1) With this commit the last print now works: >>> b6 Pin(Pin.cpu.B6, mode=Pin.IN) --- ports/stm32/pin_defs_stm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/pin_defs_stm32.c b/ports/stm32/pin_defs_stm32.c index 0aef5f95fa294..a3f9d039d6320 100644 --- a/ports/stm32/pin_defs_stm32.c +++ b/ports/stm32/pin_defs_stm32.c @@ -8,9 +8,9 @@ uint32_t pin_get_mode(const pin_obj_t *pin) { GPIO_TypeDef *gpio = pin->gpio; uint32_t mode = (gpio->MODER >> (pin->pin * 2)) & 3; - if (mode != GPIO_MODE_ANALOG) { + if (mode == GPIO_MODE_OUTPUT_PP || mode == GPIO_MODE_AF_PP) { if (gpio->OTYPER & pin->pin_mask) { - mode |= 1 << 4; + mode |= 1 << 4; // Converts from xxx_PP to xxx_OD } } return mode; From 60cf2c0959ed2668c01df664a2e7c3cd1f5c6d89 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Wed, 12 Aug 2020 21:30:33 +0200 Subject: [PATCH 206/352] tools/pyboard.py: Replace eval() of received data with alternative. Prior to this commit, pyboard.py used eval() to "parse" file data received from the board. Using eval() on received data from a device is dangerous, because a malicious device may inject arbitrary code execution on the PC that is doing the operation. Consider the following scenario: Eve may write a malicious script to Bob's board in his absence. On return Bob notices that something is wrong with the board, because it doesn't work as expected anymore. He wants to read out boot.py (or any other file) to see what is wrong. What he gets is a remote code execution on his PC. Proof of concept: Eve: $ cat boot.py _print = print print = lambda *x, **y: _print("os.system('ls /; echo Pwned!')", end="\r\n\x04") $ ./pyboard.py -f cp boot.py : cp boot.py :boot.py Bob: $ ./pyboard.py -f cp :boot.py /tmp/foo cp :boot.py /tmp/foo bin chroot dev home lib32 media opt root sbin sys usr boot config etc lib lib64 mnt proc run srv tmp var Pwned! There's also the possibility that the device is malfunctioning and sends random and possibly dangerous data back to the PC, to be eval'd. Fix this problem by using ast.literal_eval() to parse the received bytes, instead of eval(). Signed-off-by: Michael Buesch --- tools/pyboard.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index 6376b13c7e333..c97ddbe487be3 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -70,6 +70,7 @@ import sys import time import os +import ast try: stdout = sys.stdout.buffer @@ -426,7 +427,12 @@ def fs_get(self, src, dest, chunk_size=256): data = bytearray() self.exec_("print(r(%u))" % chunk_size, data_consumer=lambda d: data.extend(d)) assert data.endswith(b"\r\n\x04") - data = eval(str(data[:-3], "ascii")) + try: + data = ast.literal_eval(str(data[:-3], "ascii")) + if not isinstance(data, bytes): + raise ValueError("Not bytes") + except (UnicodeError, ValueError) as e: + raise PyboardError("fs_get: Could not interpret received data: %s" % str(e)) if not data: break f.write(data) From 492cf34fd860330f44a74732d89e9db8bc02a4e8 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Thu, 13 Aug 2020 15:20:08 +0200 Subject: [PATCH 207/352] tools/mpy-tool.py: Fix offset of line number info. Signed-off-by: Martin Milata --- tools/mpy-tool.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 43d35503eb564..1ac6c93d7d8b2 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -253,6 +253,7 @@ def __init__(self, code_kind, bytecode, prelude_offset, qstrs, objs, raw_codes): self.ip, self.ip2, self.prelude = extract_prelude(self.bytecode, self.prelude_offset) self.simple_name = self._unpack_qstr(self.ip2) self.source_file = self._unpack_qstr(self.ip2 + 2) + self.line_info_offset = self.ip2 + 4 def _unpack_qstr(self, ip): qst = self.bytecode[ip] | self.bytecode[ip + 1] << 8 @@ -404,7 +405,10 @@ def freeze_module(self, qstr_links=(), type_sig=0): print(" .n_def_pos_args = %u," % self.prelude[5]) print(" .qstr_block_name = %s," % self.simple_name.qstr_id) print(" .qstr_source_file = %s," % self.source_file.qstr_id) - print(" .line_info = fun_data_%s + %u," % (self.escaped_name, 0)) # TODO + print( + " .line_info = fun_data_%s + %u," + % (self.escaped_name, self.line_info_offset) + ) print(" .opcodes = fun_data_%s + %u," % (self.escaped_name, self.ip)) print(" },") print(" .line_of_definition = %u," % 0) # TODO From 448319a7450714efd72ae527e184a247a82ee39d Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 22 Aug 2020 01:07:57 +1000 Subject: [PATCH 208/352] tools/makemanifest.py: Use os.makedirs to make path for generated files. The existing implementation of mkdir() in this file is not sophisticated enough to work correctly on all operating systems (eg Mac can raise EISDIR). Using the standard os.makedirs() function handles all cases correctly. Signed-off-by: Damien George --- tools/makemanifest.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index b7d4a4d4a68af..377f245596fe9 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -25,7 +25,6 @@ # THE SOFTWARE. from __future__ import print_function -import errno import sys import os import subprocess @@ -165,17 +164,10 @@ def get_timestamp_newest(path): return ts_newest -def mkdir(path): - cur_path = "" - for p in path.split("/")[:-1]: - cur_path += p + "/" - try: - os.mkdir(cur_path) - except OSError as er: - if er.args[0] == errno.EEXIST: - pass - else: - raise er +def mkdir(filename): + path = os.path.dirname(filename) + if not os.path.isdir(path): + os.makedirs(path) def freeze_internal(kind, path, script, opt): From 5f9b105244bb9f605f3ca157cd421967c665bd6e Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Aug 2020 10:30:09 +1000 Subject: [PATCH 209/352] py/runtime: Fix builtin compile() in "single" mode so it prints exprs. As per CPython behaviour, compile(stmt, "file", "single") should create code which prints to stdout (via __repl_print__) the results of any expressions in stmt. Signed-off-by: Damien George --- py/runtime.c | 2 +- tests/basics/builtin_compile.py | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 38da2f4538e97..c12271f4e2cf5 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1473,7 +1473,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i if (nlr_push(&nlr) == 0) { qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT); mp_obj_t ret; if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { diff --git a/tests/basics/builtin_compile.py b/tests/basics/builtin_compile.py index bf272aca15d43..a2f2cbe550131 100644 --- a/tests/basics/builtin_compile.py +++ b/tests/basics/builtin_compile.py @@ -1,11 +1,10 @@ # test compile builtin -def have_compile(): - try: - compile - return True - except NameError: - return False +try: + compile +except NameError: + print("SKIP") + raise SystemExit def test(): global x @@ -26,8 +25,9 @@ def test(): exec(c, {}, {"x":3}) # single/eval mode - exec(compile('print(1 + 1)', 'file', 'single')) - print(eval(compile('1 + 1', 'file', 'eval'))) + exec(compile("if 1: 10 + 1\n", "file", "single")) + exec(compile("print(10 + 2)", "file", "single")) + print(eval(compile("10 + 3", "file", "eval"))) # bad mode try: @@ -42,8 +42,4 @@ def test(): print("NameError") print(x) # check 'x' still exists as a global -if have_compile(): - test() -else: - print("SKIP") - raise SystemExit +test() From 20948a3d54ff560a3d029b8bfc9761dcbbad9312 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Aug 2020 23:11:44 +1000 Subject: [PATCH 210/352] tests/extmod: Add test for uasyncio.sleep of a negative time. It should take 0 time to await on a negative sleep. Signed-off-by: Damien George --- tests/extmod/uasyncio_basic.py | 12 ++++++++++-- tests/extmod/uasyncio_basic.py.exp | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/extmod/uasyncio_basic.py b/tests/extmod/uasyncio_basic.py index f6685fa674676..c88908d99b320 100644 --- a/tests/extmod/uasyncio_basic.py +++ b/tests/extmod/uasyncio_basic.py @@ -36,8 +36,16 @@ async def main(): t1 = ticks() await delay_print(0.04, "long") t2 = ticks() - - print("took {} {}".format(round(ticks_diff(t1, t0), -1), round(ticks_diff(t2, t1), -1))) + await delay_print(-1, "negative") + t3 = ticks() + + print( + "took {} {} {}".format( + round(ticks_diff(t1, t0), -1), + round(ticks_diff(t2, t1), -1), + round(ticks_diff(t3, t2), -1), + ) + ) asyncio.run(main()) diff --git a/tests/extmod/uasyncio_basic.py.exp b/tests/extmod/uasyncio_basic.py.exp index 447d05d5e0984..6673978769fbf 100644 --- a/tests/extmod/uasyncio_basic.py.exp +++ b/tests/extmod/uasyncio_basic.py.exp @@ -2,4 +2,5 @@ start after sleep short long -took 20 40 +negative +took 20 40 0 From 55c76eaac12af4e93f8de11bb25c835e8b65c623 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Aug 2020 23:13:25 +1000 Subject: [PATCH 211/352] extmod/uasyncio: Truncate negative sleeps to 0. Otherwise a task that continuously awaits on a large negative sleep can monopolise the scheduler (because its wake time is always less than everything else in the pairing heap). Signed-off-by: Damien George --- extmod/uasyncio/core.py | 2 +- tests/extmod/uasyncio_fair.py | 1 + tests/extmod/uasyncio_fair.py.exp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index 689487d36a6fd..045b4cd139182 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -53,7 +53,7 @@ def __next__(self): # Use a SingletonGenerator to do it without allocating on the heap def sleep_ms(t, sgen=SingletonGenerator()): assert sgen.state is None - sgen.state = ticks_add(ticks(), t) + sgen.state = ticks_add(ticks(), max(0, t)) return sgen diff --git a/tests/extmod/uasyncio_fair.py b/tests/extmod/uasyncio_fair.py index 9b04454bc1f50..e0ee811a9b046 100644 --- a/tests/extmod/uasyncio_fair.py +++ b/tests/extmod/uasyncio_fair.py @@ -22,6 +22,7 @@ async def main(): t1 = asyncio.create_task(task(1, -0.01)) t2 = asyncio.create_task(task(2, 0.1)) t3 = asyncio.create_task(task(3, 0.2)) + t3 = asyncio.create_task(task(4, -100)) await asyncio.sleep(0.5) t1.cancel() t2.cancel() diff --git a/tests/extmod/uasyncio_fair.py.exp b/tests/extmod/uasyncio_fair.py.exp index 4428943f46287..b4b6481db019f 100644 --- a/tests/extmod/uasyncio_fair.py.exp +++ b/tests/extmod/uasyncio_fair.py.exp @@ -3,6 +3,7 @@ task start 2 task work 2 task start 3 task work 3 +task start 4 task work 2 task work 3 task work 2 From 92899354d96a5b5463f3002b2476a73c11ebe626 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Jul 2020 01:01:31 +1000 Subject: [PATCH 212/352] unix/fatfs_port: Implement get_fattime. Signed-off-by: Damien George --- ports/unix/fatfs_port.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ports/unix/fatfs_port.c b/ports/unix/fatfs_port.c index 30f1959f52063..4ec5b919ab54b 100644 --- a/ports/unix/fatfs_port.c +++ b/ports/unix/fatfs_port.c @@ -1,5 +1,13 @@ +#include #include "lib/oofatfs/ff.h" DWORD get_fattime(void) { - return 0; + time_t now = time(NULL); + struct tm *tm = localtime(&now); + return ((1900 + tm->tm_year - 1980) << 25) + | (tm->tm_mon << 21) + | (tm->tm_mday << 16) + | (tm->tm_hour << 11) + | (tm->tm_min << 5) + | (tm->tm_sec / 2); } From badd351150df70bea6644db98f48fbc3a1e5ffea Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 2 Aug 2020 00:32:38 +1000 Subject: [PATCH 213/352] lib/timeutils: Add helper functions to deal with nanosecs since 1970. Signed-off-by: Damien George --- lib/timeutils/timeutils.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/timeutils/timeutils.h b/lib/timeutils/timeutils.h index cb7a72123a119..08b0dc2e8559a 100644 --- a/lib/timeutils/timeutils.h +++ b/lib/timeutils/timeutils.h @@ -27,6 +27,10 @@ #ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H +// The number of seconds between 1970/1/1 and 2000/1/1 is calculated using: +// time.mktime((2000,1,1,0,0,0,0,0,0)) - time.mktime((1970,1,1,0,0,0,0,0,0)) +#define TIMEUTILS_SECONDS_1970_TO_2000 (946684800ULL) + typedef struct _timeutils_struct_time_t { uint16_t tm_year; // i.e. 2014 uint8_t tm_mon; // 1..12 @@ -38,6 +42,14 @@ typedef struct _timeutils_struct_time_t { uint16_t tm_yday; // 1..366 } timeutils_struct_time_t; +static inline uint64_t timeutils_seconds_since_2000_to_nanoseconds_since_1970(mp_uint_t s) { + return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; +} + +static inline mp_uint_t timeutils_seconds_since_2000_from_nanoseconds_since_1970(uint64_t ns) { + return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; +} + bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); @@ -51,4 +63,10 @@ mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); +static inline uint64_t timeutils_nanoseconds_since_1970(mp_uint_t year, mp_uint_t month, + mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_2000_to_nanoseconds_since_1970( + timeutils_seconds_since_2000(year, month, date, hour, minute, second)); +} + #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H From ee50a6effebf315c38d4a129dc9f65ee722eb5b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 1 Aug 2020 23:50:23 +1000 Subject: [PATCH 214/352] py/mphal.h: Introduce mp_hal_time_ns and implement on various ports. This should return a 64-bit value being the number of nanoseconds since 1970/1/1. Signed-off-by: Damien George --- ports/esp32/mphalport.c | 7 +++++++ ports/esp8266/esp_mphal.c | 4 ++++ ports/stm32/rtc.c | 18 ++++++++++++++++++ ports/unix/unix_mphal.c | 5 +++++ ports/zephyr/mphalport.h | 5 +++++ py/mphal.h | 6 ++++++ 6 files changed, 45 insertions(+) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 99548ad627be5..5e526df6673cb 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -28,6 +28,7 @@ #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -195,6 +196,12 @@ void mp_hal_delay_us(uint32_t us) { } } +uint64_t mp_hal_time_ns(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; +} + // Wake up the main task if it is sleeping void mp_hal_wake_main_task_from_isr(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index c467ab72db7ec..9e1b72e438ad7 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -138,6 +138,10 @@ void MP_FASTCODE(mp_hal_delay_ms)(uint32_t delay) { mp_hal_delay_us(delay * 1000); } +uint64_t mp_hal_time_ns(void) { + return pyb_rtc_get_us_since_2000() * 1000ULL; +} + void ets_event_poll(void) { ets_loop_iter(); mp_handle_pending(true); diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index de26352004771..4f759c4bc74f0 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -27,6 +27,7 @@ #include #include "py/runtime.h" +#include "lib/timeutils/timeutils.h" #include "extint.h" #include "rtc.h" #include "irq.h" @@ -442,6 +443,23 @@ STATIC void RTC_CalendarConfig(void) { } } +uint64_t mp_hal_time_ns(void) { + uint64_t ns = 0; + #if MICROPY_HW_ENABLE_RTC + // Get current according to the RTC. + rtc_init_finalise(); + RTC_TimeTypeDef time; + RTC_DateTypeDef date; + HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN); + HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN); + ns = timeutils_nanoseconds_since_1970( + 2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds); + uint32_t usec = ((RTC_SYNCH_PREDIV - time.SubSeconds) * (1000000 / 64)) / ((RTC_SYNCH_PREDIV + 1) / 64); + ns += usec * 1000; + #endif + return ns; +} + /******************************************************************************/ // MicroPython bindings diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 42c22f7b50482..f43067f646ba9 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -214,3 +214,8 @@ mp_uint_t mp_hal_ticks_us(void) { return tv.tv_sec * 1000000 + tv.tv_usec; #endif } + +uint64_t mp_hal_time_ns(void) { + time_t now = time(NULL); + return (uint64_t)now * 1000000000ULL; +} diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h index 8434a388b0a5e..9ef213ddb0809 100644 --- a/ports/zephyr/mphalport.h +++ b/ports/zephyr/mphalport.h @@ -24,6 +24,11 @@ static inline void mp_hal_delay_ms(mp_uint_t delay) { k_msleep(delay); } +static inline uint64_t mp_hal_time_ns(void) { + // Not currently implemented. + return 0; +} + #define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us)) #define mp_hal_pin_od_low(p) (mp_raise_NotImplementedError("mp_hal_pin_od_low")) #define mp_hal_pin_od_high(p) (mp_raise_NotImplementedError("mp_hal_pin_od_high")) diff --git a/py/mphal.h b/py/mphal.h index 66d80705a60ee..6d11f6ddc0836 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_PY_MPHAL_H #define MICROPY_INCLUDED_PY_MPHAL_H +#include #include "py/mpconfig.h" #ifdef MICROPY_MPHALPORT_H @@ -74,6 +75,11 @@ mp_uint_t mp_hal_ticks_us(void); mp_uint_t mp_hal_ticks_cpu(void); #endif +#ifndef mp_hal_time_ns +// Nanoseconds since 1970/1/1. +uint64_t mp_hal_time_ns(void); +#endif + // If port HAL didn't define its own pin API, use generic // "virtual pin" API from the core. #ifndef mp_hal_pin_obj_t From 2acc087880de39d7e17abc9344b8d2faba3478dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Jul 2020 01:01:48 +1000 Subject: [PATCH 215/352] extmod/vfs_lfs: Add mtime support to littlefs files. This commit adds support for modification time of files on littlefs v2 filesystems, using file attributes. For some background see issue #6114. Features/properties of this implementation: - Only supported on littlefs2 (not littlefs1). - Uses littlefs2's general file attributes to store the timestamp. - The timestamp is 64-bits and stores nanoseconds since 1970/1/1 (if the range to the year 2554 is not enough then additional bits can be added to this timestamp by adding another file attribute). - mtime is enabled by default but can be disabled in the constructor, eg: uos.mount(uos.VfsLfs2(bdev, mtime=False), '/flash') - It's fully backwards compatible, existing littlefs2 filesystems will work without reformatting and timestamps will be added transparently to existing files (once they are opened for writing). - Files without timestamps will open correctly, and stat will just return 0 for their timestamp. - mtime can be disabled or enabled each mount time and timestamps will only be updated if mtime is enabled (otherwise they will be untouched). Signed-off-by: Damien George --- docs/library/uos.rst | 16 +++-- extmod/vfs_lfs.c | 21 ++++++- extmod/vfs_lfsx.c | 25 ++++++-- extmod/vfs_lfsx_file.c | 18 +++++- tests/extmod/vfs_lfs.py | 13 ++-- tests/extmod/vfs_lfs.py.exp | 16 ++--- tests/extmod/vfs_lfs_mtime.py | 98 +++++++++++++++++++++++++++++++ tests/extmod/vfs_lfs_mtime.py.exp | 13 ++++ 8 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 tests/extmod/vfs_lfs_mtime.py create mode 100644 tests/extmod/vfs_lfs_mtime.py.exp diff --git a/docs/library/uos.rst b/docs/library/uos.rst index 051247b717419..edc94556b1722 100644 --- a/docs/library/uos.rst +++ b/docs/library/uos.rst @@ -178,7 +178,7 @@ represented by VFS classes. Build a FAT filesystem on *block_dev*. -.. class:: VfsLfs1(block_dev) +.. class:: VfsLfs1(block_dev, readsize=32, progsize=32, lookahead=32) Create a filesystem object that uses the `littlefs v1 filesystem format`_. Storage of the littlefs filesystem is provided by *block_dev*, which must @@ -187,23 +187,31 @@ represented by VFS classes. See :ref:`filesystem` for more information. - .. staticmethod:: mkfs(block_dev) + .. staticmethod:: mkfs(block_dev, readsize=32, progsize=32, lookahead=32) Build a Lfs1 filesystem on *block_dev*. .. note:: There are reports of littlefs v1 failing in certain situations, for details see `littlefs issue 347`_. -.. class:: VfsLfs2(block_dev) +.. class:: VfsLfs2(block_dev, readsize=32, progsize=32, lookahead=32, mtime=True) Create a filesystem object that uses the `littlefs v2 filesystem format`_. Storage of the littlefs filesystem is provided by *block_dev*, which must support the :ref:`extended interface `. Objects created by this constructor can be mounted using :func:`mount`. + The *mtime* argument enables modification timestamps for files, stored using + littlefs attributes. This option can be disabled or enabled differently each + mount time and timestamps will only be added or updated if *mtime* is enabled, + otherwise the timestamps will remain untouched. Littlefs v2 filesystems without + timestamps will work without reformatting and timestamps will be added + transparently to existing files once they are opened for writing. When *mtime* + is enabled `uos.stat` on files without timestamps will return 0 for the timestamp. + See :ref:`filesystem` for more information. - .. staticmethod:: mkfs(block_dev) + .. staticmethod:: mkfs(block_dev, readsize=32, progsize=32, lookahead=32) Build a Lfs2 filesystem on *block_dev*. diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index 90a1996f9ce4a..a53f66f2d63c8 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -25,18 +25,20 @@ */ #include "py/runtime.h" +#include "py/mphal.h" #include "extmod/vfs.h" #include "extmod/vfs_lfs.h" #if MICROPY_VFS && (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) -enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead }; +enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead, LFS_MAKE_ARG_mtime }; static const mp_arg_t lfs_make_allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_readsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_progsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_lookahead, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, + { MP_QSTR_mtime, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, }; #if MICROPY_VFS_LFS1 @@ -98,9 +100,13 @@ mp_obj_t mp_vfs_lfs1_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode #define MP_TYPE_VFS_LFSx mp_type_vfs_lfs2 #define MP_TYPE_VFS_LFSx_(s) mp_type_vfs_lfs2##s +// Attribute ids for lfs2_attr.type. +#define LFS_ATTR_MTIME (1) // 64-bit little endian, nanoseconds since 1970/1/1 + typedef struct _mp_obj_vfs_lfs2_t { mp_obj_base_t base; mp_vfs_blockdev_t blockdev; + bool enable_mtime; vstr_t cur_dir; struct lfs2_config config; lfs2_t lfs; @@ -109,14 +115,25 @@ typedef struct _mp_obj_vfs_lfs2_t { typedef struct _mp_obj_vfs_lfs2_file_t { mp_obj_base_t base; mp_obj_vfs_lfs2_t *vfs; + uint8_t mtime[8]; lfs2_file_t file; struct lfs2_file_config cfg; + struct lfs2_attr attrs[1]; uint8_t file_buffer[0]; } mp_obj_vfs_lfs2_file_t; const char *mp_vfs_lfs2_make_path(mp_obj_vfs_lfs2_t *self, mp_obj_t path_in); mp_obj_t mp_vfs_lfs2_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in); +STATIC void lfs_get_mtime(uint8_t buf[8]) { + uint64_t ns = mp_hal_time_ns(); + // Store "ns" to "buf" in little-endian format (essentially htole64). + for (size_t i = 0; i < 8; ++i) { + buf[i] = ns; + ns >>= 8; + } +} + #include "extmod/vfs_lfsx.c" #include "extmod/vfs_lfsx_file.c" diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 24816433be015..511b741b0dec1 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -34,6 +34,7 @@ #include "py/objstr.h" #include "py/mperrno.h" #include "extmod/vfs.h" +#include "lib/timeutils/timeutils.h" STATIC int MP_VFS_LFSx(dev_ioctl)(const struct LFSx_API (config) * c, int cmd, int arg, bool must_return_int) { mp_obj_t ret = mp_vfs_blockdev_ioctl(c->context, cmd, arg); @@ -120,6 +121,9 @@ STATIC mp_obj_t MP_VFS_LFSx(make_new)(const mp_obj_type_t * type, size_t n_args, self->base.type = type; vstr_init(&self->cur_dir, 16); vstr_add_byte(&self->cur_dir, '/'); + #if LFS_BUILD_VERSION == 2 + self->enable_mtime = args[LFS_MAKE_ARG_mtime].u_bool; + #endif MP_VFS_LFSx(init_config)(self, args[LFS_MAKE_ARG_bdev].u_obj, args[LFS_MAKE_ARG_readsize].u_int, args[LFS_MAKE_ARG_progsize].u_int, args[LFS_MAKE_ARG_lookahead].u_int); int ret = LFSx_API(mount)(&self->lfs, &self->config); @@ -352,6 +356,19 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { mp_raise_OSError(-ret); } + mp_uint_t mtime = 0; + #if LFS_BUILD_VERSION == 2 + uint8_t mtime_buf[8]; + lfs2_ssize_t sz = lfs2_getattr(&self->lfs, path, LFS_ATTR_MTIME, &mtime_buf, sizeof(mtime_buf)); + if (sz == sizeof(mtime_buf)) { + uint64_t ns = 0; + for (size_t i = sizeof(mtime_buf); i > 0; --i) { + ns = ns << 8 | mtime_buf[i - 1]; + } + mtime = timeutils_seconds_since_2000_from_nanoseconds_since_1970(ns); + } + #endif + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(info.type == LFSx_MACRO(_TYPE_REG) ? MP_S_IFREG : MP_S_IFDIR); // st_mode t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino @@ -360,9 +377,9 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size - t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(0); // st_ctime + t->items[7] = MP_OBJ_NEW_SMALL_INT(mtime); // st_atime + t->items[8] = MP_OBJ_NEW_SMALL_INT(mtime); // st_mtime + t->items[9] = MP_OBJ_NEW_SMALL_INT(mtime); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index f74b41837d32d..bc1a37b90bdb8 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -101,6 +101,17 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod #endif o->cfg.buffer = &o->file_buffer[0]; + #if LFS_BUILD_VERSION == 2 + if (self->enable_mtime) { + lfs_get_mtime(&o->mtime[0]); + o->attrs[0].type = LFS_ATTR_MTIME; + o->attrs[0].buffer = &o->mtime[0]; + o->attrs[0].size = sizeof(o->mtime); + o->cfg.attrs = &o->attrs[0]; + o->cfg.attr_count = MP_ARRAY_SIZE(o->attrs); + } + #endif + const char *path = MP_VFS_LFSx(make_path)(self, path_in); int ret = LFSx_API(file_opencfg)(&self->lfs, &o->file, path, flags, &o->cfg); if (ret < 0) { @@ -131,6 +142,11 @@ STATIC mp_uint_t MP_VFS_LFSx(file_read)(mp_obj_t self_in, void *buf, mp_uint_t s STATIC mp_uint_t MP_VFS_LFSx(file_write)(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { MP_OBJ_VFS_LFSx_FILE *self = MP_OBJ_TO_PTR(self_in); MP_VFS_LFSx(check_open)(self); + #if LFS_BUILD_VERSION == 2 + if (self->vfs->enable_mtime) { + lfs_get_mtime(&self->mtime[0]); + } + #endif LFSx_API(ssize_t) sz = LFSx_API(file_write)(&self->vfs->lfs, &self->file, buf, size); if (sz < 0) { *errcode = -sz; diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py index 609d9f949e17d..8e56400df3c2b 100644 --- a/tests/extmod/vfs_lfs.py +++ b/tests/extmod/vfs_lfs.py @@ -35,6 +35,11 @@ def ioctl(self, op, arg): return 0 +def print_stat(st, print_size=True): + # don't print times (just check that they have the correct type) + print(st[:6], st[6] if print_size else -1, type(st[7]), type(st[8]), type(st[9])) + + def test(bdev, vfs_class): print("test", vfs_class) @@ -69,10 +74,10 @@ def test(bdev, vfs_class): vfs.mkdir("testdir") # stat a file - print(vfs.stat("test")) + print_stat(vfs.stat("test")) # stat a dir (size seems to vary on LFS2 so don't print that) - print(vfs.stat("testdir")[:6]) + print_stat(vfs.stat("testdir"), False) # read with vfs.open("test", "r") as f: @@ -112,8 +117,8 @@ def test(bdev, vfs_class): # create file in directory to make sure paths are relative vfs.open("test2", "w").close() - print(vfs.stat("test2")) - print(vfs.stat("/testdir/test2")) + print_stat(vfs.stat("test2")) + print_stat(vfs.stat("/testdir/test2")) vfs.remove("test2") # chdir back to root and remove testdir diff --git a/tests/extmod/vfs_lfs.py.exp b/tests/extmod/vfs_lfs.py.exp index a7025577449d0..a0450c84b79f8 100644 --- a/tests/extmod/vfs_lfs.py.exp +++ b/tests/extmod/vfs_lfs.py.exp @@ -7,8 +7,8 @@ test [('test', 32768, 0, 8), ('testdir', 16384, 0, 0)] [] [('test', 32768, 0, 8)] -(32768, 0, 0, 0, 0, 0, 8, 0, 0, 0) -(16384, 0, 0, 0, 0, 0) +(32768, 0, 0, 0, 0, 0) 8 +(16384, 0, 0, 0, 0, 0) -1 littlefs data length: 4096 write 0 @@ -22,8 +22,8 @@ write 3 [('test', 32768, 0, 8), ('testdir', 16384, 0, 0)] / /testdir -(32768, 0, 0, 0, 0, 0, 0, 0, 0, 0) -(32768, 0, 0, 0, 0, 0, 0, 0, 0, 0) +(32768, 0, 0, 0, 0, 0) 0 +(32768, 0, 0, 0, 0, 0) 0 / /testdir / @@ -44,8 +44,8 @@ test [('testdir', 16384, 0, 0), ('test', 32768, 0, 8)] [] [('test', 32768, 0, 8)] -(32768, 0, 0, 0, 0, 0, 8, 0, 0, 0) -(16384, 0, 0, 0, 0, 0) +(32768, 0, 0, 0, 0, 0) 8 +(16384, 0, 0, 0, 0, 0) -1 littlefs data length: 4096 write 0 @@ -59,8 +59,8 @@ write 3 [('test', 32768, 0, 8), ('testdir', 16384, 0, 0)] / /testdir -(32768, 0, 0, 0, 0, 0, 0, 0, 0, 0) -(32768, 0, 0, 0, 0, 0, 0, 0, 0, 0) +(32768, 0, 0, 0, 0, 0) 0 +(32768, 0, 0, 0, 0, 0) 0 / /testdir / diff --git a/tests/extmod/vfs_lfs_mtime.py b/tests/extmod/vfs_lfs_mtime.py new file mode 100644 index 0000000000000..0108076884055 --- /dev/null +++ b/tests/extmod/vfs_lfs_mtime.py @@ -0,0 +1,98 @@ +# Test for VfsLfs using a RAM device, mtime feature + +try: + import utime, uos + + utime.sleep + uos.VfsLfs2 +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + ERASE_BLOCK_SIZE = 1024 + + def __init__(self, blocks): + self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) + + def readblocks(self, block, buf, off): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def writeblocks(self, block, buf, off): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + self.data[addr + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.ERASE_BLOCK_SIZE + if op == 5: # block size + return self.ERASE_BLOCK_SIZE + if op == 6: # erase block + return 0 + + +def test(bdev, vfs_class): + print("test", vfs_class) + + # Initial format of block device. + vfs_class.mkfs(bdev) + + # construction + print("mtime=True") + vfs = vfs_class(bdev, mtime=True) + + # Create an empty file, should have a timestamp. + vfs.open("test1", "wt").close() + + # Wait 1 second so mtime will increase by at least 1. + utime.sleep(1) + + # Create another empty file, should have a timestamp. + vfs.open("test2", "wt").close() + + # Stat the files and check that test1 is older than test2. + stat1 = vfs.stat("test1") + stat2 = vfs.stat("test2") + print(stat1[8] != 0, stat2[8] != 0) + print(stat1[8] < stat2[8]) + + # Wait 1 second so mtime will increase by at least 1. + utime.sleep(1) + + # Open test1 for reading and ensure mtime did not change. + vfs.open("test1", "rt").close() + print(vfs.stat("test1") == stat1) + + # Open test1 for writing and ensure mtime increased from the previous value. + vfs.open("test1", "wt").close() + stat1_old = stat1 + stat1 = vfs.stat("test1") + print(stat1_old[8] < stat1[8]) + + # Unmount. + vfs.umount() + + # Check that remounting with mtime=False can read the timestamps. + print("mtime=False") + vfs = vfs_class(bdev, mtime=False) + print(vfs.stat("test1") == stat1) + print(vfs.stat("test2") == stat2) + f = vfs.open("test1", "wt") + f.close() + print(vfs.stat("test1") == stat1) + vfs.umount() + + # Check that remounting with mtime=True still has the timestamps. + print("mtime=True") + vfs = vfs_class(bdev, mtime=True) + print(vfs.stat("test1") == stat1) + print(vfs.stat("test2") == stat2) + vfs.umount() + + +bdev = RAMBlockDevice(30) +test(bdev, uos.VfsLfs2) diff --git a/tests/extmod/vfs_lfs_mtime.py.exp b/tests/extmod/vfs_lfs_mtime.py.exp new file mode 100644 index 0000000000000..2e3d7491bcbd8 --- /dev/null +++ b/tests/extmod/vfs_lfs_mtime.py.exp @@ -0,0 +1,13 @@ +test +mtime=True +True True +True +True +True +mtime=False +True +True +True +mtime=True +True +True From 3d9a7ed02f4e50ee2eb541ac2f4d7f6f5b5d316e Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:43:37 +1000 Subject: [PATCH 216/352] extmod/btstack: Implement GAP scan duration_ms parameter. This commit makes scanning work when duration_ms is set to zero. Prior to this it would not work with duration_ms set to zero. --- extmod/btstack/modbluetooth_btstack.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index c7269d33ed5ca..89e396dfd08e7 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -889,7 +889,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC btstack_timer_source_t scan_duration_timeout; -STATIC void hci_initialization_timeout_handler(btstack_timer_source_t *ds) { +STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) { (void)ds; mp_bluetooth_gap_scan_stop(); } @@ -897,9 +897,11 @@ STATIC void hci_initialization_timeout_handler(btstack_timer_source_t *ds) { int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); - btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); - btstack_run_loop_set_timer_handler(&scan_duration_timeout, hci_initialization_timeout_handler); - btstack_run_loop_add_timer(&scan_duration_timeout); + if (duration_ms > 0) { + btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); + btstack_run_loop_set_timer_handler(&scan_duration_timeout, scan_duration_timeout_handler); + btstack_run_loop_add_timer(&scan_duration_timeout); + } // 0 = passive scan (we don't handle scan response). gap_set_scan_parameters(0, interval_us / 625, window_us / 625); From 0bc2c1c1057d7f5c1e4987139062386a8f9fe5f2 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 17 Aug 2020 10:21:27 +1000 Subject: [PATCH 217/352] extmod/modbluetooth: Fix race between READ_REQUEST and other IRQs. The READ_REQUEST callback is handled as a hard interrupt (because the BLE stack needs an immediate response from it so it can continue) and so calls to Python require extra protection: - the caller-owned tuple passed into the callback must be separate from the tuple used by other callback events (which are soft interrupts); - the GC and scheduler must be locked during callback execution. --- extmod/modbluetooth.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 7bfb774782048..730c288ee7885 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -26,14 +26,15 @@ */ #include "py/binary.h" +#include "py/gc.h" #include "py/misc.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "py/obj.h" -#include "py/objstr.h" #include "py/objarray.h" +#include "py/objstr.h" #include "py/qstr.h" #include "py/runtime.h" -#include "py/mphal.h" #include "extmod/modbluetooth.h" #include @@ -66,6 +67,9 @@ typedef struct { mp_obj_str_t irq_data_data; mp_obj_bluetooth_uuid_t irq_data_uuid; ringbuf_t ringbuf; + #if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK + mp_obj_t irq_read_request_data_tuple; + #endif } mp_obj_bluetooth_ble_t; // TODO: this seems like it could be generic? @@ -252,6 +256,11 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, // Pre-allocate the event data tuple to prevent needing to allocate in the IRQ handler. o->irq_data_tuple = mp_obj_new_tuple(MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN, NULL); + #if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK + // Pre-allocate a separate data tuple for the read request "hard" irq. + o->irq_read_request_data_tuple = mp_obj_new_tuple(2, NULL); + #endif + // Pre-allocated buffers for address, payload and uuid. o->irq_data_addr.base.type = &mp_type_bytes; o->irq_data_addr.data = o->irq_data_addr_bytes; @@ -1139,12 +1148,22 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) { mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); if (o->irq_handler != mp_const_none) { - // Use pre-allocated tuple because this is a hard IRQ. - mp_obj_tuple_t *data = MP_OBJ_TO_PTR(o->irq_data_tuple); + // 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(); + + // Use pre-allocated tuple distinct to the one used by the "soft" IRQs. + mp_obj_tuple_t *data = MP_OBJ_TO_PTR(o->irq_read_request_data_tuple); data->items[0] = MP_OBJ_NEW_SMALL_INT(conn_handle); data->items[1] = MP_OBJ_NEW_SMALL_INT(value_handle); data->len = 2; - mp_obj_t irq_ret = mp_call_function_2_protected(o->irq_handler, MP_OBJ_NEW_SMALL_INT(MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST), o->irq_data_tuple); + mp_obj_t irq_ret = mp_call_function_2_protected(o->irq_handler, MP_OBJ_NEW_SMALL_INT(MP_BLUETOOTH_IRQ_GATTS_READ_REQUEST), o->irq_read_request_data_tuple); + + gc_unlock(); + mp_sched_unlock(); + // If the IRQ handler explicitly returned false, then deny the read. Otherwise if it returns None/True, allow it. return irq_ret != MP_OBJ_NULL && (irq_ret == mp_const_none || mp_obj_is_true(irq_ret)); } else { From a80a146858b3c9a7fbae4030a524666ab19f7a47 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 14 Aug 2020 11:49:41 +1000 Subject: [PATCH 218/352] extmod/bluetooth: Support active scanning in BLE.gap_scan(). This adds an additional optional parameter to gap_scan() to select active scanning, where scan responses are returned as well as normal scan results. This parameter is False by default which retains the existing behaviour. --- docs/library/ubluetooth.rst | 4 +++- extmod/btstack/modbluetooth_btstack.c | 5 ++--- extmod/modbluetooth.c | 8 ++++++-- extmod/modbluetooth.h | 2 +- extmod/nimble/modbluetooth_nimble.c | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index e103932efd2f6..0cac16f5f4ad9 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -204,7 +204,7 @@ Broadcaster Role (Advertiser) Observer Role (Scanner) ----------------------- -.. method:: BLE.gap_scan(duration_ms, [interval_us], [window_us]) +.. method:: BLE.gap_scan(duration_ms, [interval_us], [window_us], [active]) Run a scan operation lasting for the specified duration (in **milli**\ seconds). @@ -228,6 +228,8 @@ Observer Role (Scanner) * 0x03 - ADV_NONCONN_IND - non-connectable undirected advertising * 0x04 - SCAN_RSP - scan response + ``active`` can be set ``True`` if you want to receive scan responses in the results. + When scanning is stopped (either due to the duration finishing or when explicitly stopped), the ``_IRQ_SCAN_DONE`` event will be raised. diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 89e396dfd08e7..8f0c82974c316 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -894,7 +894,7 @@ STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) { mp_bluetooth_gap_scan_stop(); } -int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { +int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); if (duration_ms > 0) { @@ -903,8 +903,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ btstack_run_loop_add_timer(&scan_duration_timeout); } - // 0 = passive scan (we don't handle scan response). - gap_set_scan_parameters(0, interval_us / 625, window_us / 625); + gap_set_scan_parameters(active_scan ? 1 : 0, interval_us / 625, window_us / 625); gap_start_scan(); return 0; diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 730c288ee7885..82efe49385377 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -609,6 +609,7 @@ STATIC mp_obj_t bluetooth_ble_gap_scan(size_t n_args, const mp_obj_t *args) { mp_int_t duration_ms = 0; mp_int_t interval_us = 1280000; mp_int_t window_us = 11250; + bool active_scan = false; if (n_args > 1) { if (args[1] == mp_const_none) { // scan(None) --> stop scan. @@ -619,12 +620,15 @@ STATIC mp_obj_t bluetooth_ble_gap_scan(size_t n_args, const mp_obj_t *args) { interval_us = mp_obj_get_int(args[2]); if (n_args > 3) { window_us = mp_obj_get_int(args[3]); + if (n_args > 4) { + active_scan = mp_obj_is_true(args[4]); + } } } } - return bluetooth_handle_errno(mp_bluetooth_gap_scan_start(duration_ms, interval_us, window_us)); + return bluetooth_handle_errno(mp_bluetooth_gap_scan_start(duration_ms, interval_us, window_us, active_scan)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gap_scan_obj, 1, 4, bluetooth_ble_gap_scan); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gap_scan_obj, 1, 5, bluetooth_ble_gap_scan); #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC mp_obj_t bluetooth_ble_gap_disconnect(mp_obj_t self_in, mp_obj_t conn_handle_in) { diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index cdb86e5e63c44..2c07683124232 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -215,7 +215,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Start a discovery (scan). Set duration to zero to run continuously. -int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us); +int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan); // Stop discovery (if currently active). int mp_bluetooth_gap_scan_stop(void); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 843989b2b8986..e51f2747ec5c8 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -661,7 +661,7 @@ STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) { return 0; } -int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us) { +int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -673,7 +673,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ .window = MAX(BLE_HCI_SCAN_WINDOW_MIN, MIN(BLE_HCI_SCAN_WINDOW_MAX, window_us / BLE_HCI_SCAN_ITVL)), .filter_policy = BLE_HCI_CONN_FILT_NO_WL, .limited = 0, - .passive = 1, // TODO: Handle BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP in gap_scan_cb above. + .passive = active_scan ? 0 : 1, .filter_duplicates = 0, }; int err = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, duration_ms, &discover_params, gap_scan_cb, NULL); From 5fb276de33c238d0a56696c701b8274d070684d8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 26 Aug 2020 16:02:39 +1000 Subject: [PATCH 219/352] tests/extmod: Make uasyncio_fair test more reliable by adjusting sleeps. With sleep(0.2) a multiple of sleep(0.1), the order of task 2 and 3 execution is not well defined, and depends on the precision of the system clock and how fast the rest of the code runs. So change 0.2 to 0.18 to make the test more reliable. Also fix a typo of t3/t4, and cancel t4 at the end. Signed-off-by: Damien George --- tests/extmod/uasyncio_fair.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/extmod/uasyncio_fair.py b/tests/extmod/uasyncio_fair.py index e0ee811a9b046..e2257060548bb 100644 --- a/tests/extmod/uasyncio_fair.py +++ b/tests/extmod/uasyncio_fair.py @@ -21,12 +21,13 @@ async def task(id, t): async def main(): t1 = asyncio.create_task(task(1, -0.01)) t2 = asyncio.create_task(task(2, 0.1)) - t3 = asyncio.create_task(task(3, 0.2)) - t3 = asyncio.create_task(task(4, -100)) + t3 = asyncio.create_task(task(3, 0.18)) + t4 = asyncio.create_task(task(4, -100)) await asyncio.sleep(0.5) t1.cancel() t2.cancel() t3.cancel() + t4.cancel() print("finish") From 91c5d168c061ec45a548876a76a5540316d02594 Mon Sep 17 00:00:00 2001 From: Roberto Colistete Jr Date: Sat, 15 Aug 2020 02:35:02 -0300 Subject: [PATCH 220/352] nrf/Makefile: Improve user C modules support. Add CFLAGS_EXTRA to CFLAGS. Include LDFLAGS_MOD to the compilation. And, add SRC_MOD to SRC_QSTR. --- ports/nrf/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 8f73336e0b1e8..e5a1b471a35d7 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -113,7 +113,7 @@ endif CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) -CFLAGS += $(INC) -Wall -Werror -g -ansi -std=c11 -nostdlib $(COPT) $(NRF_DEFINES) $(CFLAGS_MOD) +CFLAGS += $(INC) -Wall -Werror -g -ansi -std=c11 -nostdlib $(COPT) $(NRF_DEFINES) $(CFLAGS_MOD) $(CFLAGS_EXTRA) CFLAGS += -fno-strict-aliasing CFLAGS += -Iboards/$(BOARD) CFLAGS += -DNRF5_HAL_H='<$(MCU_VARIANT)_hal.h>' @@ -445,11 +445,11 @@ flash: deploy $(BUILD)/$(OUTPUT_FILENAME).elf: $(OBJ) $(ECHO) "LINK $@" - $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LDFLAGS_MOD) $(LIBS) $(Q)$(SIZE) $@ # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SRC_LIB) $(DRIVERS_SRC_C) $(SRC_BOARD_MODULES) +SRC_QSTR += $(SRC_C) $(SRC_LIB) $(DRIVERS_SRC_C) $(SRC_BOARD_MODULES) $(SRC_MOD) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR From 405893afc6654ed2ace9a6475bfd2095133e614a Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 8 Apr 2020 08:01:58 +0200 Subject: [PATCH 221/352] tests/run-tests: Use absolute paths where possible. Replace some usages of paths relative to the current working directory with absolute paths relative to the tests directory. Fixes and resulting changes: - default values of MICROPYTHON and MPYCROSS are absolute paths and always correct - likewise, the correct full paths for tools and extmod directories are appended to sys.path - printing/cleaning failures works properly since it expects the .exp and .out files in the tests directory which is also where they are written to now, plus no more need for changing directories This fixes #5872 and allows running custom tests which use run-tests without having to cd to the tests directory first, and the test output still is in the tests/ directory instead of the current working directory. Discovery of tests and all skip test logic based on paths relative to the current working directory remains unchanged which essentially means that for running most of MicroPython's own tests, run-tests must still be ran from within it's directory, so document that. --- tests/run-tests | 54 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index 102b0f7790e14..c2831614a3599 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -5,21 +5,29 @@ import subprocess import sys import platform import argparse +import inspect import re from glob import glob +# See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] +# are guaranteed to always work, this one should though. +BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) + +def base_path(*p): + return os.path.abspath(os.path.join(BASEPATH, *p)).replace('\\', '/') + # Tests require at least CPython 3.3. If your default python3 executable # is of lower version, you can point MICROPY_CPYTHON3 environment var # to the correct executable. if os.name == 'nt': CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../ports/windows/micropython.exe') + MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/windows/micropython.exe')) else: CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../ports/unix/micropython') + MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/unix/micropython')) # mpy-cross is only needed if --via-mpy command-line arg is passed -MPYCROSS = os.getenv('MICROPY_MPYCROSS', '../mpy-cross/mpy-cross') +MPYCROSS = os.getenv('MICROPY_MPYCROSS', base_path('../mpy-cross/mpy-cross')) # For diff'ing test output DIFF = os.getenv('MICROPY_DIFF', 'diff -u') @@ -61,7 +69,7 @@ def run_micropython(pyb, args, test_file, is_special=False): had_crash = False if pyb is None: # run on PC - if test_file.startswith(('cmdline/', 'feature_check/')) or test_file in special_tests: + if test_file.startswith(('cmdline/', base_path('feature_check/'))) or test_file in special_tests: # special handling for tests of the unix cmdline program is_special = True @@ -215,10 +223,10 @@ def run_feature_check(pyb, args, base_path, test_file): if pyb is not None and test_file.startswith("repl_"): # REPL feature tests will not run via pyboard because they require prompt interactivity return b"" - return run_micropython(pyb, args, base_path + "/feature_check/" + test_file, is_special=True) + return run_micropython(pyb, args, base_path("feature_check", test_file), is_special=True) -def run_tests(pyb, tests, args, base_path="."): +def run_tests(pyb, tests, args): test_count = 0 testcase_count = 0 passed_count = 0 @@ -244,6 +252,10 @@ def run_tests(pyb, tests, args, base_path="."): # If we're asked to --list-tests, we can't assume that there's a # connection to target, so we can't run feature checks usefully. if not (args.list_tests or args.write_exp): + # Even if we run completely different tests in a different directory, + # we need to access feature_checks from the same directory as the + # run-tests script itself so use base_path. + # Check if micropython.native is supported, and skip such tests if it's not output = run_feature_check(pyb, args, base_path, 'native_check.py') if output == b'CRASH': @@ -307,7 +319,7 @@ def run_tests(pyb, tests, args, base_path="."): upy_float_precision = int(upy_float_precision) has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' - cpy_byteorder = subprocess.check_output([CPYTHON3, base_path + '/feature_check/byteorder.py']) + cpy_byteorder = subprocess.check_output([CPYTHON3, base_path('feature_check/byteorder.py')]) skip_endian = (upy_byteorder != cpy_byteorder) # These tests don't test slice explicitly but rather use it to perform the test @@ -509,8 +521,8 @@ def run_tests(pyb, tests, args, base_path="."): testcase_count += len(output_expected.splitlines()) - filename_expected = test_basename + ".exp" - filename_mupy = test_basename + ".out" + filename_expected = base_path(test_basename + ".exp") + filename_mupy = base_path(test_basename + ".out") if output_expected == output_mupy: print("pass ", test_file) @@ -563,6 +575,10 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, description='''Run and manage tests for MicroPython. +Tests are discovered by scanning test directories for .py files or using the +specified test files. If test files nor directories are specified, the script +expects to be ran in the tests directory (where this file is located) and the +builtin tests suitable for the target platform are ran. When running tests, run-tests compares the MicroPython output of the test with the output produced by running the test through CPython unless a .exp file is found, in which case it is used as comparison. @@ -598,10 +614,8 @@ the last matching regex is used: args = cmd_parser.parse_args() if args.print_failures: - os.chdir(os.path.abspath(os.path.dirname(__file__))) - - for exp in glob("*.exp"): - testbase = os.path.basename(exp)[:-4] + for exp in glob(base_path("*.exp")): + testbase = exp[:-4] print() print("FAILURE {0}".format(testbase)) os.system("{0} {1}.exp {1}.out".format(DIFF, testbase)) @@ -609,9 +623,7 @@ the last matching regex is used: sys.exit(0) if args.clean_failures: - os.chdir(os.path.abspath(os.path.dirname(__file__))) - - for f in glob("*.exp") + glob("*.out"): + for f in glob(base_path("*.exp")) + glob(base_path("*.out")): os.remove(f) sys.exit(0) @@ -622,7 +634,7 @@ the last matching regex is used: pyb = None elif args.target in EXTERNAL_TARGETS: global pyboard - sys.path.append('../tools') + sys.path.append(base_path('../tools')) import pyboard pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) pyb.enter_raw_repl() @@ -659,14 +671,10 @@ the last matching regex is used: if not args.keep_path: # clear search path to make sure tests use only builtin modules and those in extmod - os.environ['MICROPYPATH'] = os.pathsep + '../extmod' + os.environ['MICROPYPATH'] = os.pathsep + base_path('../extmod') - # Even if we run completely different tests in a different directory, - # we need to access feature_check's from the same directory as the - # run-tests script itself. - base_path = os.path.dirname(sys.argv[0]) or "." try: - res = run_tests(pyb, tests, args, base_path) + res = run_tests(pyb, tests, args) finally: if pyb: pyb.close() From 0c3f9d58a5dcf0480460548a416f27d9c8350f3c Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 7 Apr 2020 16:19:49 +0200 Subject: [PATCH 222/352] tests/run-tests: Make test output directory configurable. A configurable result directory is advantageous because it enables using a dedicated location, eventually outside of the source tree, instead of forcing the output files into a fixed directory which might also contain other files already. For that reason the default output directory also has been changed to tests/results/. --- .gitignore | 3 +-- tests/run-tests | 16 +++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 50bd30e877e87..c52f59eec74e6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,7 @@ build-*/ # Test failure outputs ###################### -tests/*.exp -tests/*.out +tests/results/* # Python cache files ###################### diff --git a/tests/run-tests b/tests/run-tests index c2831614a3599..49811d9b78845 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -226,7 +226,7 @@ def run_feature_check(pyb, args, base_path, test_file): return run_micropython(pyb, args, base_path("feature_check", test_file), is_special=True) -def run_tests(pyb, tests, args): +def run_tests(pyb, tests, args, result_dir): test_count = 0 testcase_count = 0 passed_count = 0 @@ -521,8 +521,8 @@ def run_tests(pyb, tests, args): testcase_count += len(output_expected.splitlines()) - filename_expected = base_path(test_basename + ".exp") - filename_mupy = base_path(test_basename + ".out") + filename_expected = os.path.join(result_dir, test_basename + ".exp") + filename_mupy = os.path.join(result_dir, test_basename + ".out") if output_expected == output_mupy: print("pass ", test_file) @@ -582,7 +582,7 @@ builtin tests suitable for the target platform are ran. When running tests, run-tests compares the MicroPython output of the test with the output produced by running the test through CPython unless a .exp file is found, in which case it is used as comparison. -If a test fails, run-tests produces a pair of .out and .exp files in the current +If a test fails, run-tests produces a pair of .out and .exp files in the result directory with the MicroPython output and the expectations, respectively. ''', epilog='''\ @@ -599,6 +599,7 @@ the last matching regex is used: cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)') + cmd_parser.add_argument('-r', '--result-dir', default=base_path('results'), help='directory for test results') cmd_parser.add_argument('-e', '--exclude', action=append_filter, metavar='REGEX', dest='filters', help='exclude test by regex on path/name.py') cmd_parser.add_argument('-i', '--include', action=append_filter, metavar='REGEX', dest='filters', help='include test by regex on path/name.py') cmd_parser.add_argument('--write-exp', action='store_true', help='use CPython to generate .exp files to run tests w/o CPython') @@ -614,7 +615,7 @@ the last matching regex is used: args = cmd_parser.parse_args() if args.print_failures: - for exp in glob(base_path("*.exp")): + for exp in glob(os.path.join(args.result_dir, "*.exp")): testbase = exp[:-4] print() print("FAILURE {0}".format(testbase)) @@ -623,7 +624,7 @@ the last matching regex is used: sys.exit(0) if args.clean_failures: - for f in glob(base_path("*.exp")) + glob(base_path("*.out")): + for f in glob(os.path.join(args.result_dir, "*.exp")) + glob(os.path.join(args.result_dir, "*.out")): os.remove(f) sys.exit(0) @@ -674,7 +675,8 @@ the last matching regex is used: os.environ['MICROPYPATH'] = os.pathsep + base_path('../extmod') try: - res = run_tests(pyb, tests, args) + os.makedirs(args.result_dir, exist_ok=True) + res = run_tests(pyb, tests, args, args.result_dir) finally: if pyb: pyb.close() From cef678b2dbab528a54bc36bd6c3be84a38857629 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sun, 23 Aug 2020 15:16:00 +0200 Subject: [PATCH 223/352] extmod/machine_i2c: Fix buffer overrun if 'addrsize' is bigger than 32. The memory operation functions read_mem() and write_mem() create a temporary buffer on the local C stack for the address bytes with the size of 4 bytes. This buffer is filled in a loop from the user supplied address and address length. If the user supplied 'addrsize' is bigger than 32, the local buffer is overrun. Fix this by raising an exception for invalid 'addrsize' values. Signed-off-by: Michael Buesch --- extmod/machine_i2c.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index 14cba962365e1..c804cf570385d 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -526,13 +526,24 @@ STATIC mp_obj_t machine_i2c_writevto(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writevto_obj, 3, 4, machine_i2c_writevto); -STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) { - mp_obj_base_t *self = (mp_obj_base_t *)MP_OBJ_TO_PTR(self_in); - uint8_t memaddr_buf[4]; +STATIC size_t fill_memaddr_buf(uint8_t *memaddr_buf, uint32_t memaddr, uint8_t addrsize) { size_t memaddr_len = 0; + if ((addrsize & 7) != 0 || addrsize > 32) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid addrsize")); + } for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } + return memaddr_len; +} + +STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) { + mp_obj_base_t *self = (mp_obj_base_t *)MP_OBJ_TO_PTR(self_in); + + // Create buffer with memory address + uint8_t memaddr_buf[4]; + size_t memaddr_len = fill_memaddr_buf(&memaddr_buf[0], memaddr, addrsize); + int ret = mp_machine_i2c_writeto(self, addr, memaddr_buf, memaddr_len, false); if (ret != memaddr_len) { // must generate STOP @@ -546,11 +557,8 @@ STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t mp_obj_base_t *self = (mp_obj_base_t *)MP_OBJ_TO_PTR(self_in); // Create buffer with memory address - size_t memaddr_len = 0; uint8_t memaddr_buf[4]; - for (int16_t i = addrsize - 8; i >= 0; i -= 8) { - memaddr_buf[memaddr_len++] = memaddr >> i; - } + size_t memaddr_len = fill_memaddr_buf(&memaddr_buf[0], memaddr, addrsize); // Create partial write buffers mp_machine_i2c_buf_t bufs[2] = { From a93a378e933abc294de6ae26fb86a421e858fbcf Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 28 Aug 2020 16:42:32 +1000 Subject: [PATCH 224/352] zephyr/README: Update required Zephyr version and mention new features. Signed-off-by: Damien George --- ports/zephyr/README.md | 54 +++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index 6bcbccd8b36a3..a2fb393d90d0f 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -1,10 +1,11 @@ MicroPython port to Zephyr RTOS =============================== -This is an work-in-progress port of MicroPython to Zephyr RTOS +This is a work-in-progress port of MicroPython to Zephyr RTOS (http://zephyrproject.org). -This port requires Zephyr version 1.8 or higher. All boards supported +This port requires Zephyr version 2.3.0, and may also work on higher +versions. All boards supported by Zephyr (with standard level of features support, like UART console) should work with MicroPython (but not all were tested). @@ -12,12 +13,14 @@ Features supported at this time: * REPL (interactive prompt) over Zephyr UART console. * `utime` module for time measurements and delays. -* `machine.Pin` class for GPIO control. +* `machine.Pin` class for GPIO control, with IRQ support. * `machine.I2C` class for I2C control. * `usocket` module for networking (IPv4/IPv6). * "Frozen modules" support to allow to bundle Python modules together with firmware. Including complete applications, including with run-on-boot capability. +* virtual filesystem with FAT and littlefs formats, backed by either + DiskAccess or FlashArea (flash map). Over time, bindings for various Zephyr subsystems may be added. @@ -28,17 +31,35 @@ Building Follow to Zephyr web site for Getting Started instruction of installing Zephyr SDK, getting Zephyr source code, and setting up development environment. (Direct link: -https://www.zephyrproject.org/doc/getting_started/getting_started.html). +https://docs.zephyrproject.org/latest/getting_started/index.html). You may want to build Zephyr's own sample applications to make sure your setup is correct. -To build MicroPython port, in the port subdirectory (zephyr/), run: +If you already have Zephyr installed but are having issues building the +MicroPython port then try installing the correct version of Zephyr via: - make BOARD= + $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.3.0 + +Alternatively, you don't have to redo the Zephyr installation to just +switch from master to a tagged release, you can instead do: + + $ cd zephyrproject/zephyr + $ git checkout v2.3.0 + $ west update + +With Zephyr installed you may then need to configure your environment, +for example by sourcing `zephyrproject/zephyr/zephyr-env.sh`. + +Once Zephyr is ready to use you can build the MicroPython port. +In the port subdirectory `ports/zephyr/` run: + + $ make BOARD= If you don't specify BOARD, the default is `qemu_x86` (x86 target running -in QEMU emulator). Consult Zephyr documentation above for the list of -supported boards. +in QEMU emulator). Consult the Zephyr documentation above for the list of +supported boards. Board configuration files appearing in `ports/zephyr/boards/` +correspond to boards that have been tested with MicroPython and may have +additional options enabled, like filesystem support. Running @@ -66,6 +87,10 @@ cf. for example QEMU networking requirements above; real hardware boards generally should not have any special requirements, unless there're known issues). +For example, to deploy firmware on the FRDM-K64F board run: + + $ make BOARD=frdm_k64f flash + Quick example ------------- @@ -89,6 +114,19 @@ reference materials). To execute the above sample, copy it to clipboard, in MicroPython REPL enter "paste mode" using Ctrl+E, paste clipboard, press Ctrl+D to finish paste mode and start execution. +To respond to Pin change IRQs, on a FRDM-K64F board run: + + from machine import Pin + + SW2 = Pin(("GPIO_2", 6), Pin.IN) + SW3 = Pin(("GPIO_0", 4), Pin.IN) + + SW2.irq(lambda t: print("SW2 changed")) + SW3.irq(lambda t: print("SW3 changed")) + + while True: + pass + Example of using I2C to scan for I2C slaves: from machine import I2C From 338b12d3c840ea56504c83d9c51e4d0f628684f4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 29 Aug 2020 13:58:20 +1000 Subject: [PATCH 225/352] LICENSE,docs: Update copyright year range to include 2020. Signed-off-by: Damien George --- LICENSE | 2 +- docs/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index e6a54cf269947..8c5e4fe4c359a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2019 Damien P. George +Copyright (c) 2013-2020 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 diff --git a/docs/conf.py b/docs/conf.py index 36f99cd2c441b..7dc039d6ca599 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,7 +66,7 @@ # General information about the project. project = 'MicroPython' -copyright = '2014-2019, Damien P. George, Paul Sokolovsky, and contributors' +copyright = '2014-2020, Damien P. George, Paul Sokolovsky, and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 40d174ac7d1cc5c548756bb75b4f36b9b450b8bf Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 29 Aug 2020 14:00:24 +1000 Subject: [PATCH 226/352] stm32/powerctrl.h: Include stdbool.h to get definition of bool. Signed-off-by: Damien George --- ports/stm32/powerctrl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h index 6e5f899a4cd1b..9f223e794a984 100644 --- a/ports/stm32/powerctrl.h +++ b/ports/stm32/powerctrl.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_POWERCTRL_H #define MICROPY_INCLUDED_STM32_POWERCTRL_H +#include #include void SystemClock_Config(void); From 0c7354afaf91da3dac2c5ec471603c9e7acc8eac Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 29 Aug 2020 14:04:59 +1000 Subject: [PATCH 227/352] tests: Split out complex reverse-op tests to separate test file. So they can be skipped if __rOP__'s are not supported on the target. Also fix the typo in the complex_special_methods.py filename. Signed-off-by: Damien George --- tests/float/complex_reverse_op.py | 10 ++++++++++ ...ex_special_mehods.py => complex_special_methods.py} | 5 ----- tests/run-tests | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 tests/float/complex_reverse_op.py rename tests/float/{complex_special_mehods.py => complex_special_methods.py} (62%) diff --git a/tests/float/complex_reverse_op.py b/tests/float/complex_reverse_op.py new file mode 100644 index 0000000000000..a7351949d0cea --- /dev/null +++ b/tests/float/complex_reverse_op.py @@ -0,0 +1,10 @@ +# test complex interacting with special reverse methods + + +class A: + def __radd__(self, x): + print("__radd__") + return 2 + + +print(1j + A()) diff --git a/tests/float/complex_special_mehods.py b/tests/float/complex_special_methods.py similarity index 62% rename from tests/float/complex_special_mehods.py rename to tests/float/complex_special_methods.py index 6789013fa6e0d..7e54905e495ad 100644 --- a/tests/float/complex_special_mehods.py +++ b/tests/float/complex_special_methods.py @@ -6,10 +6,5 @@ def __add__(self, x): print("__add__") return 1 - def __radd__(self, x): - print("__radd__") - return 2 - print(A() + 1j) -print(1j + A()) diff --git a/tests/run-tests b/tests/run-tests index 49811d9b78845..a7b88ecdd3265 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -367,7 +367,7 @@ def run_tests(pyb, tests, args, result_dir): if not has_complex: skip_tests.add('float/complex1.py') skip_tests.add('float/complex1_intbig.py') - skip_tests.add('float/complex_special_mehods.py') + skip_tests.add('float/complex_special_methods.py') skip_tests.add('float/int_big_float.py') skip_tests.add('float/true_value.py') skip_tests.add('float/types.py') @@ -476,7 +476,7 @@ def run_tests(pyb, tests, args, result_dir): skip_it |= skip_slice and is_slice skip_it |= skip_async and is_async skip_it |= skip_const and is_const - skip_it |= skip_revops and test_name.startswith("class_reverse_op") + skip_it |= skip_revops and "reverse_op" in test_name skip_it |= skip_io_module and is_io_module if args.list_tests: From 06659077a81b85882254cf0953c33b27614e018e Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 29 Aug 2020 15:14:29 +1000 Subject: [PATCH 228/352] all: Update Python code to conform to latest black formatting. Updating to Black v20.8b1 there are two changes that affect the code in this repository: - If there is a trailing comma in a list (eg [], () or function call) then that list is now written out with one line per element. So remove such trailing commas where the list should stay on one line. - Spaces at the start of """ doc strings are removed. Signed-off-by: Damien George --- examples/bluetooth/ble_simple_central.py | 6 +++--- examples/bluetooth/ble_simple_peripheral.py | 12 +++++------- examples/bluetooth/ble_temperature.py | 6 +++--- examples/bluetooth/ble_temperature_central.py | 6 +++--- examples/bluetooth/ble_uart_peripheral.py | 12 +++++------- extmod/webrepl/manifest.py | 2 +- ports/cc3200/boards/make-pins.py | 5 ++++- ports/nrf/boards/make-pins.py | 5 ++++- ports/stm32/boards/make-pins.py | 5 ++++- ports/teensy/make-pins.py | 5 ++++- py/makemoduledefs.py | 6 +++--- tests/extmod/uasyncio_gather.py | 2 +- tests/extmod/uctypes_le.py | 2 +- tests/extmod/uctypes_native_le.py | 2 +- tests/extmod/uctypes_sizeof_native.py | 2 +- tests/float/cmath_fun.py | 2 +- tests/float/math_fun.py | 8 ++++---- tests/float/math_fun_special.py | 2 +- tools/mpy_ld.py | 2 +- 19 files changed, 50 insertions(+), 42 deletions(-) diff --git a/examples/bluetooth/ble_simple_central.py b/examples/bluetooth/ble_simple_central.py index f6996366e3da1..f0a3297d322e1 100644 --- a/examples/bluetooth/ble_simple_central.py +++ b/examples/bluetooth/ble_simple_central.py @@ -74,7 +74,7 @@ def _reset(self): def _irq(self, event, data): if event == _IRQ_SCAN_RESULT: addr_type, addr, adv_type, rssi, adv_data = data - if adv_type in (_ADV_IND, _ADV_DIRECT_IND,) and _UART_SERVICE_UUID in decode_services( + if adv_type in (_ADV_IND, _ADV_DIRECT_IND) and _UART_SERVICE_UUID in decode_services( adv_data ): # Found a potential device, remember it and stop scanning. @@ -97,14 +97,14 @@ def _irq(self, event, data): elif event == _IRQ_PERIPHERAL_CONNECT: # Connect successful. - conn_handle, addr_type, addr, = data + conn_handle, addr_type, addr = data if addr_type == self._addr_type and addr == self._addr: self._conn_handle = conn_handle self._ble.gattc_discover_services(self._conn_handle) elif event == _IRQ_PERIPHERAL_DISCONNECT: # Disconnect (either initiated by us or the remote end). - conn_handle, _, _, = data + conn_handle, _, _ = data if conn_handle == self._conn_handle: # If it was initiated by us, it'll already be reset. self._reset() diff --git a/examples/bluetooth/ble_simple_peripheral.py b/examples/bluetooth/ble_simple_peripheral.py index 08cd7fa98d69c..f5e7661926ba8 100644 --- a/examples/bluetooth/ble_simple_peripheral.py +++ b/examples/bluetooth/ble_simple_peripheral.py @@ -23,7 +23,7 @@ ) _UART_SERVICE = ( _UART_UUID, - (_UART_TX, _UART_RX,), + (_UART_TX, _UART_RX), ) @@ -32,22 +32,20 @@ def __init__(self, ble, name="mpy-uart"): self._ble = ble self._ble.active(True) self._ble.irq(handler=self._irq) - ((self._handle_tx, self._handle_rx,),) = self._ble.gatts_register_services( - (_UART_SERVICE,) - ) + ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,)) self._connections = set() self._write_callback = None - self._payload = advertising_payload(name=name, services=[_UART_UUID],) + self._payload = advertising_payload(name=name, services=[_UART_UUID]) self._advertise() def _irq(self, event, data): # Track connections so we can send notifications. if event == _IRQ_CENTRAL_CONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data print("New connection", conn_handle) self._connections.add(conn_handle) elif event == _IRQ_CENTRAL_DISCONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data print("Disconnected", conn_handle) self._connections.remove(conn_handle) # Start advertising again to allow a new connection. diff --git a/examples/bluetooth/ble_temperature.py b/examples/bluetooth/ble_temperature.py index 0e2da2239d50a..001a26b114a0a 100644 --- a/examples/bluetooth/ble_temperature.py +++ b/examples/bluetooth/ble_temperature.py @@ -46,15 +46,15 @@ def __init__(self, ble, name="mpy-temp"): def _irq(self, event, data): # Track connections so we can send notifications. if event == _IRQ_CENTRAL_CONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data self._connections.add(conn_handle) elif event == _IRQ_CENTRAL_DISCONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data self._connections.remove(conn_handle) # Start advertising again to allow a new connection. self._advertise() elif event == _IRQ_GATTS_INDICATE_DONE: - conn_handle, value_handle, status, = data + conn_handle, value_handle, status = data def set_temperature(self, temp_deg_c, notify=False, indicate=False): # Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius. diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index f9db225c16942..79830341789d4 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -87,7 +87,7 @@ def _reset(self): def _irq(self, event, data): if event == _IRQ_SCAN_RESULT: addr_type, addr, adv_type, rssi, adv_data = data - if adv_type in (_ADV_IND, _ADV_DIRECT_IND,) and _ENV_SENSE_UUID in decode_services( + if adv_type in (_ADV_IND, _ADV_DIRECT_IND) and _ENV_SENSE_UUID in decode_services( adv_data ): # Found a potential device, remember it and stop scanning. @@ -110,14 +110,14 @@ def _irq(self, event, data): elif event == _IRQ_PERIPHERAL_CONNECT: # Connect successful. - conn_handle, addr_type, addr, = data + conn_handle, addr_type, addr = data if addr_type == self._addr_type and addr == self._addr: self._conn_handle = conn_handle self._ble.gattc_discover_services(self._conn_handle) elif event == _IRQ_PERIPHERAL_DISCONNECT: # Disconnect (either initiated by us or the remote end). - conn_handle, _, _, = data + conn_handle, _, _ = data if conn_handle == self._conn_handle: # If it was initiated by us, it'll already be reset. self._reset() diff --git a/examples/bluetooth/ble_uart_peripheral.py b/examples/bluetooth/ble_uart_peripheral.py index cc8d589b0e35f..59b35f7e6ad5e 100644 --- a/examples/bluetooth/ble_uart_peripheral.py +++ b/examples/bluetooth/ble_uart_peripheral.py @@ -20,7 +20,7 @@ ) _UART_SERVICE = ( _UART_UUID, - (_UART_TX, _UART_RX,), + (_UART_TX, _UART_RX), ) # org.bluetooth.characteristic.gap.appearance.xml @@ -32,9 +32,7 @@ def __init__(self, ble, name="mpy-uart", rxbuf=100): self._ble = ble self._ble.active(True) self._ble.irq(handler=self._irq) - ((self._tx_handle, self._rx_handle,),) = self._ble.gatts_register_services( - (_UART_SERVICE,) - ) + ((self._tx_handle, self._rx_handle),) = self._ble.gatts_register_services((_UART_SERVICE,)) # Increase the size of the rx buffer and enable append mode. self._ble.gatts_set_buffer(self._rx_handle, rxbuf, True) self._connections = set() @@ -50,16 +48,16 @@ def irq(self, handler): def _irq(self, event, data): # Track connections so we can send notifications. if event == _IRQ_CENTRAL_CONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data self._connections.add(conn_handle) elif event == _IRQ_CENTRAL_DISCONNECT: - conn_handle, _, _, = data + conn_handle, _, _ = data if conn_handle in self._connections: self._connections.remove(conn_handle) # Start advertising again to allow a new connection. self._advertise() elif event == _IRQ_GATTS_WRITE: - conn_handle, value_handle, = data + conn_handle, value_handle = data if conn_handle in self._connections and value_handle == self._rx_handle: self._rx_buffer += self._ble.gatts_read(self._rx_handle) if self._handler: diff --git a/extmod/webrepl/manifest.py b/extmod/webrepl/manifest.py index 6ce7d85466c06..6eceb3eeb6c56 100644 --- a/extmod/webrepl/manifest.py +++ b/extmod/webrepl/manifest.py @@ -1 +1 @@ -freeze(".", ("webrepl.py", "webrepl_setup.py", "websocket_helper.py",)) +freeze(".", ("webrepl.py", "webrepl_setup.py", "websocket_helper.py")) diff --git a/ports/cc3200/boards/make-pins.py b/ports/cc3200/boards/make-pins.py index a204561cfbc32..0cf0d565606f2 100644 --- a/ports/cc3200/boards/make-pins.py +++ b/ports/cc3200/boards/make-pins.py @@ -216,7 +216,10 @@ def main(): default="cc3200_af.csv", ) parser.add_argument( - "-b", "--board", dest="board_filename", help="Specifies the board file", + "-b", + "--board", + dest="board_filename", + help="Specifies the board file", ) parser.add_argument( "-p", diff --git a/ports/nrf/boards/make-pins.py b/ports/nrf/boards/make-pins.py index 56c56ae5f42f3..347ed15b21b31 100644 --- a/ports/nrf/boards/make-pins.py +++ b/ports/nrf/boards/make-pins.py @@ -381,7 +381,10 @@ def main(): default="build/pins_af.py", ) parser.add_argument( - "-b", "--board", dest="board_filename", help="Specifies the board file", + "-b", + "--board", + dest="board_filename", + help="Specifies the board file", ) parser.add_argument( "-p", diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 692387f404ea5..a91ed8a2c9150 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -515,7 +515,10 @@ def main(): action="store_true", ) parser.add_argument( - "-b", "--board", dest="board_filename", help="Specifies the board file", + "-b", + "--board", + dest="board_filename", + help="Specifies the board file", ) parser.add_argument( "-p", diff --git a/ports/teensy/make-pins.py b/ports/teensy/make-pins.py index ddefae8521c50..4e46a8c244969 100755 --- a/ports/teensy/make-pins.py +++ b/ports/teensy/make-pins.py @@ -370,7 +370,10 @@ def main(): default="build/pins_af.py", ) parser.add_argument( - "-b", "--board", dest="board_filename", help="Specifies the board file", + "-b", + "--board", + dest="board_filename", + help="Specifies the board file", ) parser.add_argument( "-p", diff --git a/py/makemoduledefs.py b/py/makemoduledefs.py index d718da6e02513..612f3d29a3660 100644 --- a/py/makemoduledefs.py +++ b/py/makemoduledefs.py @@ -17,7 +17,7 @@ def find_c_file(obj_file, vpath): - """ Search vpaths for the c file that matches the provided object_file. + """Search vpaths for the c file that matches the provided object_file. :param str obj_file: object file to find the matching c file for :param List[str] vpath: List of base paths, similar to gcc vpath @@ -36,7 +36,7 @@ def find_c_file(obj_file, vpath): def find_module_registrations(c_file): - """ Find any MP_REGISTER_MODULE definitions in the provided c file. + """Find any MP_REGISTER_MODULE definitions in the provided c file. :param str c_file: path to c file to check :return: List[(module_name, obj_module, enabled_define)] @@ -52,7 +52,7 @@ def find_module_registrations(c_file): def generate_module_table_header(modules): - """ Generate header with module table entries for builtin modules. + """Generate header with module table entries for builtin modules. :param List[(module_name, obj_module, enabled_define)] modules: module defs :return: None diff --git a/tests/extmod/uasyncio_gather.py b/tests/extmod/uasyncio_gather.py index 2697a6278b08c..0e2948b07caa4 100644 --- a/tests/extmod/uasyncio_gather.py +++ b/tests/extmod/uasyncio_gather.py @@ -34,7 +34,7 @@ async def gather_task(): async def main(): # Simple gather with return values - print(await asyncio.gather(factorial("A", 2), factorial("B", 3), factorial("C", 4),)) + print(await asyncio.gather(factorial("A", 2), factorial("B", 3), factorial("C", 4))) # Cancel a multi gather # TODO doesn't work, Task should not forward cancellation from gather to sub-task diff --git a/tests/extmod/uctypes_le.py b/tests/extmod/uctypes_le.py index 23f2af982a1a9..f69da30b61ea7 100644 --- a/tests/extmod/uctypes_le.py +++ b/tests/extmod/uctypes_le.py @@ -6,7 +6,7 @@ desc = { "s0": uctypes.UINT16 | 0, - "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1,}), + "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), "bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN, diff --git a/tests/extmod/uctypes_native_le.py b/tests/extmod/uctypes_native_le.py index c161539c624f8..7958e5c22ada9 100644 --- a/tests/extmod/uctypes_native_le.py +++ b/tests/extmod/uctypes_native_le.py @@ -16,7 +16,7 @@ desc = { "s0": uctypes.UINT16 | 0, - "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1,}), + "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), "bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN, diff --git a/tests/extmod/uctypes_sizeof_native.py b/tests/extmod/uctypes_sizeof_native.py index 352c191e0d8a6..157e554b09d47 100644 --- a/tests/extmod/uctypes_sizeof_native.py +++ b/tests/extmod/uctypes_sizeof_native.py @@ -28,7 +28,7 @@ "b": uctypes.UINT32 | 4, "c": uctypes.UINT8 | 8, "d": uctypes.UINT32 | 0, - "sub": (4, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1,}), + "sub": (4, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), } assert uctypes.sizeof(S5) == 12 diff --git a/tests/float/cmath_fun.py b/tests/float/cmath_fun.py index 15b72e7a6203b..39011733b02b4 100644 --- a/tests/float/cmath_fun.py +++ b/tests/float/cmath_fun.py @@ -22,7 +22,7 @@ test_values_non_zero.append(complex(r, -i)) if r != 0.0 and i != 0.0: test_values_non_zero.append(complex(-r, -i)) -test_values = [complex(0.0, 0.0),] + test_values_non_zero +test_values = [complex(0.0, 0.0)] + test_values_non_zero print(test_values) functions = [ diff --git a/tests/float/math_fun.py b/tests/float/math_fun.py index 7b6bb86489b7d..0d443a4754521 100644 --- a/tests/float/math_fun.py +++ b/tests/float/math_fun.py @@ -62,10 +62,10 @@ copysign, [(23.0, 42.0), (-23.0, 42.0), (23.0, -42.0), (-23.0, -42.0), (1.0, 0.0), (1.0, -0.0)], ), - ("pow", pow, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0),)), - ("atan2", atan2, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0),)), - ("fmod", fmod, ((1.0, 1.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0),)), - ("ldexp", ldexp, ((1.0, 0), (0.0, 1), (2.0, 2), (3.0, -2), (-3.0, -4),)), + ("pow", pow, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("atan2", atan2, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("fmod", fmod, ((1.0, 1.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("ldexp", ldexp, ((1.0, 0), (0.0, 1), (2.0, 2), (3.0, -2), (-3.0, -4))), ( "log", log, diff --git a/tests/float/math_fun_special.py b/tests/float/math_fun_special.py index c101a7e5019db..614470c0f15dd 100644 --- a/tests/float/math_fun_special.py +++ b/tests/float/math_fun_special.py @@ -40,7 +40,7 @@ ("erf", erf, test_values), ("erfc", erfc, test_values), ("gamma", gamma, pos_test_values), - ("lgamma", lgamma, pos_test_values + [50.0, 100.0,]), + ("lgamma", lgamma, pos_test_values + [50.0, 100.0]), ] for function_name, function, test_vals in functions: diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 32bc176cb71c4..774966a7f68a0 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -908,7 +908,7 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): # MPY: header out.write_bytes( bytearray( - [ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS, QSTR_WINDOW_SIZE,] + [ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS, QSTR_WINDOW_SIZE] ) ) From d1995e50ebda074f8151609bb4c95d4c31072513 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 30 Aug 2020 13:20:51 +1000 Subject: [PATCH 229/352] extmod/modlwip: Fix error return for TCP recv when not connected. This commit fixes the cases when a TCP socket is in STATE_NEW, STATE_LISTENING or STATE_CONNECTING and recv() is called on it. It now raises ENOTCONN instead of a random error code due to it previously indexing beyond the start of error_lookup_table[]. Signed-off-by: Damien George --- extmod/modlwip.c | 7 +++++-- tests/extmod/usocket_tcp_basic.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/extmod/usocket_tcp_basic.py diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 216e81749d1c3..1d557a6a84b5a 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -757,8 +757,11 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ return 0; } } else if (socket->state != STATE_CONNECTED) { - assert(socket->state < 0); - *_errno = error_lookup_table[-socket->state]; + if (socket->state >= STATE_NEW) { + *_errno = MP_ENOTCONN; + } else { + *_errno = error_lookup_table[-socket->state]; + } return -1; } } diff --git a/tests/extmod/usocket_tcp_basic.py b/tests/extmod/usocket_tcp_basic.py new file mode 100644 index 0000000000000..368dfe3c98f4a --- /dev/null +++ b/tests/extmod/usocket_tcp_basic.py @@ -0,0 +1,17 @@ +# Test basic, stand-alone TCP socket functionality + +try: + import usocket as socket, uerrno as errno +except ImportError: + try: + import socket, errno + except ImportError: + print("SKIP") + raise SystemExit + +# recv() on a fresh socket should raise ENOTCONN +s = socket.socket() +try: + s.recv(1) +except OSError as er: + print("ENOTCONN:", er.args[0] == errno.ENOTCONN) From 836bca9956d9f02b9c0f6f396dc76cbd1586de10 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 30 Aug 2020 13:48:26 +1000 Subject: [PATCH 230/352] unix/variants: Fix fast and freedos variants so they build again. This regressed in bd2fff66875ed5adab8ce163a7f2bdafbd0332f9 Signed-off-by: Damien George --- ports/unix/variants/fast/mpconfigvariant.h | 2 -- ports/unix/variants/fast/mpconfigvariant.mk | 2 +- ports/unix/variants/freedos/mpconfigvariant.h | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ports/unix/variants/fast/mpconfigvariant.h b/ports/unix/variants/fast/mpconfigvariant.h index 6d73275fdebfc..8a531b056ab71 100644 --- a/ports/unix/variants/fast/mpconfigvariant.h +++ b/ports/unix/variants/fast/mpconfigvariant.h @@ -32,5 +32,3 @@ // 91 is a magic number proposed by @dpgeorge, which make pystone run ~ at tie // with CPython 3.4. #define MICROPY_MODULE_DICT_SIZE (91) - -#include "variants/DEV/mpconfigvariant.h" diff --git a/ports/unix/variants/fast/mpconfigvariant.mk b/ports/unix/variants/fast/mpconfigvariant.mk index d67f7c8f38388..595e5756457c9 100644 --- a/ports/unix/variants/fast/mpconfigvariant.mk +++ b/ports/unix/variants/fast/mpconfigvariant.mk @@ -1,6 +1,6 @@ # build synthetically fast interpreter for benchmarking -COPT += "-fno-crossjumping -O2" +COPT += -fno-crossjumping -O2 PROG = micropython-fast diff --git a/ports/unix/variants/freedos/mpconfigvariant.h b/ports/unix/variants/freedos/mpconfigvariant.h index 338b34492de7f..562c783ca35fb 100644 --- a/ports/unix/variants/freedos/mpconfigvariant.h +++ b/ports/unix/variants/freedos/mpconfigvariant.h @@ -36,5 +36,3 @@ #undef _DIRENT_HAVE_D_INO #define MICROPY_USE_INTERNAL_ERRNO (1) - -#include "variants/DEV/mpconfigvariant.h" From 40153b800a8324f6cf3e47dd71cafcf90c3c4718 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 00:49:19 +1000 Subject: [PATCH 231/352] esp32/mphalport: Fix mp_hal_time_ns offset. gettimeofday returns seconds since 2000/1/1 so needs to be adjusted to seconds since 1970/1/1 to give the correct return value of mp_hal_time_ns. Signed-off-by: Damien George --- ports/esp32/mphalport.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 5e526df6673cb..3a46edf1f75c1 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -45,6 +45,7 @@ #include "py/mpstate.h" #include "py/mphal.h" #include "extmod/misc.h" +#include "lib/timeutils/timeutils.h" #include "lib/utils/pyexec.h" #include "mphalport.h" @@ -199,7 +200,10 @@ void mp_hal_delay_us(uint32_t us) { uint64_t mp_hal_time_ns(void) { struct timeval tv; gettimeofday(&tv, NULL); - return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; + // gettimeofday returns seconds since 2000/1/1 + uint64_t ns = timeutils_seconds_since_2000_to_nanoseconds_since_1970(tv.tv_sec); + ns += (uint64_t)tv.tv_usec * 1000ULL; + return ns; } // Wake up the main task if it is sleeping From c70e59965977df97d1134f0dcadb9cd4ed58139f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 14:25:20 +1000 Subject: [PATCH 232/352] extmod/vfs: Support larger integer range in VFS stat time fields. On ports like unix where the Epoch is 1970/1/1 and atime/mtime/ctime are in seconds since the Epoch, this value will overflow a small-int on 32-bit systems. So far this is only an issue on 32-bit unix builds that use the VFS layer (eg dev and coverage unix variants) but the fix (using mp_obj_new_int_from_uint instead of MP_OBJ_NEW_SMALL_INT) is there for all ports so as to not complicate the code, and because they will need the range one day. Also apply a similar fix to other fields in VfsPosix.stat because they may also be large. Signed-off-by: Damien George --- extmod/vfs_fat.c | 6 +++--- extmod/vfs_lfsx.c | 6 +++--- extmod/vfs_posix.c | 18 +++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index e7af4057b7433..79b0fc119b2fc 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -326,9 +326,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size - t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime + t->items[7] = mp_obj_new_int_from_uint(seconds); // st_atime + t->items[8] = mp_obj_new_int_from_uint(seconds); // st_mtime + t->items[9] = mp_obj_new_int_from_uint(seconds); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 511b741b0dec1..9f5d9ce6db7f7 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -377,9 +377,9 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size - t->items[7] = MP_OBJ_NEW_SMALL_INT(mtime); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(mtime); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(mtime); // st_ctime + t->items[7] = mp_obj_new_int_from_uint(mtime); // st_atime + t->items[8] = mp_obj_new_int_from_uint(mtime); // st_mtime + t->items[9] = mp_obj_new_int_from_uint(mtime); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index f14f56e81ec82..719afe28fe87f 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -295,15 +295,15 @@ STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) { MP_HAL_RETRY_SYSCALL(ret, stat(path, &sb), mp_raise_OSError(err)); mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode); - t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino); - t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev); - t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink); - t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid); - t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid); - t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.st_size); - t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime); - t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime); - t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime); + t->items[1] = mp_obj_new_int_from_uint(sb.st_ino); + t->items[2] = mp_obj_new_int_from_uint(sb.st_dev); + t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink); + t->items[4] = mp_obj_new_int_from_uint(sb.st_uid); + t->items[5] = mp_obj_new_int_from_uint(sb.st_gid); + t->items[6] = mp_obj_new_int_from_uint(sb.st_size); + t->items[7] = mp_obj_new_int_from_uint(sb.st_atime); + t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime); + t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime); return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat); From 0385b21597a3e5dc3d1a84cd37848fc2ac2a0e20 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 14:39:02 +1000 Subject: [PATCH 233/352] unix/modos: Support larger integer range in uos.stat fields. On 32-bit builds these stat fields will overflow a small-int, so use mp_obj_new_int_from_uint to construct the int object. Signed-off-by: Damien George --- ports/unix/modos.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 82b1b11425679..5e719c5736c1c 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -58,15 +58,15 @@ STATIC mp_obj_t mod_os_stat(mp_obj_t path_in) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode); - t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino); - t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.st_dev); - t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.st_nlink); - t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.st_uid); - t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.st_gid); + t->items[1] = mp_obj_new_int_from_uint(sb.st_ino); + t->items[2] = mp_obj_new_int_from_uint(sb.st_dev); + t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink); + t->items[4] = mp_obj_new_int_from_uint(sb.st_uid); + t->items[5] = mp_obj_new_int_from_uint(sb.st_gid); t->items[6] = mp_obj_new_int_from_uint(sb.st_size); - t->items[7] = MP_OBJ_NEW_SMALL_INT(sb.st_atime); - t->items[8] = MP_OBJ_NEW_SMALL_INT(sb.st_mtime); - t->items[9] = MP_OBJ_NEW_SMALL_INT(sb.st_ctime); + t->items[7] = mp_obj_new_int_from_uint(sb.st_atime); + t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime); + t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime); return MP_OBJ_FROM_PTR(t); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_stat_obj, mod_os_stat); From 2a72e90ab8969b69304d84aa7070720d63c60d06 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 14:55:54 +1000 Subject: [PATCH 234/352] extmod/vfs: Add option to use 1970 as Epoch. By setting MICROPY_EPOCH_IS_1970 a port can opt to use 1970/1/1 as the Epoch for timestamps returned by stat(). And this setting is enabled on the unix and windows ports because that's what they use. Signed-off-by: Damien George --- extmod/vfs_fat.c | 3 +++ extmod/vfs_lfsx.c | 3 +++ ports/unix/mpconfigport.h | 3 +++ ports/windows/mpconfigport.h | 3 +++ 4 files changed, 12 insertions(+) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 79b0fc119b2fc..ace5ba5b6547f 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -319,6 +319,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { (fno.ftime >> 5) & 0x3f, 2 * (fno.ftime & 0x1f) ); + #if MICROPY_EPOCH_IS_1970 + seconds += TIMEUTILS_SECONDS_1970_TO_2000; + #endif t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 9f5d9ce6db7f7..d00df53104f6f 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -366,6 +366,9 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { ns = ns << 8 | mtime_buf[i - 1]; } mtime = timeutils_seconds_since_2000_from_nanoseconds_since_1970(ns); + #if MICROPY_EPOCH_IS_1970 + mtime += TIMEUTILS_SECONDS_1970_TO_2000; + #endif } #endif diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index f3c61c18f100d..08605842fd5bd 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -176,6 +176,9 @@ #define MICROPY_ERROR_PRINTER (&mp_stderr_print) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) +// VFS stat functions should return time values relative to 1970/1/1 +#define MICROPY_EPOCH_IS_1970 (1) + extern const struct _mp_print_t mp_stderr_print; #if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index fa09dda7527e0..30389c700eac3 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -123,6 +123,9 @@ #define MICROPY_WARNINGS (1) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) +// VFS stat functions should return time values relative to 1970/1/1 +#define MICROPY_EPOCH_IS_1970 (1) + extern const struct _mp_print_t mp_stderr_print; #ifdef _MSC_VER From a909c215879d94e7e76046f71f38e881949fe7dc Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 14:59:10 +1000 Subject: [PATCH 235/352] unix/fatfs_port: Fix month offset in timestamp calculation. Signed-off-by: Damien George --- ports/unix/fatfs_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/unix/fatfs_port.c b/ports/unix/fatfs_port.c index 4ec5b919ab54b..9e0f444ce2576 100644 --- a/ports/unix/fatfs_port.c +++ b/ports/unix/fatfs_port.c @@ -5,7 +5,7 @@ DWORD get_fattime(void) { time_t now = time(NULL); struct tm *tm = localtime(&now); return ((1900 + tm->tm_year - 1980) << 25) - | (tm->tm_mon << 21) + | ((tm->tm_mon + 1) << 21) | (tm->tm_mday << 16) | (tm->tm_hour << 11) | (tm->tm_min << 5) From 0e6ef403598a7a5fbc44b28e43c264c24aa02416 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Aug 2020 15:01:10 +1000 Subject: [PATCH 236/352] tests/extmod: Add tests for verifying FAT and littlefs mtime values. Verifies mtime timestamps on files match the value returned by time.time(). Also update vfs_fat_ramdisk.py so it doesn't check FAT timestamp of the root, because that may change across runs/ports. Signed-off-by: Damien George --- tests/extmod/vfs_fat_mtime.py | 74 +++++++++++++++++++++++++++++ tests/extmod/vfs_fat_mtime.py.exp | 3 ++ tests/extmod/vfs_fat_ramdisk.py | 2 +- tests/extmod/vfs_fat_ramdisk.py.exp | 2 +- tests/extmod/vfs_lfs_mtime.py | 9 +++- tests/extmod/vfs_lfs_mtime.py.exp | 1 + 6 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 tests/extmod/vfs_fat_mtime.py create mode 100644 tests/extmod/vfs_fat_mtime.py.exp diff --git a/tests/extmod/vfs_fat_mtime.py b/tests/extmod/vfs_fat_mtime.py new file mode 100644 index 0000000000000..d8fd66b75f769 --- /dev/null +++ b/tests/extmod/vfs_fat_mtime.py @@ -0,0 +1,74 @@ +# Test for VfsFat using a RAM device, mtime feature + +try: + import utime, uos + + utime.time + utime.sleep + uos.VfsFat +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + ERASE_BLOCK_SIZE = 512 + + def __init__(self, blocks): + self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) + + def readblocks(self, block, buf): + addr = block * self.ERASE_BLOCK_SIZE + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def writeblocks(self, block, buf): + addr = block * self.ERASE_BLOCK_SIZE + for i in range(len(buf)): + self.data[addr + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.ERASE_BLOCK_SIZE + if op == 5: # block size + return self.ERASE_BLOCK_SIZE + + +def test(bdev, vfs_class): + print("test", vfs_class) + + # Initial format of block device. + vfs_class.mkfs(bdev) + + # construction + vfs = vfs_class(bdev) + + # Create an empty file, should have a timestamp. + current_time = int(utime.time()) + vfs.open("test1", "wt").close() + + # Wait 2 seconds so mtime will increase (FAT has 2 second resolution). + utime.sleep(2) + + # Create another empty file, should have a timestamp. + vfs.open("test2", "wt").close() + + # Stat the files and check mtime is non-zero. + stat1 = vfs.stat("test1") + stat2 = vfs.stat("test2") + print(stat1[8] != 0, stat2[8] != 0) + + # Check that test1 has mtime which matches time.time() at point of creation. + # TODO this currently fails on the unix port because FAT stores timestamps + # in localtime and stat() is UTC based. + # print(current_time - 1 <= stat1[8] <= current_time + 1) + + # Check that test1 is older than test2. + print(stat1[8] < stat2[8]) + + # Unmount. + vfs.umount() + + +bdev = RAMBlockDevice(50) +test(bdev, uos.VfsFat) diff --git a/tests/extmod/vfs_fat_mtime.py.exp b/tests/extmod/vfs_fat_mtime.py.exp new file mode 100644 index 0000000000000..55550b9834e5b --- /dev/null +++ b/tests/extmod/vfs_fat_mtime.py.exp @@ -0,0 +1,3 @@ +test +True True +True diff --git a/tests/extmod/vfs_fat_ramdisk.py b/tests/extmod/vfs_fat_ramdisk.py index 5f758bf89c87a..9a68d94fedefa 100644 --- a/tests/extmod/vfs_fat_ramdisk.py +++ b/tests/extmod/vfs_fat_ramdisk.py @@ -63,7 +63,7 @@ def ioctl(self, op, arg): f.write("hello!") print(list(vfs.ilistdir())) -print("stat root:", vfs.stat("/")) +print("stat root:", vfs.stat("/")[:-3]) # timestamps differ across runs print("stat file:", vfs.stat("foo_file.txt")[:-3]) # timestamps differ across runs print(b"FOO_FILETXT" in bdev.data) diff --git a/tests/extmod/vfs_fat_ramdisk.py.exp b/tests/extmod/vfs_fat_ramdisk.py.exp index ef6cf1e72b62f..384fa64c76510 100644 --- a/tests/extmod/vfs_fat_ramdisk.py.exp +++ b/tests/extmod/vfs_fat_ramdisk.py.exp @@ -4,7 +4,7 @@ statvfs: (512, 512, 16, 16, 16, 0, 0, 0, 0, 255) getcwd: / True [('foo_file.txt', 32768, 0, 6)] -stat root: (16384, 0, 0, 0, 0, 0, 0, 0, 0, 0) +stat root: (16384, 0, 0, 0, 0, 0, 0) stat file: (32768, 0, 0, 0, 0, 0, 6) True True diff --git a/tests/extmod/vfs_lfs_mtime.py b/tests/extmod/vfs_lfs_mtime.py index 0108076884055..bacdd2246c6d6 100644 --- a/tests/extmod/vfs_lfs_mtime.py +++ b/tests/extmod/vfs_lfs_mtime.py @@ -3,6 +3,7 @@ try: import utime, uos + utime.time utime.sleep uos.VfsLfs2 except (ImportError, AttributeError): @@ -46,6 +47,7 @@ def test(bdev, vfs_class): vfs = vfs_class(bdev, mtime=True) # Create an empty file, should have a timestamp. + current_time = int(utime.time()) vfs.open("test1", "wt").close() # Wait 1 second so mtime will increase by at least 1. @@ -54,10 +56,15 @@ def test(bdev, vfs_class): # Create another empty file, should have a timestamp. vfs.open("test2", "wt").close() - # Stat the files and check that test1 is older than test2. + # Stat the files and check mtime is non-zero. stat1 = vfs.stat("test1") stat2 = vfs.stat("test2") print(stat1[8] != 0, stat2[8] != 0) + + # Check that test1 has mtime which matches time.time() at point of creation. + print(current_time <= stat1[8] <= current_time + 1) + + # Check that test1 is older than test2. print(stat1[8] < stat2[8]) # Wait 1 second so mtime will increase by at least 1. diff --git a/tests/extmod/vfs_lfs_mtime.py.exp b/tests/extmod/vfs_lfs_mtime.py.exp index 2e3d7491bcbd8..cf6455c04015b 100644 --- a/tests/extmod/vfs_lfs_mtime.py.exp +++ b/tests/extmod/vfs_lfs_mtime.py.exp @@ -4,6 +4,7 @@ True True True True True +True mtime=False True True From b0932fcf2e2f9a81abf7737ed4b2573bd9ad4a49 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Sep 2020 12:01:26 +1000 Subject: [PATCH 237/352] all: Bump version to 1.13. Signed-off-by: Damien George --- docs/conf.py | 2 +- py/mpconfig.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7dc039d6ca599..460886b4da2e8 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -74,7 +74,7 @@ # # We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags" # breakdown, so use the same version identifier for both to avoid confusion. -version = release = '1.12' +version = release = '1.13' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/py/mpconfig.h b/py/mpconfig.h index 3d9ce8bb55c95..ae4cabfdcf0f2 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -28,7 +28,7 @@ // Current version of MicroPython #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 12 +#define MICROPY_VERSION_MINOR 13 #define MICROPY_VERSION_MICRO 0 // Combined version as a 32-bit number for convenience From 40ad8f1666b265dafc7844d765f45cfae4b6299f Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 18 Jun 2020 11:19:14 +0200 Subject: [PATCH 238/352] all: Rename "sys" module to "usys". This is consistent with the other 'micro' modules and allows implementing additional features in Python via e.g. micropython-lib's sys. Note this is a breaking change (not backwards compatible) for ports which do not enable weak links, as "import sys" must now be replaced with "import usys". --- docs/library/index.rst | 2 +- docs/library/{sys.rst => usys.rst} | 14 +++++++------- drivers/nrf24l01/nrf24l01test.py | 10 +++++----- extmod/webrepl/websocket_helper.py | 5 ++++- py/objmodule.c | 2 +- tests/basics/async_await2.py | 5 ++++- tests/basics/async_for2.py | 5 ++++- tests/basics/async_with2.py | 5 ++++- tests/basics/attrtuple1.py | 5 ++++- tests/basics/builtin_callable.py | 5 ++++- tests/basics/builtin_dir.py | 5 ++++- tests/basics/int_big1.py | 5 ++++- tests/basics/module2.py | 4 ++-- tests/basics/python34.py | 6 +++--- tests/basics/string_compare.py | 5 ++++- tests/basics/sys1.py | 5 ++++- tests/basics/sys_exit.py | 5 ++++- tests/basics/sys_getsizeof.py | 5 ++++- tests/extmod/uctypes_array_assign_native_le.py | 4 ++-- .../uctypes_array_assign_native_le_intbig.py | 4 ++-- tests/extmod/uctypes_native_le.py | 4 ++-- tests/extmod/uctypes_ptr_le.py | 4 ++-- tests/extmod/uctypes_ptr_native_le.py | 4 ++-- tests/extmod/vfs_fat_more.py | 6 +++--- tests/extmod/vfs_lfs_mount.py | 10 +++++----- tests/extmod/vfs_userfs.py | 6 +++--- tests/feature_check/byteorder.py | 5 ++++- tests/float/float2int_doubleprec_intbig.py | 4 ++-- tests/float/float2int_fp30_intbig.py | 4 ++-- tests/float/float2int_intbig.py | 4 ++-- tests/io/argv.py | 5 ++++- tests/io/builtin_print_file.py | 5 ++++- tests/io/file_stdio.py | 5 ++++- tests/io/resource_stream.py | 4 ++-- tests/micropython/emg_exc.py | 4 ++-- tests/micropython/heapalloc_traceback.py | 4 ++-- tests/micropython/import_mpy_invalid.py | 6 +++--- tests/micropython/import_mpy_native_x64.py | 8 ++++---- tests/micropython/opt_level_lineno.py | 2 +- tests/micropython/viper_misc_intbig.py | 4 ++-- tests/misc/print_exception.py | 4 ++-- tests/misc/sys_atexit.py | 6 +++--- tests/misc/sys_exc_info.py | 5 ++++- tests/run-natmodtests.py | 4 ++-- tests/run-tests-exp.py | 2 +- tests/thread/thread_stacksize1.py | 6 ++++-- 46 files changed, 142 insertions(+), 89 deletions(-) rename docs/library/{sys.rst => usys.rst} (93%) diff --git a/docs/library/index.rst b/docs/library/index.rst index 952e67d43ccec..43d9e87f3cda7 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -77,7 +77,6 @@ it will fallback to loading the built-in ``ujson`` module. cmath.rst gc.rst math.rst - sys.rst uarray.rst uasyncio.rst ubinascii.rst @@ -93,6 +92,7 @@ it will fallback to loading the built-in ``ujson`` module. usocket.rst ussl.rst ustruct.rst + usys.rst utime.rst uzlib.rst _thread.rst diff --git a/docs/library/sys.rst b/docs/library/usys.rst similarity index 93% rename from docs/library/sys.rst rename to docs/library/usys.rst index 24f9e353bb006..96016438507cf 100644 --- a/docs/library/sys.rst +++ b/docs/library/usys.rst @@ -1,7 +1,7 @@ -:mod:`sys` -- system specific functions -======================================= +:mod:`usys` -- system specific functions +======================================== -.. module:: sys +.. module:: usys :synopsis: system specific functions |see_cpython_module| :mod:`python:sys`. @@ -28,10 +28,10 @@ Functions This function is a MicroPython extension intended to provide similar functionality to the :mod:`atexit` module in CPython. -.. function:: print_exception(exc, file=sys.stdout, /) +.. function:: print_exception(exc, file=usys.stdout, /) Print exception with a traceback to a file-like object *file* (or - `sys.stdout` by default). + `usys.stdout` by default). .. admonition:: Difference to CPython :class: attention @@ -84,7 +84,7 @@ Constants value directly, but instead count number of bits in it:: bits = 0 - v = sys.maxsize + v = usys.maxsize while v: bits += 1 v >>= 1 @@ -113,7 +113,7 @@ Constants is an identifier of a board, e.g. ``"pyboard"`` for the original MicroPython reference board. It thus can be used to distinguish one board from another. If you need to check whether your program runs on MicroPython (vs other - Python implementation), use `sys.implementation` instead. + Python implementation), use `usys.implementation` instead. .. data:: stderr diff --git a/drivers/nrf24l01/nrf24l01test.py b/drivers/nrf24l01/nrf24l01test.py index 14efbffd2aba1..56bdb6e26eb9c 100644 --- a/drivers/nrf24l01/nrf24l01test.py +++ b/drivers/nrf24l01/nrf24l01test.py @@ -1,6 +1,6 @@ """Test for nrf24l01 module. Portable between MicroPython targets.""" -import sys +import usys import ustruct as struct import utime from machine import Pin, SPI @@ -14,14 +14,14 @@ # master may be a slow device. Value tested with Pyboard, ESP32 and ESP8266. _SLAVE_SEND_DELAY = const(10) -if sys.platform == "pyboard": +if usys.platform == "pyboard": cfg = {"spi": 2, "miso": "Y7", "mosi": "Y8", "sck": "Y6", "csn": "Y5", "ce": "Y4"} -elif sys.platform == "esp8266": # Hardware SPI +elif usys.platform == "esp8266": # Hardware SPI cfg = {"spi": 1, "miso": 12, "mosi": 13, "sck": 14, "csn": 4, "ce": 5} -elif sys.platform == "esp32": # Software SPI +elif usys.platform == "esp32": # Software SPI cfg = {"spi": -1, "miso": 32, "mosi": 33, "sck": 25, "csn": 26, "ce": 27} else: - raise ValueError("Unsupported platform {}".format(sys.platform)) + raise ValueError("Unsupported platform {}".format(usys.platform)) # Addresses are in little-endian format. They correspond to big-endian # 0xf0f0f0f0e1, 0xf0f0f0f0d2 diff --git a/extmod/webrepl/websocket_helper.py b/extmod/webrepl/websocket_helper.py index 5ca80534eb33f..3260acc52f377 100644 --- a/extmod/webrepl/websocket_helper.py +++ b/extmod/webrepl/websocket_helper.py @@ -1,4 +1,7 @@ -import sys +try: + import usys as sys +except ImportError: + import sys try: import ubinascii as binascii diff --git a/py/objmodule.c b/py/objmodule.c index 060e1bc1e6c2d..a1f9d9d7f146a 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -159,7 +159,7 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #endif #endif #if MICROPY_PY_SYS - { MP_ROM_QSTR(MP_QSTR_sys), MP_ROM_PTR(&mp_module_sys) }, + { MP_ROM_QSTR(MP_QSTR_usys), MP_ROM_PTR(&mp_module_sys) }, #endif #if MICROPY_PY_GC && MICROPY_ENABLE_GC { MP_ROM_QSTR(MP_QSTR_gc), MP_ROM_PTR(&mp_module_gc) }, diff --git a/tests/basics/async_await2.py b/tests/basics/async_await2.py index 1e3164df93372..56f77604ac1be 100644 --- a/tests/basics/async_await2.py +++ b/tests/basics/async_await2.py @@ -1,6 +1,9 @@ # test await expression -import sys +try: + import usys as sys +except ImportError: + import sys if sys.implementation.name == 'micropython': # uPy allows normal generators to be awaitables coroutine = lambda f: f diff --git a/tests/basics/async_for2.py b/tests/basics/async_for2.py index aad23a3e5ad8c..4af3be4c6df32 100644 --- a/tests/basics/async_for2.py +++ b/tests/basics/async_for2.py @@ -1,6 +1,9 @@ # test waiting within "async for" __anext__ function -import sys +try: + import usys as sys +except ImportError: + import sys if sys.implementation.name == 'micropython': # uPy allows normal generators to be awaitables coroutine = lambda f: f diff --git a/tests/basics/async_with2.py b/tests/basics/async_with2.py index 44421ae91798e..4dd1386240f2c 100644 --- a/tests/basics/async_with2.py +++ b/tests/basics/async_with2.py @@ -1,6 +1,9 @@ # test waiting within async with enter/exit functions -import sys +try: + import usys as sys +except ImportError: + import sys if sys.implementation.name == 'micropython': # uPy allows normal generators to be awaitables coroutine = lambda f: f diff --git a/tests/basics/attrtuple1.py b/tests/basics/attrtuple1.py index 78a0fbed1b73a..249c030bb4fdb 100644 --- a/tests/basics/attrtuple1.py +++ b/tests/basics/attrtuple1.py @@ -1,7 +1,10 @@ # test attrtuple # we can't test this type directly so we use sys.implementation object -import sys +try: + import usys as sys +except ImportError: + import sys t = sys.implementation # It can be just a normal tuple on small ports diff --git a/tests/basics/builtin_callable.py b/tests/basics/builtin_callable.py index 3ae49f004d18a..c0a9d0c473a5a 100644 --- a/tests/basics/builtin_callable.py +++ b/tests/basics/builtin_callable.py @@ -7,7 +7,10 @@ print(callable("dfsd")) # modules should not be callabe -import sys +try: + import usys as sys +except ImportError: + import sys print(callable(sys)) # builtins should be callable diff --git a/tests/basics/builtin_dir.py b/tests/basics/builtin_dir.py index 1eecbd044b7b0..1f2b498d77f77 100644 --- a/tests/basics/builtin_dir.py +++ b/tests/basics/builtin_dir.py @@ -4,7 +4,10 @@ print('__name__' in dir()) # dir of module -import sys +try: + import usys as sys +except ImportError: + import sys print('version' in dir(sys)) # dir of type diff --git a/tests/basics/int_big1.py b/tests/basics/int_big1.py index 40d16c455bf38..108e3ee5c9626 100644 --- a/tests/basics/int_big1.py +++ b/tests/basics/int_big1.py @@ -102,7 +102,10 @@ x = -4611686018427387904 # big # sys.maxsize is a constant mpz, so test it's compatible with dynamic ones -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.maxsize + 1 - 1 == sys.maxsize) # test extraction of big int value via mp_obj_get_int_maybe diff --git a/tests/basics/module2.py b/tests/basics/module2.py index a135601579cd7..5923a27e08d42 100644 --- a/tests/basics/module2.py +++ b/tests/basics/module2.py @@ -1,6 +1,6 @@ # uPy behaviour only: builtin modules are read-only -import sys +import usys try: - sys.x = 1 + usys.x = 1 except AttributeError: print("AttributeError") diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 4030db143c4d7..0f6e4bafd0b9e 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -33,9 +33,9 @@ def test_syntax(code): # from basics/sys1.py # uPy prints version 3.4 -import sys -print(sys.version[:3]) -print(sys.version_info[0], sys.version_info[1]) +import usys +print(usys.version[:3]) +print(usys.version_info[0], usys.version_info[1]) # from basics/exception1.py # in 3.7 no comma is printed if there is only 1 arg (in 3.4-3.6 one is printed) diff --git a/tests/basics/string_compare.py b/tests/basics/string_compare.py index 6515809b3642d..f34879df25bcb 100644 --- a/tests/basics/string_compare.py +++ b/tests/basics/string_compare.py @@ -51,7 +51,10 @@ # this tests an internal string that doesn't have a hash with a string # that does have a hash, but the lengths of the two strings are different -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.version == 'a long string that has a hash') # this special string would have a hash of 0 but is incremented to 1 diff --git a/tests/basics/sys1.py b/tests/basics/sys1.py index 095824afaf1fc..0947ea1964bf9 100644 --- a/tests/basics/sys1.py +++ b/tests/basics/sys1.py @@ -1,6 +1,9 @@ # test sys module -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.__name__) print(type(sys.path)) diff --git a/tests/basics/sys_exit.py b/tests/basics/sys_exit.py index b1f71549db1c5..4d640ae60e4e9 100644 --- a/tests/basics/sys_exit.py +++ b/tests/basics/sys_exit.py @@ -1,6 +1,9 @@ # test sys module's exit function -import sys +try: + import usys as sys +except ImportError: + import sys try: sys.exit diff --git a/tests/basics/sys_getsizeof.py b/tests/basics/sys_getsizeof.py index fe1b403e04bbf..4dc919848ca4c 100644 --- a/tests/basics/sys_getsizeof.py +++ b/tests/basics/sys_getsizeof.py @@ -1,6 +1,9 @@ # test sys.getsizeof() function -import sys +try: + import usys as sys +except ImportError: + import sys try: sys.getsizeof except AttributeError: diff --git a/tests/extmod/uctypes_array_assign_native_le.py b/tests/extmod/uctypes_array_assign_native_le.py index d4c27fc4b3f5a..5bddfcdf2f601 100644 --- a/tests/extmod/uctypes_array_assign_native_le.py +++ b/tests/extmod/uctypes_array_assign_native_le.py @@ -1,4 +1,4 @@ -import sys +import usys try: import uctypes @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -if sys.byteorder != "little": +if usys.byteorder != "little": print("SKIP") raise SystemExit diff --git a/tests/extmod/uctypes_array_assign_native_le_intbig.py b/tests/extmod/uctypes_array_assign_native_le_intbig.py index f33c63b4ef959..42583b8afefef 100644 --- a/tests/extmod/uctypes_array_assign_native_le_intbig.py +++ b/tests/extmod/uctypes_array_assign_native_le_intbig.py @@ -1,4 +1,4 @@ -import sys +import usys try: import uctypes @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -if sys.byteorder != "little": +if usys.byteorder != "little": print("SKIP") raise SystemExit diff --git a/tests/extmod/uctypes_native_le.py b/tests/extmod/uctypes_native_le.py index 7958e5c22ada9..9889b98e9d00f 100644 --- a/tests/extmod/uctypes_native_le.py +++ b/tests/extmod/uctypes_native_le.py @@ -1,7 +1,7 @@ # This test is exactly like uctypes_le.py, but uses native structure layout. # Codepaths for packed vs native structures are different. This test only works # on little-endian machine (no matter if 32 or 64 bit). -import sys +import usys try: import uctypes @@ -9,7 +9,7 @@ print("SKIP") raise SystemExit -if sys.byteorder != "little": +if usys.byteorder != "little": print("SKIP") raise SystemExit diff --git a/tests/extmod/uctypes_ptr_le.py b/tests/extmod/uctypes_ptr_le.py index 5d8094ee48a79..f475465ae80f8 100644 --- a/tests/extmod/uctypes_ptr_le.py +++ b/tests/extmod/uctypes_ptr_le.py @@ -1,4 +1,4 @@ -import sys +import usys try: import uctypes @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -if sys.byteorder != "little": +if usys.byteorder != "little": print("SKIP") raise SystemExit diff --git a/tests/extmod/uctypes_ptr_native_le.py b/tests/extmod/uctypes_ptr_native_le.py index 8ca4d2c55cf9c..ca2b316c54a5d 100644 --- a/tests/extmod/uctypes_ptr_native_le.py +++ b/tests/extmod/uctypes_ptr_native_le.py @@ -1,4 +1,4 @@ -import sys +import usys try: import uctypes @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -if sys.byteorder != "little": +if usys.byteorder != "little": print("SKIP") raise SystemExit diff --git a/tests/extmod/vfs_fat_more.py b/tests/extmod/vfs_fat_more.py index d8449829deacb..076802f6ad5eb 100644 --- a/tests/extmod/vfs_fat_more.py +++ b/tests/extmod/vfs_fat_more.py @@ -111,10 +111,10 @@ def ioctl(self, op, arg): print(uos.listdir("sys")) # test importing a file from a mounted FS -import sys +import usys -sys.path.clear() -sys.path.append("/sys") +usys.path.clear() +usys.path.append("/sys") with open("sys/test_module.py", "w") as f: f.write('print("test_module!")') import test_module diff --git a/tests/extmod/vfs_lfs_mount.py b/tests/extmod/vfs_lfs_mount.py index 9207f4a8c6d05..2c40b2989796f 100644 --- a/tests/extmod/vfs_lfs_mount.py +++ b/tests/extmod/vfs_lfs_mount.py @@ -68,17 +68,17 @@ def test(bdev, vfs_class): uos.umount("/lfs") # clear imported modules - sys.modules.clear() + usys.modules.clear() bdev = RAMBlockDevice(30) # initialise path -import sys +import usys -sys.path.clear() -sys.path.append("/lfs") -sys.path.append("") +usys.path.clear() +usys.path.append("/lfs") +usys.path.append("") # run tests test(bdev, uos.VfsLfs1) diff --git a/tests/extmod/vfs_userfs.py b/tests/extmod/vfs_userfs.py index 3cdfe82eea8dc..570b8335341e1 100644 --- a/tests/extmod/vfs_userfs.py +++ b/tests/extmod/vfs_userfs.py @@ -1,7 +1,7 @@ # test VFS functionality with a user-defined filesystem # also tests parts of uio.IOBase implementation -import sys +import usys try: import uio @@ -76,9 +76,9 @@ def open(self, path, mode): print(f.read()) # import files from the user filesystem -sys.path.append("/userfs") +usys.path.append("/userfs") import usermod1 # unmount and undo path addition uos.umount("/userfs") -sys.path.pop() +usys.path.pop() diff --git a/tests/feature_check/byteorder.py b/tests/feature_check/byteorder.py index c82a41a24b1c0..509bd8b1b51de 100644 --- a/tests/feature_check/byteorder.py +++ b/tests/feature_check/byteorder.py @@ -1,3 +1,6 @@ -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.byteorder) diff --git a/tests/float/float2int_doubleprec_intbig.py b/tests/float/float2int_doubleprec_intbig.py index 84fa2d25360e1..418eddb60781a 100644 --- a/tests/float/float2int_doubleprec_intbig.py +++ b/tests/float/float2int_doubleprec_intbig.py @@ -2,10 +2,10 @@ try: import ustruct as struct + import usys as sys except: import struct - -import sys + import sys maxsize_bits = 0 maxsize = sys.maxsize diff --git a/tests/float/float2int_fp30_intbig.py b/tests/float/float2int_fp30_intbig.py index 75ff52f39dd49..fcbe2e309f0be 100644 --- a/tests/float/float2int_fp30_intbig.py +++ b/tests/float/float2int_fp30_intbig.py @@ -2,10 +2,10 @@ try: import ustruct as struct + import usys as sys except: import struct - -import sys + import sys maxsize_bits = 0 maxsize = sys.maxsize diff --git a/tests/float/float2int_intbig.py b/tests/float/float2int_intbig.py index 62aca39634968..865aeea7b9171 100644 --- a/tests/float/float2int_intbig.py +++ b/tests/float/float2int_intbig.py @@ -2,10 +2,10 @@ try: import ustruct as struct + import usys as sys except: import struct - -import sys + import sys maxsize_bits = 0 maxsize = sys.maxsize diff --git a/tests/io/argv.py b/tests/io/argv.py index 53254da11906b..834292504d05d 100644 --- a/tests/io/argv.py +++ b/tests/io/argv.py @@ -1,3 +1,6 @@ -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.argv) diff --git a/tests/io/builtin_print_file.py b/tests/io/builtin_print_file.py index 822356a6cc305..e5c20b64e4bec 100644 --- a/tests/io/builtin_print_file.py +++ b/tests/io/builtin_print_file.py @@ -1,6 +1,9 @@ # test builtin print function, using file= argument -import sys +try: + import usys as sys +except ImportError: + import sys try: sys.stdout diff --git a/tests/io/file_stdio.py b/tests/io/file_stdio.py index cbdb070163c31..6c08f35d78784 100644 --- a/tests/io/file_stdio.py +++ b/tests/io/file_stdio.py @@ -1,4 +1,7 @@ -import sys +try: + import usys as sys +except ImportError: + import sys print(sys.stdin.fileno()) print(sys.stdout.fileno()) diff --git a/tests/io/resource_stream.py b/tests/io/resource_stream.py index 5656205b692d6..b589ff99bf2ba 100644 --- a/tests/io/resource_stream.py +++ b/tests/io/resource_stream.py @@ -1,5 +1,5 @@ import uio -import sys +import usys try: uio.resource_stream @@ -11,5 +11,5 @@ print(buf.read()) # resource_stream(None, ...) look ups from current dir, hence sys.path[0] hack -buf = uio.resource_stream(None, sys.path[0] + "/data/file2") +buf = uio.resource_stream(None, usys.path[0] + "/data/file2") print(buf.read()) diff --git a/tests/micropython/emg_exc.py b/tests/micropython/emg_exc.py index bca4d2d9fbaca..b8df94b071af8 100644 --- a/tests/micropython/emg_exc.py +++ b/tests/micropython/emg_exc.py @@ -1,7 +1,7 @@ # test that emergency exceptions work import micropython -import sys +import usys try: import uio @@ -26,7 +26,7 @@ def f(): # print the exception buf = uio.StringIO() - sys.print_exception(exc, buf) + usys.print_exception(exc, buf) for l in buf.getvalue().split("\n"): if l.startswith(" File "): print(l.split('"')[2]) diff --git a/tests/micropython/heapalloc_traceback.py b/tests/micropython/heapalloc_traceback.py index eabd09388b90b..09a2ad2c14d6e 100644 --- a/tests/micropython/heapalloc_traceback.py +++ b/tests/micropython/heapalloc_traceback.py @@ -1,7 +1,7 @@ # test that we can generate a traceback without allocating import micropython -import sys +import usys try: import uio @@ -33,7 +33,7 @@ def test(): # print the exception that was raised buf = uio.StringIO() -sys.print_exception(global_exc, buf) +usys.print_exception(global_exc, buf) for l in buf.getvalue().split("\n"): # uPy on pyboard prints as file, so remove filename. if l.startswith(" File "): diff --git a/tests/micropython/import_mpy_invalid.py b/tests/micropython/import_mpy_invalid.py index 00973fe3f9f12..02fd4b1254971 100644 --- a/tests/micropython/import_mpy_invalid.py +++ b/tests/micropython/import_mpy_invalid.py @@ -1,7 +1,7 @@ # test importing of invalid .mpy files try: - import sys, uio, uos + import usys, uio, uos uio.IOBase uos.mount @@ -54,7 +54,7 @@ def open(self, path, mode): # create and mount a user filesystem uos.mount(UserFS(user_files), "/userfs") -sys.path.append("/userfs") +usys.path.append("/userfs") # import .mpy files from the user filesystem for i in range(len(user_files)): @@ -66,4 +66,4 @@ def open(self, path, mode): # unmount and undo path addition uos.umount("/userfs") -sys.path.pop() +usys.path.pop() diff --git a/tests/micropython/import_mpy_native_x64.py b/tests/micropython/import_mpy_native_x64.py index d0de507d44234..3e7b2058eb812 100644 --- a/tests/micropython/import_mpy_native_x64.py +++ b/tests/micropython/import_mpy_native_x64.py @@ -1,7 +1,7 @@ # test importing of .mpy files with native code (x64 only) try: - import sys, uio, uos + import usys, uio, uos uio.IOBase uos.mount @@ -9,7 +9,7 @@ print("SKIP") raise SystemExit -if not (sys.platform == "linux" and sys.maxsize > 2 ** 32): +if not (usys.platform == "linux" and usys.maxsize > 2 ** 32): print("SKIP") raise SystemExit @@ -101,7 +101,7 @@ def open(self, path, mode): # create and mount a user filesystem uos.mount(UserFS(user_files), "/userfs") -sys.path.append("/userfs") +usys.path.append("/userfs") # import .mpy files from the user filesystem for i in range(len(user_files)): @@ -114,4 +114,4 @@ def open(self, path, mode): # unmount and undo path addition uos.umount("/userfs") -sys.path.pop() +usys.path.pop() diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py index d8253e54b41f0..1cbf2fb1a8548 100644 --- a/tests/micropython/opt_level_lineno.py +++ b/tests/micropython/opt_level_lineno.py @@ -3,4 +3,4 @@ # check that level 3 doesn't store line numbers # the expected output is that any line is printed as "line 1" micropython.opt_level(3) -exec("try:\n xyz\nexcept NameError as er:\n import sys\n sys.print_exception(er)") +exec("try:\n xyz\nexcept NameError as er:\n import usys\n usys.print_exception(er)") diff --git a/tests/micropython/viper_misc_intbig.py b/tests/micropython/viper_misc_intbig.py index 055c08d8e53dc..eda873ca63dee 100644 --- a/tests/micropython/viper_misc_intbig.py +++ b/tests/micropython/viper_misc_intbig.py @@ -6,6 +6,6 @@ def viper_uint() -> uint: return uint(-1) -import sys +import usys -print(viper_uint() == (sys.maxsize << 1 | 1)) +print(viper_uint() == (usys.maxsize << 1 | 1)) diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py index f26c1fd5ac867..9b3467b0a0f84 100644 --- a/tests/misc/print_exception.py +++ b/tests/misc/print_exception.py @@ -1,10 +1,10 @@ -import sys - try: try: import uio as io + import usys as sys except ImportError: import io + import sys except ImportError: print("SKIP") raise SystemExit diff --git a/tests/misc/sys_atexit.py b/tests/misc/sys_atexit.py index e9c5693f975e3..141b24cc9f62b 100644 --- a/tests/misc/sys_atexit.py +++ b/tests/misc/sys_atexit.py @@ -1,9 +1,9 @@ # test sys.atexit() function -import sys +import usys try: - sys.atexit + usys.atexit except AttributeError: print("SKIP") raise SystemExit @@ -15,7 +15,7 @@ def do_at_exit(): print("done at exit:", some_var) -sys.atexit(do_at_exit) +usys.atexit(do_at_exit) some_var = "ok" print("done before exit") diff --git a/tests/misc/sys_exc_info.py b/tests/misc/sys_exc_info.py index d7e8a2d943b5e..3a8c4a6c8d3ed 100644 --- a/tests/misc/sys_exc_info.py +++ b/tests/misc/sys_exc_info.py @@ -1,4 +1,7 @@ -import sys +try: + import usys as sys +except ImportError: + import sys try: sys.exc_info diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index f88ca0b788971..8eb27169c4ca1 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -30,7 +30,7 @@ # Code to allow a target MicroPython to import an .mpy from RAM injected_import_hook_code = """\ -import sys, uos, uio +import usys, uos, uio class __File(uio.IOBase): def __init__(self): self.off = 0 @@ -54,7 +54,7 @@ def open(self, path, mode): return __File() uos.mount(__FS(), '/__remote') uos.chdir('/__remote') -sys.modules['{}'] = __import__('__injected') +usys.modules['{}'] = __import__('__injected') """ diff --git a/tests/run-tests-exp.py b/tests/run-tests-exp.py index 34c6f650f83b6..ac32fe9869c36 100644 --- a/tests/run-tests-exp.py +++ b/tests/run-tests-exp.py @@ -5,7 +5,7 @@ # This script is intended to be run by the same interpreter executable # which is to be tested, so should use minimal language functionality. # -import sys +import usys as sys import uos as os diff --git a/tests/thread/thread_stacksize1.py b/tests/thread/thread_stacksize1.py index 5827237a175eb..5d25509b763a0 100644 --- a/tests/thread/thread_stacksize1.py +++ b/tests/thread/thread_stacksize1.py @@ -1,8 +1,10 @@ # test setting the thread stack size # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd - -import sys +try: + import usys as sys +except ImportError: + import sys import _thread # different implementations have different minimum sizes From 38959ed8f11970d47f4e07b720a590e65e80dc35 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 00:32:39 +1000 Subject: [PATCH 239/352] lib/libm: Reduce size of static two_over_pi array. Thanks to Jeff Epler for the idea. Signed-off-by: Damien George --- lib/libm/ef_rem_pio2.c | 4 ++-- lib/libm/fdlibm.h | 2 +- lib/libm/kf_rem_pio2.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/libm/ef_rem_pio2.c b/lib/libm/ef_rem_pio2.c index 8111ca197f0e4..fcdfb18287e67 100644 --- a/lib/libm/ef_rem_pio2.c +++ b/lib/libm/ef_rem_pio2.c @@ -35,9 +35,9 @@ * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi */ #ifdef __STDC__ -static const __int32_t two_over_pi[] = { +static const __uint8_t two_over_pi[] = { #else -static __int32_t two_over_pi[] = { +static __uint8_t two_over_pi[] = { #endif 0xA2, 0xF9, 0x83, 0x6E, 0x4E, 0x44, 0x15, 0x29, 0xFC, 0x27, 0x57, 0xD1, 0xF5, 0x34, 0xDD, 0xC0, 0xDB, 0x62, diff --git a/lib/libm/fdlibm.h b/lib/libm/fdlibm.h index ace3b2da22f94..22cd8f16b4c12 100644 --- a/lib/libm/fdlibm.h +++ b/lib/libm/fdlibm.h @@ -188,7 +188,7 @@ extern float __ieee754_scalbf __P((float,float)); extern float __kernel_sinf __P((float,float,int)); extern float __kernel_cosf __P((float,float)); extern float __kernel_tanf __P((float,float,int)); -extern int __kernel_rem_pio2f __P((float*,float*,int,int,int,const __int32_t*)); +extern int __kernel_rem_pio2f __P((float*,float*,int,int,int,const __uint8_t*)); /* A union which permits us to convert between a float and a 32 bit int. */ diff --git a/lib/libm/kf_rem_pio2.c b/lib/libm/kf_rem_pio2.c index 6bd4d287e6905..43e4b4687f506 100644 --- a/lib/libm/kf_rem_pio2.c +++ b/lib/libm/kf_rem_pio2.c @@ -62,10 +62,10 @@ two8 = 2.5600000000e+02f, /* 0x43800000 */ twon8 = 3.9062500000e-03f; /* 0x3b800000 */ #ifdef __STDC__ - int __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec, const __int32_t *ipio2) + int __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec, const __uint8_t *ipio2) #else int __kernel_rem_pio2f(x,y,e0,nx,prec,ipio2) - float x[], y[]; int e0,nx,prec; __int32_t ipio2[]; + float x[], y[]; int e0,nx,prec; __uint8_t ipio2[]; #endif { __int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; From 5e69926ea06cc035e831fcb657e756764682e0b5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 01:04:08 +1000 Subject: [PATCH 240/352] stm32/mpconfigport.h: Enable MICROPY_PY_REVERSE_SPECIAL_METHODS. It's a useful core feature. Signed-off-by: Damien George --- ports/stm32/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 6f38d90e8d800..14a05898951ba 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -100,6 +100,7 @@ #define MICROPY_PY_BUILTINS_SLICE_INDICES (1) #define MICROPY_PY_BUILTINS_ROUND_INT (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_COMPILE (MICROPY_ENABLE_COMPILER) #define MICROPY_PY_BUILTINS_EXECFILE (MICROPY_ENABLE_COMPILER) #define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) From 3ff70792770e4591fec22fa6a1b50f492236fcde Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 12:40:38 +1000 Subject: [PATCH 241/352] lib/utils/mpirq: Add mp_irq_init func, and clean up unused init method. mp_irq_init() is useful when the IRQ object is allocated by the caller. The mp_irq_methods_t.init method is not used anywhere so has been removed. Signed-off-by: Damien George --- lib/utils/mpirq.c | 6 +++++- lib/utils/mpirq.h | 14 +++++++------- ports/stm32/machine_uart.c | 1 - ports/zephyr/machine_pin.c | 7 +------ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/utils/mpirq.c b/lib/utils/mpirq.c index 663be182248cc..02139f24dc5fc 100644 --- a/lib/utils/mpirq.c +++ b/lib/utils/mpirq.c @@ -53,12 +53,16 @@ const mp_arg_t mp_irq_init_args[] = { mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1); + mp_irq_init(self, methods, parent); + return self; +} + +void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) { self->base.type = &mp_irq_type; self->methods = (mp_irq_methods_t *)methods; self->parent = parent; self->handler = mp_const_none; self->ishard = false; - return self; } void mp_irq_handler(mp_irq_obj_t *self) { diff --git a/lib/utils/mpirq.h b/lib/utils/mpirq.h index 548185b531147..186c9e1b0b92b 100644 --- a/lib/utils/mpirq.h +++ b/lib/utils/mpirq.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H #define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H +#include "py/obj.h" + /****************************************************************************** DEFINE CONSTANTS ******************************************************************************/ @@ -41,20 +43,17 @@ enum { DEFINE TYPES ******************************************************************************/ -typedef mp_obj_t (*mp_irq_init_t)(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -typedef mp_uint_t (*mp_irq_uint_method_one_uint_para_t)(mp_obj_t self, mp_uint_t trigger); -typedef mp_uint_t (*mp_irq_int_method_one_para_t)(mp_obj_t self, mp_uint_t info_type); +typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger); +typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type); enum { MP_IRQ_INFO_FLAGS, MP_IRQ_INFO_TRIGGERS, - MP_IRQ_INFO_CNT }; typedef struct _mp_irq_methods_t { - mp_irq_init_t init; - mp_irq_uint_method_one_uint_para_t trigger; - mp_irq_int_method_one_para_t info; + mp_irq_trigger_fun_t trigger; + mp_irq_info_fun_t info; } mp_irq_methods_t; typedef struct _mp_irq_obj_t { @@ -77,6 +76,7 @@ extern const mp_obj_type_t mp_irq_type; ******************************************************************************/ mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); +void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); void mp_irq_handler(mp_irq_obj_t *self); #endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 39a45a2a16060..631f46e81f6e1 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -139,7 +139,6 @@ STATIC mp_uint_t pyb_uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { } STATIC const mp_irq_methods_t pyb_uart_irq_methods = { - .init = pyb_uart_irq, .trigger = pyb_uart_irq_trigger, .info = pyb_uart_irq_info, }; diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c index 79d759eb28bf3..c9c6a8c8936e0 100644 --- a/ports/zephyr/machine_pin.c +++ b/ports/zephyr/machine_pin.c @@ -213,11 +213,7 @@ STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ } if (irq == NULL) { irq = m_new_obj(machine_pin_irq_obj_t); - irq->base.base.type = &mp_irq_type; - irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods; - irq->base.parent = MP_OBJ_FROM_PTR(self); - irq->base.handler = mp_const_none; - irq->base.ishard = false; + mp_irq_init(&irq->base, &machine_pin_irq_methods, MP_OBJ_FROM_PTR(self)); irq->next = MP_STATE_PORT(machine_pin_irq_list); gpio_init_callback(&irq->callback, gpio_callback_handler, BIT(self->pin)); int ret = gpio_add_callback(self->port, &irq->callback); @@ -322,7 +318,6 @@ STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) { } STATIC const mp_irq_methods_t machine_pin_irq_methods = { - .init = machine_pin_irq, .trigger = machine_pin_irq_trigger, .info = machine_pin_irq_info, }; From 23109988c29447067b15f1943f72c0b486819c6c Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:21:23 +1000 Subject: [PATCH 242/352] stm32/uart: Allow static IRQ handler registration. This will allow the HCI UART to use a non-heap mp_irq_obj_t, which avoids needing to make a root pointer for it. --- lib/utils/mpirq.h | 2 +- ports/stm32/machine_uart.c | 78 ++------------------------------------ ports/stm32/uart.c | 62 ++++++++++++++++++++++++++++++ ports/stm32/uart.h | 12 +++++- 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/lib/utils/mpirq.h b/lib/utils/mpirq.h index 186c9e1b0b92b..dd423c0101137 100644 --- a/lib/utils/mpirq.h +++ b/lib/utils/mpirq.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H #define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H -#include "py/obj.h" +#include "py/runtime.h" /****************************************************************************** DEFINE CONSTANTS diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 631f46e81f6e1..2862f4c0e7b15 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -73,76 +73,6 @@ /// /// uart.any() # returns True if any characters waiting -typedef struct _pyb_uart_irq_map_t { - uint16_t irq_en; - uint16_t flag; -} pyb_uart_irq_map_t; - -STATIC const pyb_uart_irq_map_t mp_irq_map[] = { - { USART_CR1_IDLEIE, UART_FLAG_IDLE}, // RX idle - { USART_CR1_PEIE, UART_FLAG_PE}, // parity error - { USART_CR1_TXEIE, UART_FLAG_TXE}, // TX register empty - { USART_CR1_TCIE, UART_FLAG_TC}, // TX complete - { USART_CR1_RXNEIE, UART_FLAG_RXNE}, // RX register not empty - #if 0 - // For now only IRQs selected by CR1 are supported - #if defined(STM32F4) - { USART_CR2_LBDIE, UART_FLAG_LBD}, // LIN break detection - #else - { USART_CR2_LBDIE, UART_FLAG_LBDF}, // LIN break detection - #endif - { USART_CR3_CTSIE, UART_FLAG_CTS}, // CTS - #endif -}; - -// OR-ed IRQ flags which should not be touched by the user -STATIC const uint32_t mp_irq_reserved = UART_FLAG_RXNE; - -// OR-ed IRQ flags which are allowed to be used by the user -STATIC const uint32_t mp_irq_allowed = UART_FLAG_IDLE; - -STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); - -STATIC void pyb_uart_irq_config(pyb_uart_obj_t *self, bool enable) { - if (self->mp_irq_trigger) { - for (size_t entry = 0; entry < MP_ARRAY_SIZE(mp_irq_map); ++entry) { - if (mp_irq_map[entry].flag & mp_irq_reserved) { - continue; - } - if (mp_irq_map[entry].flag & self->mp_irq_trigger) { - if (enable) { - self->uartx->CR1 |= mp_irq_map[entry].irq_en; - } else { - self->uartx->CR1 &= ~mp_irq_map[entry].irq_en; - } - } - } - } -} - -STATIC mp_uint_t pyb_uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { - pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - pyb_uart_irq_config(self, false); - self->mp_irq_trigger = new_trigger; - pyb_uart_irq_config(self, true); - return 0; -} - -STATIC mp_uint_t pyb_uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { - pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (info_type == MP_IRQ_INFO_FLAGS) { - return self->mp_irq_flags; - } else if (info_type == MP_IRQ_INFO_TRIGGERS) { - return self->mp_irq_trigger; - } - return 0; -} - -STATIC const mp_irq_methods_t pyb_uart_irq_methods = { - .trigger = pyb_uart_irq_trigger, - .info = pyb_uart_irq_info, -}; - STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->is_enabled) { @@ -518,7 +448,7 @@ STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t * if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; - self->mp_irq_obj = mp_irq_new(&pyb_uart_irq_methods, MP_OBJ_FROM_PTR(self)); + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); } if (n_args > 1 || kw_args->used != 0) { @@ -530,17 +460,17 @@ STATIC mp_obj_t pyb_uart_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t * // Check the trigger mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; - mp_uint_t not_supported = trigger & ~mp_irq_allowed; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; if (trigger != 0 && not_supported) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%08x unsupported"), not_supported); } // Reconfigure user IRQs - pyb_uart_irq_config(self, false); + uart_irq_config(self, false); self->mp_irq_obj->handler = handler; self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; self->mp_irq_trigger = trigger; - pyb_uart_irq_config(self, true); + uart_irq_config(self, true); } return MP_OBJ_FROM_PTR(self->mp_irq_obj); diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 8693813fa4413..b14a18c6ddfa6 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -93,6 +93,28 @@ extern void NORETURN __fatal_error(const char *msg); +typedef struct _pyb_uart_irq_map_t { + uint16_t irq_en; + uint16_t flag; +} pyb_uart_irq_map_t; + +STATIC const pyb_uart_irq_map_t mp_uart_irq_map[] = { + { USART_CR1_IDLEIE, UART_FLAG_IDLE}, // RX idle + { USART_CR1_PEIE, UART_FLAG_PE}, // parity error + { USART_CR1_TXEIE, UART_FLAG_TXE}, // TX register empty + { USART_CR1_TCIE, UART_FLAG_TC}, // TX complete + { USART_CR1_RXNEIE, UART_FLAG_RXNE}, // RX register not empty + #if 0 + // For now only IRQs selected by CR1 are supported + #if defined(STM32F4) + { USART_CR2_LBDIE, UART_FLAG_LBD}, // LIN break detection + #else + { USART_CR2_LBDIE, UART_FLAG_LBDF}, // LIN break detection + #endif + { USART_CR3_CTSIE, UART_FLAG_CTS}, // CTS + #endif +}; + void uart_init0(void) { #if defined(STM32H7) RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit = {0}; @@ -444,6 +466,23 @@ bool uart_init(pyb_uart_obj_t *uart_obj, return true; } +void uart_irq_config(pyb_uart_obj_t *self, bool enable) { + if (self->mp_irq_trigger) { + for (size_t entry = 0; entry < MP_ARRAY_SIZE(mp_uart_irq_map); ++entry) { + if (mp_uart_irq_map[entry].flag & MP_UART_RESERVED_FLAGS) { + continue; + } + if (mp_uart_irq_map[entry].flag & self->mp_irq_trigger) { + if (enable) { + self->uartx->CR1 |= mp_uart_irq_map[entry].irq_en; + } else { + self->uartx->CR1 &= ~mp_uart_irq_map[entry].irq_en; + } + } + } + } +} + void uart_set_rxbuf(pyb_uart_obj_t *self, size_t len, void *buf) { self->read_buf_head = 0; self->read_buf_tail = 0; @@ -864,3 +903,26 @@ void uart_irq_handler(mp_uint_t uart_id) { mp_irq_handler(self->mp_irq_obj); } } + +STATIC mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uart_irq_config(self, false); + self->mp_irq_trigger = new_trigger; + uart_irq_config(self, true); + return 0; +} + +STATIC mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 62676fb91be51..9a38db593d4fc 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_UART_H #define MICROPY_INCLUDED_STM32_UART_H -struct _mp_irq_obj_t; +#include "lib/utils/mpirq.h" typedef enum { PYB_UART_NONE = 0, @@ -45,6 +45,12 @@ typedef enum { #define CHAR_WIDTH_8BIT (0) #define CHAR_WIDTH_9BIT (1) +// OR-ed IRQ flags which are allowed to be used by the user +#define MP_UART_ALLOWED_FLAGS UART_FLAG_IDLE + +// OR-ed IRQ flags which should not be touched by the user +#define MP_UART_RESERVED_FLAGS UART_FLAG_RXNE + typedef struct _pyb_uart_obj_t { mp_obj_base_t base; USART_TypeDef *uartx; @@ -62,16 +68,18 @@ typedef struct _pyb_uart_obj_t { byte *read_buf; // byte or uint16_t, depending on char size uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags - struct _mp_irq_obj_t *mp_irq_obj; // user IRQ object + mp_irq_obj_t *mp_irq_obj; // user IRQ object } pyb_uart_obj_t; extern const mp_obj_type_t pyb_uart_type; +extern const mp_irq_methods_t uart_irq_methods; void uart_init0(void); void uart_deinit_all(void); bool uart_exists(int uart_id); bool uart_init(pyb_uart_obj_t *uart_obj, uint32_t baudrate, uint32_t bits, uint32_t parity, uint32_t stop, uint32_t flow); +void uart_irq_config(pyb_uart_obj_t *self, bool enable); void uart_set_rxbuf(pyb_uart_obj_t *self, size_t len, void *buf); void uart_deinit(pyb_uart_obj_t *uart_obj); void uart_irq_handler(mp_uint_t uart_id); From 5ff265a3db998fd86a45d83ccca98fda153991c4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 16:35:25 +1000 Subject: [PATCH 243/352] stm32/modbluetooth_hci: Use a static mp_irq_obj_t for BT HCI UART IRQ. So that the IRQ handler does not need to be traced by the GC. --- ports/stm32/modbluetooth_hci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ports/stm32/modbluetooth_hci.c b/ports/stm32/modbluetooth_hci.c index 4e016eae8451c..54c60adc01181 100644 --- a/ports/stm32/modbluetooth_hci.c +++ b/ports/stm32/modbluetooth_hci.c @@ -135,6 +135,7 @@ int mp_bluetooth_hci_uart_readchar(void) { #include "uart.h" pyb_uart_obj_t mp_bluetooth_hci_uart_obj; +mp_irq_obj_t mp_bluetooth_hci_uart_irq_obj; static uint8_t hci_uart_rxbuf[512]; @@ -163,15 +164,14 @@ int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { } int mp_bluetooth_hci_uart_activate(void) { - // Interrupt on RX chunk received (idle) - // Trigger stack poll when this happens - mp_obj_t uart_irq_fn = mp_load_attr(MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj), MP_QSTR_irq); - mp_obj_t uargs[] = { - MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj), - MP_OBJ_NEW_SMALL_INT(UART_FLAG_IDLE), - mp_const_true, - }; - mp_call_function_n_kw(uart_irq_fn, 3, 0, uargs); + // Add IRQ handler for IDLE (i.e. packet finished). + uart_irq_config(&mp_bluetooth_hci_uart_obj, false); + mp_irq_init(&mp_bluetooth_hci_uart_irq_obj, &uart_irq_methods, MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj)); + mp_bluetooth_hci_uart_obj.mp_irq_obj = &mp_bluetooth_hci_uart_irq_obj; + mp_bluetooth_hci_uart_obj.mp_irq_trigger = UART_FLAG_IDLE; + mp_bluetooth_hci_uart_irq_obj.handler = MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj); + mp_bluetooth_hci_uart_irq_obj.ishard = true; + uart_irq_config(&mp_bluetooth_hci_uart_obj, true); mp_bluetooth_hci_controller_init(); mp_bluetooth_hci_controller_activate(); From e46aac24bacdafcb5323fbfc702a3bd597f66072 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 16:04:31 +1000 Subject: [PATCH 244/352] extmod/modbluetooth: Rename logging macro to be just DEBUG_printf. And prefix the debug message with "btstack:" or "nimble:", depending on the context. Also use correct format specifier for %zu. --- extmod/btstack/modbluetooth_btstack.c | 146 ++++++++++++++------------ extmod/nimble/modbluetooth_nimble.c | 44 ++++---- 2 files changed, 100 insertions(+), 90 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 8f0c82974c316..9aef7e6879fb0 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -35,7 +35,7 @@ #include "lib/btstack/src/btstack.h" -#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_printf(...) // printf("btstack: " __VA_ARGS__) #ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY BTSTACK" @@ -54,7 +54,7 @@ STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3; volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; STATIC int btstack_error_to_errno(int err) { - DEBUG_EVENT_printf(" --> btstack error: %d\n", err); + DEBUG_printf(" --> btstack error: %d\n", err); if (err == ERROR_CODE_SUCCESS) { return 0; } else if (err == BTSTACK_ACL_BUFFERS_FULL || err == BTSTACK_MEMORY_ALLOC_FAILED) { @@ -168,16 +168,16 @@ STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op STATIC void btstack_notify_indicate_ready_handler(void *context) { MICROPY_PY_BLUETOOTH_ENTER mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)context; - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%lu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len); + DEBUG_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%zu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len); if (pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY) { int err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->buf, pending_op->len); - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); + DEBUG_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); assert(err == ERROR_CODE_SUCCESS); (void)err; } else { assert(pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE); int err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, NULL, 0); - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); + DEBUG_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); assert(err == ERROR_CODE_SUCCESS); (void)err; } @@ -188,7 +188,7 @@ STATIC void btstack_notify_indicate_ready_handler(void *context) { // Register a pending background operation -- copies the buffer, and makes it known to the GC. STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { - DEBUG_EVENT_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%lu\n", op_type, conn_handle, value_handle, len); + DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%zu\n", op_type, conn_handle, value_handle, len); mp_btstack_pending_op_t *pending_op = m_new_obj_var(mp_btstack_pending_op_t, uint8_t, len); pending_op->op_type = op_type; pending_op->conn_handle = conn_handle; @@ -219,20 +219,20 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty // know for sure that we're using the correct entry. STATIC mp_btstack_pending_op_t *btstack_finish_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, bool del) { MICROPY_PY_BLUETOOTH_ENTER - DEBUG_EVENT_printf("btstack_finish_pending_operation op_type=%d conn_handle=%d value_handle=%d\n", op_type, conn_handle, value_handle); + DEBUG_printf("btstack_finish_pending_operation op_type=%d conn_handle=%d value_handle=%d\n", op_type, conn_handle, value_handle); btstack_linked_list_iterator_t it; btstack_linked_list_iterator_init(&it, &MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops); while (btstack_linked_list_iterator_has_next(&it)) { mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)btstack_linked_list_iterator_next(&it); if (pending_op->op_type == op_type && pending_op->conn_handle == conn_handle && (value_handle == 0xffff || pending_op->value_handle == value_handle)) { - DEBUG_EVENT_printf("btstack_finish_pending_operation: found value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); + DEBUG_printf("btstack_finish_pending_operation: found value_handle=%d len=%zu\n", pending_op->value_handle, pending_op->len); btstack_remove_pending_operation(pending_op, del); MICROPY_PY_BLUETOOTH_EXIT return del ? NULL : pending_op; } } - DEBUG_EVENT_printf("btstack_finish_pending_operation: not found\n"); + DEBUG_printf("btstack_finish_pending_operation: not found\n"); MICROPY_PY_BLUETOOTH_EXIT return NULL; } @@ -243,7 +243,7 @@ STATIC mp_btstack_pending_op_t *btstack_finish_pending_operation(uint16_t op_typ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - DEBUG_EVENT_printf("btstack_packet_handler_att_server(packet_type=%u, packet=%p)\n", packet_type, packet); + DEBUG_printf("btstack_packet_handler_att_server(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { return; } @@ -251,13 +251,13 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan uint8_t event_type = hci_event_packet_get_type(packet); if (event_type == ATT_EVENT_CONNECTED) { - DEBUG_EVENT_printf(" --> att connected\n"); + DEBUG_printf(" --> att connected\n"); // The ATT_EVENT_*CONNECTED events are fired for both peripheral and central role, with no way to tell which. // So we use the HCI_EVENT_LE_META event directly in the main packet handler. } else if (event_type == ATT_EVENT_DISCONNECTED) { - DEBUG_EVENT_printf(" --> att disconnected\n"); + DEBUG_printf(" --> att disconnected\n"); } else if (event_type == ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE) { - DEBUG_EVENT_printf(" --> att indication complete\n"); + DEBUG_printf(" --> att indication complete\n"); uint16_t conn_handle = att_event_handle_value_indication_complete_get_conn_handle(packet); uint16_t value_handle = att_event_handle_value_indication_complete_get_attribute_handle(packet); uint8_t status = att_event_handle_value_indication_complete_get_status(packet); @@ -265,12 +265,12 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan } else if (event_type == HCI_EVENT_LE_META || event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { // Ignore, duplicated by att_server.c. } else { - DEBUG_EVENT_printf(" --> hci att server event type: unknown (0x%02x)\n", event_type); + DEBUG_printf(" --> hci att server event type: unknown (0x%02x)\n", event_type); } } STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { - DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); + DEBUG_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { return; } @@ -278,7 +278,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t uint8_t event_type = hci_event_packet_get_type(packet); if (event_type == HCI_EVENT_LE_META) { - DEBUG_EVENT_printf(" --> hci le meta\n"); + DEBUG_printf(" --> hci le meta\n"); if (hci_event_le_meta_get_subevent_code(packet) == HCI_SUBEVENT_LE_CONNECTION_COMPLETE) { uint16_t conn_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); uint8_t addr_type = hci_subevent_le_connection_complete_get_peer_address_type(packet); @@ -296,7 +296,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t } } else if (event_type == BTSTACK_EVENT_STATE) { uint8_t state = btstack_event_state_get_state(packet); - DEBUG_EVENT_printf(" --> btstack event state 0x%02x\n", state); + DEBUG_printf(" --> btstack event state 0x%02x\n", state); if (state == HCI_STATE_WORKING) { // Signal that initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; @@ -305,21 +305,21 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; } } else if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT) { - DEBUG_EVENT_printf(" --> hci transport packet sent\n"); + DEBUG_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { - DEBUG_EVENT_printf(" --> hci command complete\n"); + DEBUG_printf(" --> hci command complete\n"); } else if (event_type == HCI_EVENT_COMMAND_STATUS) { - DEBUG_EVENT_printf(" --> hci command status\n"); + DEBUG_printf(" --> hci command status\n"); } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { - DEBUG_EVENT_printf(" --> hci number of completed packets\n"); + DEBUG_printf(" --> hci number of completed packets\n"); } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { - DEBUG_EVENT_printf(" --> btstack # conns changed\n"); + DEBUG_printf(" --> btstack # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { - DEBUG_EVENT_printf(" --> hci vendor specific\n"); + DEBUG_printf(" --> hci vendor specific\n"); } else if (event_type == GATT_EVENT_MTU) { - DEBUG_EVENT_printf(" --> hci MTU\n"); + DEBUG_printf(" --> hci MTU\n"); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { - DEBUG_EVENT_printf(" --> hci disconnect complete\n"); + DEBUG_printf(" --> hci disconnect complete\n"); uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); const hci_connection_t *conn = hci_connection_for_handle(conn_handle); uint16_t irq_event; @@ -334,7 +334,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { - DEBUG_EVENT_printf(" --> gap advertising report\n"); + DEBUG_printf(" --> gap advertising report\n"); bd_addr_t address; gap_event_advertising_report_get_address(packet, address); uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); @@ -346,7 +346,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); uint16_t status = gatt_event_query_complete_get_att_status(packet); - DEBUG_EVENT_printf(" --> gatt query complete irq=%d conn_handle=%d status=%d\n", irq, conn_handle, status); + DEBUG_printf(" --> gatt query complete irq=%d conn_handle=%d status=%d\n", irq, conn_handle, status); if (irq == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { // TODO there is no value_handle available to pass here. // TODO try and get this implemented in btstack. @@ -361,28 +361,28 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gattc_on_discover_complete(irq, conn_handle, status); } } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt service query result\n"); + DEBUG_printf(" --> gatt service query result\n"); uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); gatt_client_service_t service; gatt_event_service_query_result_get_service(packet, &service); mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(service.uuid16, service.uuid128); mp_bluetooth_gattc_on_primary_service_result(conn_handle, service.start_group_handle, service.end_group_handle, &service_uuid); } else if (event_type == GATT_EVENT_CHARACTERISTIC_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt characteristic query result\n"); + DEBUG_printf(" --> gatt characteristic query result\n"); uint16_t conn_handle = gatt_event_characteristic_query_result_get_handle(packet); gatt_client_characteristic_t characteristic; gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(characteristic.uuid16, characteristic.uuid128); mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic.start_handle, characteristic.value_handle, characteristic.properties, &characteristic_uuid); } else if (event_type == GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt descriptor query result\n"); + DEBUG_printf(" --> gatt descriptor query result\n"); uint16_t conn_handle = gatt_event_all_characteristic_descriptors_query_result_get_handle(packet); gatt_client_characteristic_descriptor_t descriptor; gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor); mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(descriptor.uuid16, descriptor.uuid128); mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor.handle, &descriptor_uuid); } else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt characteristic value query result\n"); + DEBUG_printf(" --> gatt characteristic value query result\n"); uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet); uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet); uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet); @@ -392,7 +392,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gattc_on_data_available_chunk(data, len); mp_bluetooth_gattc_on_data_available_end(atomic_state); } else if (event_type == GATT_EVENT_NOTIFICATION) { - DEBUG_EVENT_printf(" --> gatt notification\n"); + DEBUG_printf(" --> gatt notification\n"); uint16_t conn_handle = gatt_event_notification_get_handle(packet); uint16_t value_handle = gatt_event_notification_get_value_handle(packet); uint16_t len = gatt_event_notification_get_value_length(packet); @@ -402,7 +402,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gattc_on_data_available_chunk(data, len); mp_bluetooth_gattc_on_data_available_end(atomic_state); } else if (event_type == GATT_EVENT_INDICATION) { - DEBUG_EVENT_printf(" --> gatt indication\n"); + DEBUG_printf(" --> gatt indication\n"); uint16_t conn_handle = gatt_event_indication_get_handle(packet); uint16_t value_handle = gatt_event_indication_get_value_handle(packet); uint16_t len = gatt_event_indication_get_value_length(packet); @@ -413,17 +413,17 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t mp_bluetooth_gattc_on_data_available_end(atomic_state); } else if (event_type == GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE) { uint16_t conn_handle = gatt_event_can_write_without_response_get_handle(packet); - DEBUG_EVENT_printf(" --> gatt can write without response %d\n", conn_handle); + DEBUG_printf(" --> gatt can write without response %d\n", conn_handle); mp_btstack_pending_op_t *pending_op = btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, 0xffff, false /* !del */); if (pending_op) { - DEBUG_EVENT_printf(" --> ready for value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); + DEBUG_printf(" --> ready for value_handle=%d len=%zu\n", pending_op->value_handle, pending_op->len); gatt_client_write_value_of_characteristic_without_response(pending_op->conn_handle, pending_op->value_handle, pending_op->len, (uint8_t *)pending_op->buf); // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. } #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else { - DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); + DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } } @@ -489,7 +489,7 @@ STATIC void btstack_init_deinit_timeout_handler(btstack_timer_source_t *ds) { } int mp_bluetooth_init(void) { - DEBUG_EVENT_printf("mp_bluetooth_init\n"); + DEBUG_printf("mp_bluetooth_init\n"); if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { return 0; @@ -538,6 +538,8 @@ int mp_bluetooth_init(void) { btstack_run_loop_set_timer_handler(&btstack_init_deinit_timeout, btstack_init_deinit_timeout_handler); btstack_run_loop_add_timer(&btstack_init_deinit_timeout); + DEBUG_printf("mp_bluetooth_init: waiting for stack startup\n"); + // Either the HCI event will set state to ACTIVE, or the timeout will set it to TIMEOUT. mp_bluetooth_btstack_port_start(); while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING) { @@ -547,6 +549,8 @@ int mp_bluetooth_init(void) { // Check for timeout. if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { + DEBUG_printf("mp_bluetooth_init: stack startup timed out\n"); + // Required to stop the polling loop. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; // Attempt a shutdown (may not do anything). @@ -566,7 +570,7 @@ int mp_bluetooth_init(void) { } void mp_bluetooth_deinit(void) { - DEBUG_EVENT_printf("mp_bluetooth_deinit\n"); + DEBUG_printf("mp_bluetooth_deinit\n"); // Nothing to do if not initialised. if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) { @@ -595,6 +599,8 @@ void mp_bluetooth_deinit(void) { mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; + + DEBUG_printf("mp_bluetooth_deinit: complete\n"); } bool mp_bluetooth_is_active(void) { @@ -618,7 +624,7 @@ int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) { } int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { - DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_start\n"); + DEBUG_printf("mp_bluetooth_gap_advertise_start\n"); uint16_t adv_int_min = interval_us / 625; uint16_t adv_int_max = interval_us / 625; uint8_t adv_type = connectable ? 0 : 2; @@ -654,14 +660,14 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons } void mp_bluetooth_gap_advertise_stop(void) { - DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_stop\n"); + DEBUG_printf("mp_bluetooth_gap_advertise_stop\n"); gap_advertisements_enable(false); MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0; MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL; } int mp_bluetooth_gatts_register_service_begin(bool append) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_begin\n"); + DEBUG_printf("mp_bluetooth_gatts_register_service_begin\n"); if (!append) { // This will reset the DB. // Becase the DB is statically allocated, there's no problem with just re-initing it. @@ -682,10 +688,10 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { (void)connection_handle; - DEBUG_EVENT_printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); + DEBUG_printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); if (!entry) { - DEBUG_EVENT_printf("btstack: att_read_callback handle not found\n"); + DEBUG_printf("btstack: att_read_callback handle not found\n"); return 0; // TODO: Find status code for not-found. } @@ -695,10 +701,10 @@ STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t a STATIC int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { (void)offset; (void)transaction_mode; - DEBUG_EVENT_printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); + DEBUG_printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); if (!entry) { - DEBUG_EVENT_printf("btstack: att_write_callback handle not found\n"); + DEBUG_printf("btstack: att_write_callback handle not found\n"); return 0; // TODO: Find status code for not-found. } @@ -720,7 +726,7 @@ STATIC inline uint16_t get_uuid16(const mp_obj_bluetooth_uuid_t *uuid) { } int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint8_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint8_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service\n"); + DEBUG_printf("mp_bluetooth_gatts_register_service\n"); // Note: btstack expects BE UUIDs (which it immediately convertes to LE). // So we have to convert all our modbluetooth LE UUIDs to BE just for the att_db_util_add_* methods (using get_uuid16 above, and reverse_128 from btstackutil.h). @@ -764,7 +770,7 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m return ret; } } - DEBUG_EVENT_printf("Registered char with handle %u\n", handles[handle_index]); + DEBUG_printf("mp_bluetooth_gatts_register_service: Registered char with handle %u\n", handles[handle_index]); ++handle_index; for (size_t j = 0; j < num_descriptors[i]; ++j) { @@ -782,7 +788,7 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m return MP_EINVAL; } mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index], MP_BLUETOOTH_DEFAULT_ATTR_LEN); - DEBUG_EVENT_printf("Registered desc with handle %u\n", handles[handle_index]); + DEBUG_printf("mp_bluetooth_gatts_register_service: Registered desc with handle %u\n", handles[handle_index]); ++descriptor_index; ++handle_index; } @@ -792,23 +798,23 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m } int mp_bluetooth_gatts_register_service_end(void) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_end\n"); + DEBUG_printf("mp_bluetooth_gatts_register_service_end\n"); att_server_init(att_db_util_get_address(), &att_read_callback, &att_write_callback); return 0; } int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_read\n"); + DEBUG_printf("mp_bluetooth_gatts_read\n"); return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_write\n"); + DEBUG_printf("mp_bluetooth_gatts_write\n"); return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify\n"); + DEBUG_printf("mp_bluetooth_gatts_notify\n"); // Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. uint8_t *data = NULL; size_t len = 0; @@ -817,7 +823,7 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { } int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send\n"); + DEBUG_printf("mp_bluetooth_gatts_notify_send\n"); // Attempt to send immediately. If it succeeds, btstack will copy the buffer. MICROPY_PY_BLUETOOTH_ENTER @@ -825,7 +831,7 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, MICROPY_PY_BLUETOOTH_EXIT if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); + DEBUG_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); // Schedule callback, making a copy of the buffer. mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, conn_handle, value_handle, value, value_len); @@ -843,7 +849,7 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, } int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate\n"); + DEBUG_printf("mp_bluetooth_gatts_indicate\n"); uint8_t *data = NULL; size_t len = 0; @@ -858,7 +864,7 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { MICROPY_PY_BLUETOOTH_EXIT if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n"); + DEBUG_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n"); // Schedule callback, making a copy of the buffer. mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, conn_handle, value_handle, data, len); @@ -876,12 +882,12 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_set_buffer\n"); + DEBUG_printf("mp_bluetooth_gatts_set_buffer\n"); return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); } int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gap_disconnect\n"); + DEBUG_printf("mp_bluetooth_gap_disconnect\n"); gap_disconnect(conn_handle); return 0; } @@ -895,7 +901,7 @@ STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) { } int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { - DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); + DEBUG_printf("mp_bluetooth_gap_scan_start\n"); if (duration_ms > 0) { btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); @@ -910,7 +916,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ } int mp_bluetooth_gap_scan_stop(void) { - DEBUG_EVENT_printf("mp_bluetooth_gap_scan_stop\n"); + DEBUG_printf("mp_bluetooth_gap_scan_stop\n"); btstack_run_loop_remove_timer(&scan_duration_timeout); gap_stop_scan(); mp_bluetooth_gap_on_scan_complete(); @@ -918,7 +924,7 @@ int mp_bluetooth_gap_scan_stop(void) { } int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { - DEBUG_EVENT_printf("mp_bluetooth_gap_peripheral_connect\n"); + DEBUG_printf("mp_bluetooth_gap_peripheral_connect\n"); uint16_t conn_scan_interval = 60000 / 625; uint16_t conn_scan_window = 30000 / 625; @@ -937,7 +943,7 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, } int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); + DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n"); uint8_t err; if (uuid) { if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { @@ -947,7 +953,7 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_ reverse_128(uuid->data, buffer); err = gatt_client_discover_primary_services_by_uuid128(&btstack_packet_handler_discover_services, conn_handle, buffer); } else { - DEBUG_EVENT_printf(" --> unknown UUID size\n"); + DEBUG_printf(" --> unknown UUID size\n"); return MP_EINVAL; } } else { @@ -957,7 +963,7 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_ } int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); + DEBUG_printf("mp_bluetooth_gattc_discover_characteristics\n"); gatt_client_service_t service = { // Only start/end handles needed for gatt_client_discover_characteristics_for_service. .start_group_handle = start_handle, @@ -974,7 +980,7 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s reverse_128(uuid->data, buffer); err = gatt_client_discover_characteristics_for_service_by_uuid128(&btstack_packet_handler_discover_characteristics, conn_handle, &service, buffer); } else { - DEBUG_EVENT_printf(" --> unknown UUID size\n"); + DEBUG_printf(" --> unknown UUID size\n"); return MP_EINVAL; } } else { @@ -984,7 +990,7 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s } int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_descriptors\n"); + DEBUG_printf("mp_bluetooth_gattc_discover_descriptors\n"); gatt_client_characteristic_t characteristic = { // Only start/end handles needed for gatt_client_discover_characteristic_descriptors. .start_handle = start_handle, @@ -998,12 +1004,12 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start } int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); + DEBUG_printf("mp_bluetooth_gattc_read\n"); return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle)); } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write\n"); + DEBUG_printf("mp_bluetooth_gattc_write\n"); // We should be distinguishing between gatt_client_write_value_of_characteristic vs // gatt_client_write_characteristic_descriptor_using_descriptor_handle. @@ -1018,13 +1024,13 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const // If possible, this will send immediately, copying the buffer directly to the ACL buffer. err = gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value); if (err == GATT_CLIENT_BUSY) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write: client busy\n"); + DEBUG_printf("mp_bluetooth_gattc_write: client busy\n"); // Can't send right now, need to take a copy of the buffer and add it to the queue. pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, value_handle, value, *value_len); // Notify when this conn_handle can write. err = gatt_client_request_can_write_without_response_event(&btstack_packet_handler_generic, conn_handle); } else { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write: other failure: %d\n", err); + DEBUG_printf("mp_bluetooth_gattc_write: other failure: %d\n", err); } } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { // Pending operation copies the value buffer and keeps a GC reference diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index e51f2747ec5c8..36bfa6cccfa61 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -45,7 +45,7 @@ #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE" #endif -#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_printf(...) // printf("nimble: " __VA_ARGS__) #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV @@ -172,10 +172,12 @@ STATIC void sync_cb(void) { } if (MP_BLUETOOTH_DEFAULT_ATTR_LEN > 20) { + DEBUG_printf("sync_cb: Setting MTU\n"); rc = ble_att_set_preferred_mtu(MP_BLUETOOTH_DEFAULT_ATTR_LEN + 3); assert(rc == 0); } + DEBUG_printf("sync_cb: Setting device name\n"); ble_svc_gap_device_name_set(MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME); mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE; @@ -188,12 +190,12 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { switch (ctxt->op) { case BLE_GATT_REGISTER_OP_SVC: // Called when a service is successfully registered. - DEBUG_EVENT_printf("gatts_register_cb: svc uuid=%p handle=%d\n", &ctxt->svc.svc_def->uuid, ctxt->svc.handle); + DEBUG_printf("gatts_register_cb: svc uuid=%p handle=%d\n", &ctxt->svc.svc_def->uuid, ctxt->svc.handle); break; case BLE_GATT_REGISTER_OP_CHR: // Called when a characteristic is successfully registered. - DEBUG_EVENT_printf("gatts_register_cb: chr uuid=%p def_handle=%d val_handle=%d\n", &ctxt->chr.chr_def->uuid, ctxt->chr.def_handle, ctxt->chr.val_handle); + DEBUG_printf("gatts_register_cb: chr uuid=%p def_handle=%d val_handle=%d\n", &ctxt->chr.chr_def->uuid, ctxt->chr.def_handle, ctxt->chr.val_handle); // Note: We will get this event for the default GAP Service, meaning that we allocate storage for the // "device name" and "appearance" characteristics, even though we never see the reads for them. @@ -207,7 +209,7 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { case BLE_GATT_REGISTER_OP_DSC: // Called when a descriptor is successfully registered. // Note: This is event is not called for the CCCD. - DEBUG_EVENT_printf("gatts_register_cb: dsc uuid=%p handle=%d\n", &ctxt->dsc.dsc_def->uuid, ctxt->dsc.handle); + DEBUG_printf("gatts_register_cb: dsc uuid=%p handle=%d\n", &ctxt->dsc.dsc_def->uuid, ctxt->dsc.handle); // See above, safe to alloc. mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, ctxt->dsc.handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); @@ -217,13 +219,13 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { break; default: - DEBUG_EVENT_printf("gatts_register_cb: unknown op %d\n", ctxt->op); + DEBUG_printf("gatts_register_cb: unknown op %d\n", ctxt->op); break; } } STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { - DEBUG_EVENT_printf("gap_event_cb: type=%d\n", event->type); + DEBUG_printf("gap_event_cb: type=%d\n", event->type); if (!mp_bluetooth_is_active()) { return 0; } @@ -250,7 +252,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { break; case BLE_GAP_EVENT_NOTIFY_TX: { - DEBUG_EVENT_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); + DEBUG_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); // This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0). if (event->notify_tx.indication && event->notify_tx.status != 0) { // Map "done/ack" to 0, otherwise pass the status directly. @@ -263,7 +265,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { } int mp_bluetooth_init(void) { - DEBUG_EVENT_printf("mp_bluetooth_init\n"); + DEBUG_printf("mp_bluetooth_init\n"); // Clean up if necessary. mp_bluetooth_deinit(); @@ -291,7 +293,7 @@ int mp_bluetooth_init(void) { while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { MICROPY_EVENT_POLL_HOOK } - DEBUG_EVENT_printf("mp_bluetooth_init: ready\n"); + DEBUG_printf("mp_bluetooth_init: ready\n"); return 0; } @@ -304,7 +306,7 @@ STATIC void ble_hs_shutdown_stop_cb(int status, void *arg) { STATIC struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; void mp_bluetooth_deinit(void) { - DEBUG_EVENT_printf("mp_bluetooth_deinit\n"); + DEBUG_printf("mp_bluetooth_deinit\n"); if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { return; } @@ -325,7 +327,7 @@ void mp_bluetooth_deinit(void) { mp_bluetooth_nimble_port_deinit(); MP_STATE_PORT(bluetooth_nimble_root_pointers) = NULL; - DEBUG_EVENT_printf("mp_bluetooth_deinit: shut down\n"); + DEBUG_printf("mp_bluetooth_deinit: shut down\n"); } bool mp_bluetooth_is_active(void) { @@ -409,7 +411,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons if (ret == 0) { return 0; } - DEBUG_EVENT_printf("ble_gap_adv_start: %d\n", ret); + DEBUG_printf("ble_gap_adv_start: %d\n", ret); return ble_hs_err_to_errno(ret); } @@ -421,7 +423,7 @@ void mp_bluetooth_gap_advertise_stop(void) { } static int characteristic_access_cb(uint16_t conn_handle, uint16_t value_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { - DEBUG_EVENT_printf("characteristic_access_cb: conn_handle=%u value_handle=%u op=%u\n", conn_handle, value_handle, ctxt->op); + DEBUG_printf("characteristic_access_cb: conn_handle=%u value_handle=%u op=%u\n", conn_handle, value_handle, ctxt->op); if (!mp_bluetooth_is_active()) { return 0; } @@ -640,7 +642,7 @@ STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_ } STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) { - DEBUG_EVENT_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1); + DEBUG_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -681,6 +683,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ } int mp_bluetooth_gap_scan_stop(void) { + DEBUG_printf("mp_bluetooth_gap_scan_stop\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -697,7 +700,7 @@ int mp_bluetooth_gap_scan_stop(void) { // Central role: GAP events for a connected peripheral. STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) { - DEBUG_EVENT_printf("peripheral_gap_event_cb: event=%d\n", event->type); + DEBUG_printf("peripheral_gap_event_cb: event=%d\n", event->type); if (!mp_bluetooth_is_active()) { return 0; } @@ -745,6 +748,7 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) { } int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { + DEBUG_printf("mp_bluetooth_gap_peripheral_connect\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -770,7 +774,7 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, } STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) { - DEBUG_EVENT_printf("peripheral_discover_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1); + DEBUG_printf("peripheral_discover_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -799,7 +803,7 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_ } STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) { - DEBUG_EVENT_printf("ble_gatt_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1); + DEBUG_printf("ble_gatt_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -828,7 +832,7 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s } STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) { - DEBUG_EVENT_printf("ble_gatt_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1); + DEBUG_printf("ble_gatt_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -850,7 +854,7 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start } STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { - DEBUG_EVENT_printf("ble_gatt_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); + DEBUG_printf("ble_gatt_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -871,7 +875,7 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { } STATIC int ble_gatt_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { - DEBUG_EVENT_printf("ble_gatt_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); + DEBUG_printf("ble_gatt_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); if (!mp_bluetooth_is_active()) { return 0; } From ed14435a8e6199b845c3404a9052f9ff4213292c Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:43:09 +1000 Subject: [PATCH 245/352] extmod/modbluetooth: Refactor stack/hci/driver/port bindings. Previously the interaction between the different layers of the Bluetooth stack was different on each port and each stack. This commit defines common interfaces between them and implements them for cyw43, btstack, nimble, stm32, unix. --- drivers/cyw43/cywbt.c | 14 +- extmod/btstack/btstack.mk | 11 ++ extmod/btstack/btstack_config.h | 1 + .../btstack/btstack_hci_uart.c | 94 ++++---------- .../btstack_hci_uart.h} | 21 ++- extmod/btstack/modbluetooth_btstack.c | 5 + extmod/btstack/modbluetooth_btstack.h | 1 + extmod/mpbthci.c | 38 ++++++ extmod/{modbluetooth_hci.h => mpbthci.h} | 25 ++-- extmod/nimble/hal/hal_uart.c | 25 ++-- extmod/nimble/hal/hal_uart.h | 31 ++++- extmod/nimble/logcfg/logcfg.h | 45 +++++++ extmod/nimble/modbluetooth_nimble.c | 122 ++++++++++++++---- extmod/nimble/modbluetooth_nimble.h | 18 ++- extmod/nimble/nimble.mk | 13 +- .../nimble/{npl_os.c => nimble_npl_os.c} | 64 ++++++--- extmod/nimble/nimble/nimble_npl_os.h | 19 ++- extmod/nimble/syscfg/syscfg.h | 5 +- ports/esp32/Makefile | 2 +- ports/esp32/{nimble.c => mpnimbleport.c} | 30 ++++- ports/stm32/Makefile | 7 +- .../{modbluetooth_hci.c => mpbthciport.c} | 97 +++++++++----- ports/stm32/mpbtstackport.c | 107 +++++++++++++++ ports/stm32/mpbtstackport.h | 34 +++++ ports/stm32/mpconfigport.h | 21 ++- ports/stm32/{nimble.c => mpnimbleport.c} | 61 ++++----- ports/stm32/mpnimbleport.h | 34 +++++ ports/unix/Makefile | 9 +- ports/unix/mpbtstackport.h | 39 ++++++ ports/unix/mpbtstackport_common.c | 92 +++++++++++++ .../{btstack_usb.c => mpbtstackport_usb.c} | 101 ++------------- ports/unix/mpconfigport.h | 4 +- 32 files changed, 849 insertions(+), 341 deletions(-) rename ports/stm32/btstack.c => extmod/btstack/btstack_hci_uart.c (67%) rename extmod/{nimble/nimble/nimble_hci_uart.h => btstack/btstack_hci_uart.h} (66%) create mode 100644 extmod/mpbthci.c rename extmod/{modbluetooth_hci.h => mpbthci.h} (71%) create mode 100644 extmod/nimble/logcfg/logcfg.h rename extmod/nimble/nimble/{npl_os.c => nimble_npl_os.c} (85%) rename ports/esp32/{nimble.c => mpnimbleport.c} (66%) rename ports/stm32/{modbluetooth_hci.c => mpbthciport.c} (75%) create mode 100644 ports/stm32/mpbtstackport.c create mode 100644 ports/stm32/mpbtstackport.h rename ports/stm32/{nimble.c => mpnimbleport.c} (56%) create mode 100644 ports/stm32/mpnimbleport.h create mode 100644 ports/unix/mpbtstackport.h create mode 100644 ports/unix/mpbtstackport_common.c rename ports/unix/{btstack_usb.c => mpbtstackport_usb.c} (54%) diff --git a/drivers/cyw43/cywbt.c b/drivers/cyw43/cywbt.c index 79318f4483ba4..defe670683ffd 100644 --- a/drivers/cyw43/cywbt.c +++ b/drivers/cyw43/cywbt.c @@ -31,13 +31,19 @@ #include "py/mphal.h" #include "pin_static_af.h" #include "uart.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #if MICROPY_PY_NETWORK_CYW43 extern const char fw_4343WA1_7_45_98_50_start; #define CYWBT_FW_ADDR (&fw_4343WA1_7_45_98_50_start + 749 * 512 + 29 * 256) +// Provided by the port. +extern pyb_uart_obj_t mp_bluetooth_hci_uart_obj; + +// Provided by the port, and also possibly shared with the stack. +extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + /******************************************************************************/ // CYW BT HCI low-level driver @@ -168,10 +174,6 @@ int mp_bluetooth_hci_controller_init(void) { mp_hal_pin_config(pyb_pin_WL_GPIO_4, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); // RF-switch power mp_hal_pin_high(pyb_pin_WL_GPIO_4); // Turn the RF-switch on - return 0; -} - -int mp_bluetooth_hci_controller_activate(void) { uint8_t buf[256]; mp_hal_pin_low(pyb_pin_BT_REG_ON); @@ -219,7 +221,7 @@ int mp_bluetooth_hci_controller_activate(void) { return 0; } -int mp_bluetooth_hci_controller_deactivate(void) { +int mp_bluetooth_hci_controller_deinit(void) { mp_hal_pin_low(pyb_pin_BT_REG_ON); return 0; diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index dd96e6337943e..3084601b85415 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -38,6 +38,17 @@ CFLAGS += $(shell pkg-config libusb-1.0 --cflags) LDFLAGS += $(shell pkg-config libusb-1.0 --libs) endif +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) +SRC_BTSTACK += \ + lib/btstack/src/hci_transport_h4.c \ + lib/btstack/chipset/zephyr/btstack_chipset_zephyr.c + +EXTMOD_SRC_C += \ + extmod/btstack/btstack_hci_uart.c \ + +CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK_H4=1 +endif + ifeq ($(MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC),1) include $(BTSTACK_DIR)/src/classic/Makefile.inc SRC_BTSTACK += \ diff --git a/extmod/btstack/btstack_config.h b/extmod/btstack/btstack_config.h index f420f47a5bee5..e56a84f94a16d 100644 --- a/extmod/btstack/btstack_config.h +++ b/extmod/btstack/btstack_config.h @@ -8,6 +8,7 @@ // #define ENABLE_CLASSIC #define ENABLE_LE_DATA_CHANNELS // #define ENABLE_LOG_INFO +// #define ENABLE_LOG_DEBUG #define ENABLE_LOG_ERROR // BTstack configuration. buffers, sizes, ... diff --git a/ports/stm32/btstack.c b/extmod/btstack/btstack_hci_uart.c similarity index 67% rename from ports/stm32/btstack.c rename to extmod/btstack/btstack_hci_uart.c index cbb15a86cb972..5c96e02dc0fed 100644 --- a/ports/stm32/btstack.c +++ b/extmod/btstack/btstack_hci_uart.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2020 Damien P. George + * Copyright (c) 2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,12 +32,14 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK #include "lib/btstack/src/btstack.h" -#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" -#include "lib/btstack/platform/embedded/hal_cpu.h" -#include "lib/btstack/platform/embedded/hal_time_ms.h" -#include "extmod/modbluetooth_hci.h" -#include "extmod/btstack/modbluetooth_btstack.h" +#include "extmod/mpbthci.h" +#include "extmod/btstack/btstack_hci_uart.h" + +#include "mpbtstackport.h" + +// Implements a btstack btstack_uart_block_t on top of the mphciuart.h +// interface to an HCI UART provided by the port. // We pass the bytes directly to the UART during a send, but then notify btstack in the next poll. STATIC bool send_done; @@ -48,42 +51,29 @@ STATIC size_t recv_len; STATIC size_t recv_idx; STATIC void (*recv_handler)(void); -// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the -// following three functions are empty. - -void hal_cpu_disable_irqs(void) { -} - -void hal_cpu_enable_irqs(void) { -} - -void hal_cpu_enable_irqs_and_sleep(void) { -} - -uint32_t hal_time_ms(void) { - return mp_hal_ticks_ms(); -} - STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) { + (void)uart_config; + send_done = false; recv_len = 0; recv_idx = 0; recv_handler = NULL; send_handler = NULL; - // Set up the UART periperhal. - mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID); + // Set up the UART peripheral, attach IRQ and power up the HCI controller. + // We haven't been told the baud rate yet, so defer that until btstack_uart_set_baudrate. + mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0); + mp_bluetooth_hci_controller_init(); return 0; } STATIC int btstack_uart_open(void) { - // Attach IRQ and power up the HCI controller. - mp_bluetooth_hci_uart_activate(); return 0; } STATIC int btstack_uart_close(void) { + mp_bluetooth_hci_controller_deinit(); return 0; } @@ -101,10 +91,12 @@ STATIC int btstack_uart_set_baudrate(uint32_t baudrate) { } STATIC int btstack_uart_set_parity(int parity) { + (void)parity; return 0; } STATIC int btstack_uart_set_flowcontrol(int flowcontrol) { + (void)flowcontrol; return 0; } @@ -123,14 +115,16 @@ STATIC int btstack_uart_get_supported_sleep_modes(void) { } STATIC void btstack_uart_set_sleep(btstack_uart_sleep_mode_t sleep_mode) { + (void)sleep_mode; // printf("btstack_uart_set_sleep %u\n", sleep_mode); } STATIC void btstack_uart_set_wakeup_handler(void (*wakeup_handler)(void)) { + (void)wakeup_handler; // printf("btstack_uart_set_wakeup_handler\n"); } -STATIC const btstack_uart_block_t btstack_uart_block = { +const btstack_uart_block_t mp_bluetooth_btstack_hci_uart_block = { &btstack_uart_init, &btstack_uart_open, &btstack_uart_close, @@ -146,15 +140,9 @@ STATIC const btstack_uart_block_t btstack_uart_block = { &btstack_uart_set_wakeup_handler, }; -STATIC const hci_transport_config_uart_t hci_transport_config_uart = { - HCI_TRANSPORT_CONFIG_UART, - MICROPY_HW_BLE_UART_BAUDRATE, - 3000000, - 0, - NULL, -}; +void mp_bluetooth_btstack_hci_uart_process(void) { + bool host_wake = mp_bluetooth_hci_controller_woken(); -STATIC void btstack_uart_process(void) { if (send_done) { // If we'd done a TX in the last interval, notify btstack that it's complete. send_done = false; @@ -176,48 +164,10 @@ STATIC void btstack_uart_process(void) { } } } -} - -void mp_bluetooth_hci_poll(void) { - if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_OFF) { - return; - } - // Process uart data. - bool host_wake = mp_bluetooth_hci_controller_woken(); - btstack_uart_process(); if (host_wake) { mp_bluetooth_hci_controller_sleep_maybe(); } - - // Call the BTstack run loop. - btstack_run_loop_embedded_execute_once(); -} - -void mp_bluetooth_btstack_port_init(void) { - static bool run_loop_init = false; - if (!run_loop_init) { - run_loop_init = true; - btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - } else { - btstack_run_loop_embedded_get_instance()->init(); - } - - // hci_dump_open(NULL, HCI_DUMP_STDOUT); - const hci_transport_t *transport = hci_transport_h4_instance(&btstack_uart_block); - hci_init(transport, &hci_transport_config_uart); - - // TODO: Probably not necessary for BCM (we have our own firmware loader), - // but might be worth investigating for other controllers in the future. - // hci_set_chipset(btstack_chipset_bcm_instance()); -} - -void mp_bluetooth_btstack_port_deinit(void) { - hci_power_control(HCI_POWER_OFF); -} - -void mp_bluetooth_btstack_port_start(void) { - hci_power_control(HCI_POWER_ON); } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/nimble/nimble/nimble_hci_uart.h b/extmod/btstack/btstack_hci_uart.h similarity index 66% rename from extmod/nimble/nimble/nimble_hci_uart.h rename to extmod/btstack/btstack_hci_uart.h index 646a12dac1460..8011e587dee3f 100644 --- a/extmod/nimble/nimble/nimble_hci_uart.h +++ b/extmod/btstack/btstack_hci_uart.h @@ -3,7 +3,8 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2020 Damien P. George + * Copyright (c) 2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +24,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H -#define MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H -// Extensions to extmod/modbluetooth_hci.h specific to NimBLE. +#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_HCI_UART_H +#define MICROPY_INCLUDED_EXTMOD_BTSTACK_HCI_UART_H -#include "extmod/nimble/hal/hal_uart.h" +#include "lib/btstack/src/btstack.h" -// Helpers called from ports. -void mp_bluetooth_nimble_hci_uart_process(void); +// --- Used by the port to create the HCI transport --------------------------- +extern const btstack_uart_block_t mp_bluetooth_btstack_hci_uart_block; -// Must be provided by the port. -void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg); -void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len); +// --- Called by the MicroPython port when UART data is available ------------- +void mp_bluetooth_btstack_hci_uart_process(void); -#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_NIMBLE_NIMBLE_HCI_UART_H +#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 9aef7e6879fb0..03ec12b734346 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -53,6 +53,8 @@ STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3; volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; +#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV + STATIC int btstack_error_to_errno(int err) { DEBUG_printf(" --> btstack error: %d\n", err); if (err == ERROR_CODE_SUCCESS) { @@ -300,6 +302,9 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t if (state == HCI_STATE_WORKING) { // Signal that initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; + } else if (state == HCI_STATE_HALTING) { + // Signal that de-initialisation has begun. + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_HALTING; } else if (state == HCI_STATE_OFF) { // Signal that de-initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 2fad86f22605e..7890bbfae2c4a 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -56,6 +56,7 @@ enum { MP_BLUETOOTH_BTSTACK_STATE_OFF, MP_BLUETOOTH_BTSTACK_STATE_STARTING, MP_BLUETOOTH_BTSTACK_STATE_ACTIVE, + MP_BLUETOOTH_BTSTACK_STATE_HALTING, MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT, }; diff --git a/extmod/mpbthci.c b/extmod/mpbthci.c new file mode 100644 index 0000000000000..79a865424233d --- /dev/null +++ b/extmod/mpbthci.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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. + */ + +// Default definitions in case a controller doesn't implement this. + +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +#include "extmod/mpbthci.h" + + +#endif // MICROPY_PY_BLUETOOTH diff --git a/extmod/modbluetooth_hci.h b/extmod/mpbthci.h similarity index 71% rename from extmod/modbluetooth_hci.h rename to extmod/mpbthci.h index d6b432d224710..acb5b832bab11 100644 --- a/extmod/modbluetooth_hci.h +++ b/extmod/mpbthci.h @@ -24,15 +24,15 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H -#define MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H +#ifndef MICROPY_INCLUDED_EXTMOD_MPBTHCI_H +#define MICROPY_INCLUDED_EXTMOD_MPBTHCI_H -#include "uart.h" +// --- Optionally can be implemented by the driver. --------------------------- -// Optionally can be implemented by the driver. +// Start/stop the HCI controller. +// Requires the UART to this HCI controller is available. int mp_bluetooth_hci_controller_init(void); -int mp_bluetooth_hci_controller_activate(void); -int mp_bluetooth_hci_controller_deactivate(void); +int mp_bluetooth_hci_controller_deinit(void); // Tell the controller to go to sleep (e.g. on RX if we don't think we're expecting anything more). int mp_bluetooth_hci_controller_sleep_maybe(void); @@ -41,16 +41,11 @@ bool mp_bluetooth_hci_controller_woken(void); // Wake up the controller (e.g. we're about to TX). int mp_bluetooth_hci_controller_wakeup(void); -// Storage and bindings that need to be implemented by the port. -// These are used by the stack bindings (e.g. nimble/hal_uart.c) -// as well as potentially the driver (e.g. cywbt.c). -extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; -extern pyb_uart_obj_t mp_bluetooth_hci_uart_obj; - -int mp_bluetooth_hci_uart_init(uint32_t port); -int mp_bluetooth_hci_uart_activate(void); +// --- Bindings that need to be implemented by the port. ---------------------- +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate); +int mp_bluetooth_hci_uart_deinit(void); int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate); int mp_bluetooth_hci_uart_readchar(void); int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len); -#endif // MICROPY_INCLUDED_EXTMOD_MODBLUETOOTH_HCI_H +#endif // MICROPY_INCLUDED_EXTMOD_MPBTHCI_H diff --git a/extmod/nimble/hal/hal_uart.c b/extmod/nimble/hal/hal_uart.c index fba82b830438a..c6d0850fea993 100644 --- a/extmod/nimble/hal/hal_uart.c +++ b/extmod/nimble/hal/hal_uart.c @@ -26,11 +26,9 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "pin_static_af.h" #include "nimble/ble.h" #include "extmod/nimble/hal/hal_uart.h" -#include "extmod/modbluetooth_hci.h" -#include "extmod/nimble/nimble/nimble_hci_uart.h" +#include "extmod/mpbthci.h" #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE @@ -39,6 +37,9 @@ static void *hal_uart_tx_arg; static hal_uart_rx_cb_t hal_uart_rx_cb; static void *hal_uart_rx_arg; +// Provided by the port, and also possibly shared with the driver. +extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + int hal_uart_init_cbs(uint32_t port, hal_uart_tx_cb_t tx_cb, void *tx_arg, hal_uart_rx_cb_t rx_cb, void *rx_arg) { hal_uart_tx_cb = tx_cb; hal_uart_tx_arg = tx_arg; @@ -48,9 +49,7 @@ int hal_uart_init_cbs(uint32_t port, hal_uart_tx_cb_t tx_cb, void *tx_arg, hal_u } int hal_uart_config(uint32_t port, uint32_t baudrate, uint32_t bits, uint32_t stop, uint32_t parity, uint32_t flow) { - mp_bluetooth_hci_uart_init(port); - mp_bluetooth_hci_uart_set_baudrate(baudrate); - return mp_bluetooth_hci_uart_activate(); + return mp_bluetooth_hci_uart_init(port, baudrate); } void hal_uart_start_tx(uint32_t port) { @@ -71,7 +70,7 @@ void hal_uart_start_tx(uint32_t port) { printf("\n"); #endif - mp_bluetooth_nimble_hci_uart_tx_strn((void*)mp_bluetooth_hci_cmd_buf, len); + mp_bluetooth_hci_uart_write(mp_bluetooth_hci_cmd_buf, len); } int hal_uart_close(uint32_t port) { @@ -79,7 +78,17 @@ int hal_uart_close(uint32_t port) { } void mp_bluetooth_nimble_hci_uart_process(void) { - mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb, hal_uart_rx_arg); + bool host_wake = mp_bluetooth_hci_controller_woken(); + + int chr; + while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { + // printf("UART RX: %02x\n", data); + hal_uart_rx_cb(hal_uart_rx_arg, chr); + } + + if (host_wake) { + mp_bluetooth_hci_controller_sleep_maybe(); + } } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/extmod/nimble/hal/hal_uart.h b/extmod/nimble/hal/hal_uart.h index 0ef04bc81e026..1ff1c17436dae 100644 --- a/extmod/nimble/hal/hal_uart.h +++ b/extmod/nimble/hal/hal_uart.h @@ -1,3 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_NIMBLE_HAL_HAL_UART_H #define MICROPY_INCLUDED_EXTMOD_NIMBLE_HAL_HAL_UART_H @@ -10,10 +36,13 @@ typedef int (*hal_uart_tx_cb_t)(void *arg); typedef int (*hal_uart_rx_cb_t)(void *arg, uint8_t data); -// Called by NimBLE, implemented in hal_uart.c. +// --- Called by NimBLE, implemented in hal_uart.c. --------------------------- int hal_uart_init_cbs(uint32_t port, hal_uart_tx_cb_t tx_cb, void *tx_arg, hal_uart_rx_cb_t rx_cb, void *rx_arg); int hal_uart_config(uint32_t port, uint32_t baud, uint32_t bits, uint32_t stop, uint32_t parity, uint32_t flow); void hal_uart_start_tx(uint32_t port); int hal_uart_close(uint32_t port); +// --- Called by the MicroPython port when UART data is available ------------- +void mp_bluetooth_nimble_hci_uart_process(void); + #endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_HAL_HAL_UART_H diff --git a/extmod/nimble/logcfg/logcfg.h b/extmod/nimble/logcfg/logcfg.h new file mode 100644 index 0000000000000..e9023dae65b5d --- /dev/null +++ b/extmod/nimble/logcfg/logcfg.h @@ -0,0 +1,45 @@ +/** + * This file was generated by Apache newt version: 1.8.0-dev + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H +#define MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H + +#include "py/mphal.h" +#include "modlog/modlog.h" +#include "log_common/log_common.h" + +#define MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING (1) + +#if MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING +#define DFLT_LOG_DEBUG(...) MODLOG_DEBUG(4, __VA_ARGS__) +#else +#define DFLT_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#endif + +#if MICROPY_PY_BLUETOOTH_DIAGNOSTIC_LOGGING > 1 +#define BLE_HS_LOG_DEBUG(...) MODLOG_DEBUG(4, __VA_ARGS__) +#else +#define BLE_HS_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#endif + +#define BLE_HS_LOG_INFO(...) MODLOG_INFO(4, __VA_ARGS__) +#define BLE_HS_LOG_WARN(...) MODLOG_WARN(4, __VA_ARGS__) +#define BLE_HS_LOG_ERROR(...) MODLOG_ERROR(4, __VA_ARGS__) +#define BLE_HS_LOG_CRITICAL(...) MODLOG_CRITICAL(4, __VA_ARGS__) +#define BLE_HS_LOG_DISABLED(...) MODLOG_DISABLED(4, __VA_ARGS__) + +#define DFLT_LOG_INFO(...) MODLOG_INFO(0, __VA_ARGS__) +#define DFLT_LOG_WARN(...) MODLOG_WARN(0, __VA_ARGS__) +#define DFLT_LOG_ERROR(...) MODLOG_ERROR(0, __VA_ARGS__) +#define DFLT_LOG_CRITICAL(...) MODLOG_CRITICAL(0, __VA_ARGS__) +#define DFLT_LOG_DISABLED(...) MODLOG_DISABLED(0, __VA_ARGS__) + +#define MFG_LOG_DEBUG(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_INFO(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_WARN(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_ERROR(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_CRITICAL(...) IGNORE(__VA_ARGS__) +#define MFG_LOG_DISABLED(...) MODLOG_DISABLED(128, __VA_ARGS__) + +#endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_LOGCFG_LOGCFG_H diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 36bfa6cccfa61..61ba3a3aba696 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -33,6 +33,7 @@ #include "extmod/nimble/modbluetooth_nimble.h" #include "extmod/modbluetooth.h" +#include "extmod/mpbthci.h" #include "host/ble_hs.h" #include "host/util/util.h" @@ -64,10 +65,11 @@ STATIC int8_t ble_hs_err_to_errno_table[] = { }; STATIC int ble_hs_err_to_errno(int err) { + DEBUG_printf("ble_hs_err_to_errno: %d\n", err); if (!err) { return 0; } - if (0 <= err && err < MP_ARRAY_SIZE(ble_hs_err_to_errno_table) && ble_hs_err_to_errno_table[err]) { + if (err >= 0 && (unsigned)err < MP_ARRAY_SIZE(ble_hs_err_to_errno_table) && ble_hs_err_to_errno_table[err]) { return ble_hs_err_to_errno_table[err]; } else { return MP_EIO; @@ -150,6 +152,12 @@ STATIC void sync_cb(void) { int rc; ble_addr_t addr; + DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state); + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + return; + } + rc = ble_hs_util_ensure_addr(0); // prefer public address if (rc != 0) { // https://mynewt.apache.org/latest/tutorials/ble/eddystone.html#configure-the-nimble-stack-with-an-address @@ -264,11 +272,67 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { return 0; } +#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + +// On ports such as ESP32 where we only implement the bindings, then +// the port must provide these functions. +// But for STM32 / Unix-H4, we provide a default implementation of the +// port-specific functionality. +// TODO: In the future if a port ever needs to customise these functions +// then investigate using MP_WEAK or splitting them out to another .c file. + +#include "transport/uart/ble_hci_uart.h" + +void mp_bluetooth_nimble_port_hci_init(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_init (nimble default)\n"); + // This calls mp_bluetooth_hci_uart_init (via ble_hci_uart_init --> hal_uart_config --> mp_bluetooth_hci_uart_init). + ble_hci_uart_init(); + mp_bluetooth_hci_controller_init(); +} + +void mp_bluetooth_nimble_port_hci_deinit(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_deinit (nimble default)\n"); + mp_bluetooth_hci_controller_deinit(); + mp_bluetooth_hci_uart_deinit(); +} + +void mp_bluetooth_nimble_port_start(void) { + DEBUG_printf("mp_bluetooth_nimble_port_start (nimble default)\n"); + // By default, assume port is already running its own background task (e.g. SysTick on STM32). + // ESP32 runs a FreeRTOS task, Unix has a thread. +} + +// Called when the host stop procedure has completed. +STATIC void ble_hs_shutdown_stop_cb(int status, void *arg) { + (void)status; + (void)arg; + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; +} + +STATIC struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; + +void mp_bluetooth_nimble_port_shutdown(void) { + DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n"); + // By default, just call ble_hs_stop directly and wait for the stack to stop. + + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING; + + ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL); + + while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + MICROPY_EVENT_POLL_HOOK + } +} + +#endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + int mp_bluetooth_init(void) { DEBUG_printf("mp_bluetooth_init\n"); // Clean up if necessary. mp_bluetooth_deinit(); + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING; + ble_hs_cfg.reset_cb = reset_cb; ble_hs_cfg.sync_cb = sync_cb; ble_hs_cfg.gatts_register_cb = gatts_register_cb; @@ -277,56 +341,70 @@ int mp_bluetooth_init(void) { MP_STATE_PORT(bluetooth_nimble_root_pointers) = m_new0(mp_bluetooth_nimble_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db); - mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING; + #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + // Dereference any previous NimBLE mallocs. + MP_STATE_PORT(bluetooth_nimble_memory) = NULL; + #endif - mp_bluetooth_nimble_port_preinit(); + // Allow port (ESP32) to override NimBLE's HCI init. + // Otherwise default implementation above calls ble_hci_uart_init(). + mp_bluetooth_nimble_port_hci_init(); + + // Initialise NimBLE memory and data structures. nimble_port_init(); - mp_bluetooth_nimble_port_postinit(); // By default, just register the default gap/gatt service. ble_svc_gap_init(); ble_svc_gatt_init(); + // Make sure that the HCI UART and event handling task is running. mp_bluetooth_nimble_port_start(); + // Static initialization is complete, can start processing events. + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC; + // Wait for sync callback while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { MICROPY_EVENT_POLL_HOOK } + DEBUG_printf("mp_bluetooth_init: ready\n"); return 0; } -// Called when the host stop procedure has completed. -STATIC void ble_hs_shutdown_stop_cb(int status, void *arg) { - mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; -} - -STATIC struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; - void mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit\n"); if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { return; } - mp_bluetooth_gap_advertise_stop(); - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - mp_bluetooth_gap_scan_stop(); - #endif - - mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING; + // Must call ble_hs_stop() in a port-specific way to stop the background + // task. Default implementation provided above. + if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { + mp_bluetooth_gap_advertise_stop(); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + mp_bluetooth_gap_scan_stop(); + #endif - ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL); + DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n"); - while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - MICROPY_EVENT_POLL_HOOK + mp_bluetooth_nimble_port_shutdown(); + assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF); + } else { + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; } - mp_bluetooth_nimble_port_deinit(); + // Shutdown the HCI controller. + mp_bluetooth_nimble_port_hci_deinit(); MP_STATE_PORT(bluetooth_nimble_root_pointers) = NULL; + + #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY + // Dereference any previous NimBLE mallocs. + MP_STATE_PORT(bluetooth_nimble_memory) = NULL; + #endif + DEBUG_printf("mp_bluetooth_deinit: shut down\n"); } @@ -485,7 +563,7 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { if (!append) { // Unref any previous service definitions. - for (int i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { MP_STATE_PORT(bluetooth_nimble_root_pointers)->services[i] = NULL; } MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services = 0; diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h index f44e1d69dca52..7e401781e7f9c 100644 --- a/extmod/nimble/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -43,15 +43,27 @@ typedef struct _mp_bluetooth_nimble_root_pointers_t { enum { MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF, MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING, + MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC, MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE, MP_BLUETOOTH_NIMBLE_BLE_STATE_STOPPING, }; extern volatile int mp_bluetooth_nimble_ble_state; -void mp_bluetooth_nimble_port_preinit(void); -void mp_bluetooth_nimble_port_postinit(void); -void mp_bluetooth_nimble_port_deinit(void); +// --- Optionally provided by the MicroPython port. --------------------------- +// (default implementations provided by modbluetooth_nimble.c) + +// Tell the port to init the UART and start the HCI controller. +void mp_bluetooth_nimble_port_hci_init(void); + +// Tell the port to deinit the UART and shutdown the HCI controller. +void mp_bluetooth_nimble_port_hci_deinit(void); + +// Tell the port to run its background task (i.e. poll the UART and pump events). void mp_bluetooth_nimble_port_start(void); +// Tell the port to stop its background task. +void mp_bluetooth_nimble_port_shutdown(void); + + #endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H diff --git a/extmod/nimble/nimble.mk b/extmod/nimble/nimble.mk index 4e1642c98a998..f8a68bc6f52b6 100644 --- a/extmod/nimble/nimble.mk +++ b/extmod/nimble/nimble.mk @@ -2,16 +2,19 @@ ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) -EXTMOD_SRC_C += extmod/nimble/modbluetooth_nimble.c +EXTMOD_DIR = extmod +NIMBLE_EXTMOD_DIR = $(EXTMOD_DIR)/nimble -CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 +EXTMOD_SRC_C += $(NIMBLE_EXTMOD_DIR)/modbluetooth_nimble.c -NIMBLE_EXTMOD_DIR = extmod/nimble +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 # Use NimBLE from the submodule in lib/mynewt-nimble by default, # allowing a port to use their own system version (e.g. ESP32). MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY ?= 0 +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY=$(MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY) + ifeq ($(MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY),0) NIMBLE_LIB_DIR = lib/mynewt-nimble @@ -82,7 +85,7 @@ LIB_SRC_C += $(addprefix $(NIMBLE_LIB_DIR)/, \ ) EXTMOD_SRC_C += $(addprefix $(NIMBLE_EXTMOD_DIR)/, \ - nimble/npl_os.c \ + nimble/nimble_npl_os.c \ hal/hal_uart.c \ ) @@ -98,7 +101,7 @@ INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/nimble/include INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/nimble/transport/uart/include INC += -I$(TOP)/$(NIMBLE_LIB_DIR)/porting/nimble/include -$(BUILD)/$(NIMBLE_LIB_DIR)/%.o: CFLAGS += -Wno-maybe-uninitialized -Wno-pointer-arith -Wno-unused-but-set-variable -Wno-format +$(BUILD)/$(NIMBLE_LIB_DIR)/%.o: CFLAGS += -Wno-maybe-uninitialized -Wno-pointer-arith -Wno-unused-but-set-variable -Wno-format -Wno-sign-compare endif diff --git a/extmod/nimble/nimble/npl_os.c b/extmod/nimble/nimble/nimble_npl_os.c similarity index 85% rename from extmod/nimble/nimble/npl_os.c rename to extmod/nimble/nimble/nimble_npl_os.c index 20c260405444f..ba3031a8ff694 100644 --- a/extmod/nimble/nimble/npl_os.c +++ b/extmod/nimble/nimble/nimble_npl_os.c @@ -29,16 +29,18 @@ #include "py/runtime.h" #include "nimble/ble.h" #include "nimble/nimble_npl.h" -#include "nimble/nimble_hci_uart.h" +#include "extmod/nimble/hal/hal_uart.h" -#define DEBUG_OS_printf(...) //printf(__VA_ARGS__) -#define DEBUG_MALLOC_printf(...) //printf(__VA_ARGS__) -#define DEBUG_EVENT_printf(...) //printf(__VA_ARGS__) -#define DEBUG_MUTEX_printf(...) //printf(__VA_ARGS__) -#define DEBUG_SEM_printf(...) //printf(__VA_ARGS__) -#define DEBUG_CALLOUT_printf(...) //printf(__VA_ARGS__) -#define DEBUG_TIME_printf(...) //printf(__VA_ARGS__) -#define DEBUG_CRIT_printf(...) //printf(__VA_ARGS__) +#include "extmod/modbluetooth.h" + +#define DEBUG_OS_printf(...) // printf(__VA_ARGS__) +#define DEBUG_MALLOC_printf(...) // printf(__VA_ARGS__) +#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_MUTEX_printf(...) // printf(__VA_ARGS__) +#define DEBUG_SEM_printf(...) // printf(__VA_ARGS__) +#define DEBUG_CALLOUT_printf(...) // printf(__VA_ARGS__) +#define DEBUG_TIME_printf(...) // printf(__VA_ARGS__) +#define DEBUG_CRIT_printf(...) // printf(__VA_ARGS__) bool ble_npl_os_started(void) { DEBUG_OS_printf("ble_npl_os_started\n"); @@ -138,7 +140,7 @@ int nimble_sprintf(char *str, const char *fmt, ...) { struct ble_npl_eventq *global_eventq = NULL; -void os_eventq_run_all(void) { +void mp_bluetooth_nimble_os_eventq_run_all(void) { for (struct ble_npl_eventq *evq = global_eventq; evq != NULL; evq = evq->nextq) { while (evq->head != NULL) { struct ble_npl_event *ev = evq->head; @@ -238,23 +240,39 @@ ble_npl_error_t ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens) { ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout) { DEBUG_SEM_printf("ble_npl_sem_pend(%p, %u) count=%u\n", sem, (uint)timeout, (uint)sem->count); + + // This is called by NimBLE to synchronously wait for an HCI ACK. The + // corresponding ble_npl_sem_release is called directly by the UART rx + // handler (i.e. hal_uart_rx_cb in extmod/nimble/hal/hal_uart.c). + + // So this implementation just polls the UART until either the semaphore + // is released, or the timeout occurs. + if (sem->count == 0) { uint32_t t0 = mp_hal_ticks_ms(); while (sem->count == 0 && mp_hal_ticks_ms() - t0 < timeout) { - // This function may be called at thread-level, so execute - // mp_bluetooth_nimble_hci_uart_process at raised priority. + // This can be called either from code running in NimBLE's "task" + // (i.e. PENDSV) or directly by application code, so for the + // latter case, prevent the "task" from running while we poll the + // UART directly. MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_nimble_hci_uart_process(); MICROPY_PY_BLUETOOTH_EXIT + if (sem->count != 0) { break; } - __WFI(); + + // Because we're polling, might as well wait for a UART IRQ indicating + // more data available. + mp_bluetooth_nimble_hci_uart_wfi(); } + if (sem->count == 0) { - printf("timeout\n"); + printf("NimBLE: HCI ACK timeout\n"); return BLE_NPL_TIMEOUT; } + DEBUG_SEM_printf("got response in %u ms\n", (int)(mp_hal_ticks_ms() - t0)); } sem->count -= 1; @@ -277,7 +295,7 @@ uint16_t ble_npl_sem_get_count(struct ble_npl_sem *sem) { static struct ble_npl_callout *global_callout = NULL; -void os_callout_process(void) { +void mp_bluetooth_nimble_os_callout_process(void) { uint32_t tnow = mp_hal_ticks_ms(); for (struct ble_npl_callout *c = global_callout; c != NULL; c = c->nextc) { if (!c->active) { @@ -386,12 +404,24 @@ void ble_npl_time_delay(ble_npl_time_t ticks) { /******************************************************************************/ // CRITICAL +// This is used anywhere NimBLE modifies global data structures. +// We need to protect between: +// - A MicroPython VM thread. +// - The NimBLE "task" (e.g. PENDSV on STM32, pthread on Unix). +// On STM32, by disabling PENDSV, we ensure that either: +// - If we're in the NimBLE task, we're exclusive anyway. +// - If we're in a VM thread, we can't be interrupted by the NimBLE task, or switched to another thread. +// On Unix, there's a global mutex. + +// TODO: Both ports currently use MICROPY_PY_BLUETOOTH_ENTER in their implementation, +// maybe this doesn't need to be port-specific? + uint32_t ble_npl_hw_enter_critical(void) { DEBUG_CRIT_printf("ble_npl_hw_enter_critical()\n"); - return raise_irq_pri(15); + return mp_bluetooth_nimble_hci_uart_enter_critical(); } void ble_npl_hw_exit_critical(uint32_t ctx) { DEBUG_CRIT_printf("ble_npl_hw_exit_critical(%u)\n", (uint)ctx); - restore_irq_pri(ctx); + mp_bluetooth_nimble_hci_uart_exit_critical(ctx); } diff --git a/extmod/nimble/nimble/nimble_npl_os.h b/extmod/nimble/nimble/nimble_npl_os.h index 3d886fedb58fd..55425206612ae 100644 --- a/extmod/nimble/nimble/nimble_npl_os.h +++ b/extmod/nimble/nimble/nimble_npl_os.h @@ -24,11 +24,15 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H -#define MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H +#ifndef MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NIMBLE_NPL_OS_H +#define MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NIMBLE_NPL_OS_H + +// This is included by nimble/nimble_npl.h -- include that rather than this file directly. #include +// --- Configuration of NimBLE data structures -------------------------------- + #define BLE_NPL_OS_ALIGNMENT (4) #define BLE_NPL_TIME_FOREVER (0xffffffff) @@ -63,4 +67,15 @@ struct ble_npl_sem { volatile uint16_t count; }; +// --- Called by the MicroPython port ----------------------------------------- + +void mp_bluetooth_nimble_os_eventq_run_all(void); +void mp_bluetooth_nimble_os_callout_process(void); + +// --- Must be provided by the MicroPython port ------------------------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void); +uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void); +void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state); + #endif // MICROPY_INCLUDED_STM32_NIMBLE_NIMBLE_NPL_OS_H diff --git a/extmod/nimble/syscfg/syscfg.h b/extmod/nimble/syscfg/syscfg.h index 6ac420f35e550..8a6f2338be2ab 100644 --- a/extmod/nimble/syscfg/syscfg.h +++ b/extmod/nimble/syscfg/syscfg.h @@ -2,12 +2,14 @@ #define MICROPY_INCLUDED_EXTMOD_NIMBLE_SYSCFG_H #include "py/mphal.h" -#include "uart.h" + +#include "mpnimbleport.h" void *nimble_malloc(size_t size); void nimble_free(void *ptr); void *nimble_realloc(void *ptr, size_t size); +// Redirect NimBLE malloc to the GC heap. #define malloc(size) nimble_malloc(size) #define free(ptr) nimble_free(ptr) #define realloc(ptr, size) nimble_realloc(ptr, size) @@ -88,6 +90,7 @@ int nimble_sprintf(char *str, const char *fmt, ...); #define MYNEWT_VAL_BLE_GATT_WRITE_NO_RSP (MYNEWT_VAL_BLE_ROLE_CENTRAL) #define MYNEWT_VAL_BLE_GATT_WRITE_RELIABLE (MYNEWT_VAL_BLE_ROLE_CENTRAL) #define MYNEWT_VAL_BLE_HOST (1) +#define MYNEWT_VAL_BLE_HS_AUTO_START (1) #define MYNEWT_VAL_BLE_HS_DEBUG (0) #define MYNEWT_VAL_BLE_HS_FLOW_CTRL (0) #define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL (1000) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 2cbe9f6be57f9..dcf4110cfc913 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -341,7 +341,7 @@ SRC_C = \ modnetwork.c \ network_lan.c \ network_ppp.c \ - nimble.c \ + mpnimbleport.c \ modsocket.c \ modesp.c \ esp32_partition.c \ diff --git a/ports/esp32/nimble.c b/ports/esp32/mpnimbleport.c similarity index 66% rename from ports/esp32/nimble.c rename to ports/esp32/mpnimbleport.c index 16829732c30e5..a58fcbdbf49f4 100644 --- a/ports/esp32/nimble.c +++ b/ports/esp32/mpnimbleport.c @@ -30,28 +30,48 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE +#define DEBUG_printf(...) // printf("nimble (esp32): " __VA_ARGS__) + #include "esp_nimble_hci.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" +#include "extmod/nimble/modbluetooth_nimble.h" + STATIC void ble_host_task(void *param) { + DEBUG_printf("ble_host_task\n"); nimble_port_run(); // This function will return only when nimble_port_stop() is executed. nimble_port_freertos_deinit(); } -void mp_bluetooth_nimble_port_preinit(void) { +void mp_bluetooth_nimble_port_hci_init(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_init\n"); esp_nimble_hci_and_controller_init(); } -void mp_bluetooth_nimble_port_postinit(void) { -} +void mp_bluetooth_nimble_port_hci_deinit(void) { + DEBUG_printf("mp_bluetooth_nimble_port_hci_deinit\n"); -void mp_bluetooth_nimble_port_deinit(void) { - nimble_port_stop(); + esp_nimble_hci_and_controller_deinit(); } void mp_bluetooth_nimble_port_start(void) { + DEBUG_printf("mp_bluetooth_nimble_port_start\n"); nimble_port_freertos_init(ble_host_task); } +void mp_bluetooth_nimble_port_shutdown(void) { + DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); + + // Despite the name, these is an ESP32-specific (no other NimBLE ports have these functions). + // Calls ble_hs_stop() and waits for stack shutdown. + nimble_port_stop(); + + // Shuts down the event queue. + nimble_port_deinit(); + + // Mark stack as shutdown. + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; +} + #endif diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index e7d2e2abc2caf..7b68299e04f70 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -480,7 +480,7 @@ endif ifeq ($(MICROPY_PY_BLUETOOTH),1) -SRC_C += modbluetooth_hci.c +SRC_C += mpbthciport.c ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) @@ -490,12 +490,13 @@ endif ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) include $(TOP)/extmod/nimble/nimble.mk -SRC_C += nimble.c +SRC_C += mpnimbleport.c endif ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 include $(TOP)/extmod/btstack/btstack.mk -SRC_C += btstack.c +SRC_C += mpbtstackport.c endif ifeq ($(MICROPY_PY_NETWORK_CYW43),1) diff --git a/ports/stm32/modbluetooth_hci.c b/ports/stm32/mpbthciport.c similarity index 75% rename from ports/stm32/modbluetooth_hci.c rename to ports/stm32/mpbthciport.c index 54c60adc01181..71a9e4fc7f433 100644 --- a/ports/stm32/modbluetooth_hci.c +++ b/ports/stm32/mpbthciport.c @@ -26,17 +26,20 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #include "systick.h" #include "pendsv.h" +#include "lib/utils/mpirq.h" #include "py/obj.h" #if MICROPY_PY_BLUETOOTH +#define DEBUG_printf(...) // printf(__VA_ARGS__) + uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; -// Must be provided by the stack bindings. +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). extern void mp_bluetooth_hci_poll(void); // Hook for pendsv poller to run this periodically every 128ms @@ -61,34 +64,19 @@ STATIC uint16_t hci_uart_rx_buf_cur; STATIC uint16_t hci_uart_rx_buf_len; STATIC uint8_t hci_uart_rx_buf_data[256]; -int mp_bluetooth_hci_controller_deactivate(void) { - return 0; -} - -int mp_bluetooth_hci_controller_sleep_maybe(void) { - return 0; -} - -bool mp_bluetooth_hci_controller_woken(void) { - return true; -} - -int mp_bluetooth_hci_controller_wakeup(void) { - return 0; -} - -int mp_bluetooth_hci_uart_init(uint32_t port) { +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { (void)port; - return 0; -} - -int mp_bluetooth_hci_uart_activate(void) { + (void)baudrate; rfcore_ble_init(); hci_uart_rx_buf_cur = 0; hci_uart_rx_buf_len = 0; return 0; } +int mp_bluetooth_hci_uart_deinit(void) { + return 0; +} + int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { (void)baudrate; return 0; @@ -140,13 +128,16 @@ mp_irq_obj_t mp_bluetooth_hci_uart_irq_obj; static uint8_t hci_uart_rxbuf[512]; mp_obj_t mp_uart_interrupt(mp_obj_t self_in) { - // New HCI data, schedule mp_bluetooth_hci_poll to make the stack handle it. + // DEBUG_printf("mp_uart_interrupt\n"); + // New HCI data, schedule mp_bluetooth_hci_poll via PENDSV to make the stack handle it. mp_bluetooth_hci_poll_wrapper(0); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt); -int mp_bluetooth_hci_uart_init(uint32_t port) { +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + DEBUG_printf("mp_bluetooth_hci_uart_init (stm32)\n"); + // bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h. mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type; mp_bluetooth_hci_uart_obj.uart_id = port; @@ -154,16 +145,29 @@ int mp_bluetooth_hci_uart_init(uint32_t port) { mp_bluetooth_hci_uart_obj.timeout = 2; mp_bluetooth_hci_uart_obj.timeout_char = 2; MP_STATE_PORT(pyb_uart_obj_all)[mp_bluetooth_hci_uart_obj.uart_id - 1] = &mp_bluetooth_hci_uart_obj; + + // This also initialises the UART. + mp_bluetooth_hci_uart_set_baudrate(baudrate); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_uart_deinit (stm32)\n"); + // TODO: deinit mp_bluetooth_hci_uart_obj + return 0; } int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + DEBUG_printf("mp_bluetooth_hci_uart_set_baudrate(%lu) (stm32)\n", baudrate); + if (!baudrate) { + return -1; + } + uart_init(&mp_bluetooth_hci_uart_obj, baudrate, UART_WORDLENGTH_8B, UART_PARITY_NONE, UART_STOPBITS_1, UART_HWCONTROL_RTS | UART_HWCONTROL_CTS); uart_set_rxbuf(&mp_bluetooth_hci_uart_obj, sizeof(hci_uart_rxbuf), hci_uart_rxbuf); - return 0; -} -int mp_bluetooth_hci_uart_activate(void) { // Add IRQ handler for IDLE (i.e. packet finished). uart_irq_config(&mp_bluetooth_hci_uart_obj, false); mp_irq_init(&mp_bluetooth_hci_uart_irq_obj, &uart_irq_methods, MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj)); @@ -173,13 +177,12 @@ int mp_bluetooth_hci_uart_activate(void) { mp_bluetooth_hci_uart_irq_obj.ishard = true; uart_irq_config(&mp_bluetooth_hci_uart_obj, true); - mp_bluetooth_hci_controller_init(); - mp_bluetooth_hci_controller_activate(); - return 0; } int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + // DEBUG_printf("mp_bluetooth_hci_uart_write (stm32)\n"); + mp_bluetooth_hci_controller_wakeup(); uart_tx_strn(&mp_bluetooth_hci_uart_obj, (void *)buf, len); return 0; @@ -188,7 +191,10 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { // This function expects the controller to be in the wake state via a previous call // to mp_bluetooth_hci_controller_woken. int mp_bluetooth_hci_uart_readchar(void) { + // DEBUG_printf("mp_bluetooth_hci_uart_readchar (stm32)\n"); + if (uart_rx_any(&mp_bluetooth_hci_uart_obj)) { + // DEBUG_printf("... available\n"); return uart_rx_char(&mp_bluetooth_hci_uart_obj); } else { return -1; @@ -197,4 +203,33 @@ int mp_bluetooth_hci_uart_readchar(void) { #endif // defined(STM32WB) +// Default (weak) implementation of the HCI controller interface. +// A driver (e.g. cywbt43.c) can override these for controller-specific +// functionality (i.e. power management). + +MP_WEAK int mp_bluetooth_hci_controller_init(void) { + DEBUG_printf("mp_bluetooth_hci_controller_init (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_controller_deinit (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_sleep_maybe(void) { + DEBUG_printf("mp_bluetooth_hci_controller_sleep_maybe (default)\n"); + return 0; +} + +MP_WEAK bool mp_bluetooth_hci_controller_woken(void) { + DEBUG_printf("mp_bluetooth_hci_controller_woken (default)\n"); + return true; +} + +MP_WEAK int mp_bluetooth_hci_controller_wakeup(void) { + DEBUG_printf("mp_bluetooth_hci_controller_wakeup (default)\n"); + return 0; +} + #endif // MICROPY_PY_BLUETOOTH diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c new file mode 100644 index 0000000000000..a031a6a151aff --- /dev/null +++ b/ports/stm32/mpbtstackport.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + +#include "lib/btstack/src/btstack.h" +#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" +#include "lib/btstack/platform/embedded/hal_cpu.h" +#include "lib/btstack/platform/embedded/hal_time_ms.h" + +#include "extmod/mpbthci.h" +#include "extmod/btstack/btstack_hci_uart.h" +#include "extmod/btstack/modbluetooth_btstack.h" + +// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the +// following three functions are empty. + +void hal_cpu_disable_irqs(void) { +} + +void hal_cpu_enable_irqs(void) { +} + +void hal_cpu_enable_irqs_and_sleep(void) { +} + +uint32_t hal_time_ms(void) { + return mp_hal_ticks_ms(); +} + +STATIC const hci_transport_config_uart_t hci_transport_config_uart = { + HCI_TRANSPORT_CONFIG_UART, + MICROPY_HW_BLE_UART_BAUDRATE, + 3000000, + 0, + NULL, +}; + +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_OFF) { + return; + } + + // Process uart data. + if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_HALTING) { + mp_bluetooth_btstack_hci_uart_process(); + } + + // Call the BTstack run loop. + btstack_run_loop_embedded_execute_once(); +} + +void mp_bluetooth_btstack_port_init(void) { + static bool run_loop_init = false; + if (!run_loop_init) { + run_loop_init = true; + btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); + } else { + btstack_run_loop_embedded_get_instance()->init(); + } + + // hci_dump_open(NULL, HCI_DUMP_STDOUT); + const hci_transport_t *transport = hci_transport_h4_instance(&mp_bluetooth_btstack_hci_uart_block); + hci_init(transport, &hci_transport_config_uart); + + // TODO: Probably not necessary for BCM (we have our own firmware loader in the cyw43 driver), + // but might be worth investigating for other controllers in the future. + // hci_set_chipset(btstack_chipset_bcm_instance()); +} + +void mp_bluetooth_btstack_port_deinit(void) { + hci_power_control(HCI_POWER_OFF); + hci_close(); +} + +void mp_bluetooth_btstack_port_start(void) { + hci_power_control(HCI_POWER_ON); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/ports/stm32/mpbtstackport.h b/ports/stm32/mpbtstackport.h new file mode 100644 index 0000000000000..110f921d9a9f8 --- /dev/null +++ b/ports/stm32/mpbtstackport.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_STM32_BTSTACK_PORT_H +#define MICROPY_INCLUDED_STM32_BTSTACK_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#include "uart.h" + +#endif // MICROPY_INCLUDED_STM32_BTSTACK_PORT_H diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 14a05898951ba..e1e1491d64c6d 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -298,6 +298,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; #if MICROPY_BLUETOOTH_NIMBLE struct _mp_bluetooth_nimble_root_pointers_t; +struct _mp_bluetooth_nimble_malloc_t; #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE void **bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; #else #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE @@ -410,14 +411,20 @@ static inline mp_uint_t disable_irq(void) { #define MICROPY_THREAD_YIELD() #endif -// The LwIP interface must run at a raised IRQ priority -#define MICROPY_PY_LWIP_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); -#define MICROPY_PY_LWIP_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); -#define MICROPY_PY_LWIP_EXIT restore_irq_pri(atomic_state); +// For regular code that wants to prevent "background tasks" from running. +// These background tasks (LWIP, Bluetooth) run in PENDSV context. +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); -// Bluetooth calls must run at a raised IRQ priority -#define MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_LWIP_ENTER -#define MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_LWIP_EXIT +// Prevent the "LWIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + +// Prevent the "Bluetooth task" from running (either NimBLE or btstack). +#define MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_PENDSV_EXIT // We need an implementation of the log2 function which is not a macro #define MP_NEED_LOG2 (1) diff --git a/ports/stm32/nimble.c b/ports/stm32/mpnimbleport.c similarity index 56% rename from ports/stm32/nimble.c rename to ports/stm32/mpnimbleport.c index 0d349585fb1ae..1d7c095139cde 100644 --- a/ports/stm32/nimble.c +++ b/ports/stm32/mpnimbleport.c @@ -31,58 +31,41 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE -#include "transport/uart/ble_hci_uart.h" #include "host/ble_hs.h" +#include "nimble/nimble_npl.h" -#include "extmod/modbluetooth_hci.h" +#include "extmod/mpbthci.h" #include "extmod/nimble/modbluetooth_nimble.h" -#include "extmod/nimble/nimble/nimble_hci_uart.h" - -extern void os_eventq_run_all(void); -extern void os_callout_process(void); +#include "extmod/nimble/hal/hal_uart.h" +// This implements the Nimble "background task". It's called at PENDSV +// priority, either every 128ms or whenever there's UART data available. +// Because it's called via PENDSV, you can implicitly consider that it +// is surrounded by MICROPY_PY_BLUETOOTH_ENTER / MICROPY_PY_BLUETOOTH_EXIT. void mp_bluetooth_hci_poll(void) { - if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - return; - } - - mp_bluetooth_nimble_hci_uart_process(); - os_callout_process(); - os_eventq_run_all(); -} - -void mp_bluetooth_nimble_port_preinit(void) { - MP_STATE_PORT(bluetooth_nimble_memory) = NULL; - ble_hci_uart_init(); -} + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // Ask NimBLE to process UART data. + mp_bluetooth_nimble_hci_uart_process(); -void mp_bluetooth_nimble_port_postinit(void) { + // Run pending background operations and events, but only after HCI sync. + mp_bluetooth_nimble_os_callout_process(); + mp_bluetooth_nimble_os_eventq_run_all(); + } } -void mp_bluetooth_nimble_port_deinit(void) { - mp_bluetooth_hci_controller_deactivate(); -} +// --- Port-specific helpers for the generic NimBLE bindings. ----------------- -void mp_bluetooth_nimble_port_start(void) { - ble_hs_start(); +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFI(); } -void mp_bluetooth_nimble_hci_uart_rx(hal_uart_rx_cb_t rx_cb, void *rx_arg) { - bool host_wake = mp_bluetooth_hci_controller_woken(); - - int chr; - while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { - // printf("UART RX: %02x\n", data); - rx_cb(rx_arg, chr); - } - - if (host_wake) { - mp_bluetooth_hci_controller_sleep_maybe(); - } +uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void) { + MICROPY_PY_BLUETOOTH_ENTER + return atomic_state; } -void mp_bluetooth_nimble_hci_uart_tx_strn(const char *str, uint len) { - mp_bluetooth_hci_uart_write((const uint8_t *)str, len); +void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state) { + MICROPY_PY_BLUETOOTH_EXIT } #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/stm32/mpnimbleport.h b/ports/stm32/mpnimbleport.h new file mode 100644 index 0000000000000..f3ee597183a04 --- /dev/null +++ b/ports/stm32/mpnimbleport.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_STM32_NIMBLE_PORT_H +#define MICROPY_INCLUDED_STM32_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#include "uart.h" + +#endif // MICROPY_INCLUDED_STM32_NIMBLE_PORT_H diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 3e2fa63a16c3b..a9ba5edfad9af 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -140,18 +140,19 @@ ifeq ($(HAVE_LIBUSB),1) CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=1 +# Runs in a thread, cannot make calls into the VM. +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=0 MICROPY_BLUETOOTH_BTSTACK ?= 1 MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) GIT_SUBMODULES += lib/btstack - include $(TOP)/extmod/btstack/btstack.mk endif endif + endif ifeq ($(MICROPY_PY_FFI),1) @@ -198,7 +199,9 @@ SRC_C = \ alloc.c \ coverage.c \ fatfs_port.c \ - btstack_usb.c \ + mpbtstackport_common.c \ + mpbtstackport_usb.c \ + mpnimbleport.c \ $(SRC_MOD) \ $(wildcard $(VARIANT_DIR)/*.c) diff --git a/ports/unix/mpbtstackport.h b/ports/unix/mpbtstackport.h new file mode 100644 index 0000000000000..29433cc7fb006 --- /dev/null +++ b/ports/unix/mpbtstackport.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_UNIX_BTSTACK_PORT_H +#define MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H + +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (1000000) + +bool mp_bluetooth_hci_poll(void); + +#if MICROPY_BLUETOOTH_BTSTACK_USB +void mp_bluetooth_btstack_port_init_usb(void); +#endif + +#endif // MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c new file mode 100644 index 0000000000000..402cf95283441 --- /dev/null +++ b/ports/unix/mpbtstackport_common.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + +#include "lib/btstack/src/btstack.h" + +#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" +#include "lib/btstack/platform/embedded/hal_cpu.h" +#include "lib/btstack/platform/embedded/hal_time_ms.h" + +#include "extmod/btstack/modbluetooth_btstack.h" + +#include "mpbtstackport.h" + +// Called by the UART polling thread in mpbthciport.c, or by the USB polling thread in mpbtstackport_usb.c. +bool mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_HALTING) { + // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + btstack_run_loop_embedded_execute_once(); + MICROPY_END_ATOMIC_SECTION(atomic_state); + + return true; + } + + return false; +} + +// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the +// following three functions are empty. + +void hal_cpu_disable_irqs(void) { +} + +void hal_cpu_enable_irqs(void) { +} + +void hal_cpu_enable_irqs_and_sleep(void) { +} + +uint32_t hal_time_ms(void) { + return mp_hal_ticks_ms(); +} + +void mp_bluetooth_btstack_port_init(void) { + static bool run_loop_init = false; + if (!run_loop_init) { + run_loop_init = true; + btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); + } else { + btstack_run_loop_embedded_get_instance()->init(); + } + + // hci_dump_open(NULL, HCI_DUMP_STDOUT); + + #if MICROPY_BLUETOOTH_BTSTACK_USB + mp_bluetooth_btstack_port_init_usb(); + #endif +} + +void mp_hal_get_mac(int idx, uint8_t buf[6]) { +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/ports/unix/btstack_usb.c b/ports/unix/mpbtstackport_usb.c similarity index 54% rename from ports/unix/btstack_usb.c rename to ports/unix/mpbtstackport_usb.c index ab6a49f39ae73..28d2c8c543f71 100644 --- a/ports/unix/btstack_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -31,7 +31,7 @@ #include "py/mperrno.h" #include "py/mphal.h" -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB #include "lib/btstack/src/btstack.h" #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" @@ -40,76 +40,15 @@ #include "extmod/btstack/modbluetooth_btstack.h" +#include "mpbtstackport.h" + #if !MICROPY_PY_THREAD #error Unix btstack requires MICROPY_PY_THREAD #endif STATIC const useconds_t USB_POLL_INTERVAL_US = 1000; -STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; - -STATIC uint8_t local_addr[6] = {0}; -STATIC uint8_t static_address[6] = {0}; -STATIC volatile bool have_addr = false; -STATIC bool using_static_address = false; - -STATIC btstack_packet_callback_registration_t hci_event_callback_registration; - -STATIC void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - if (packet_type != HCI_EVENT_PACKET) { - return; - } - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) { - return; - } - gap_local_bd_addr(local_addr); - if (using_static_address) { - memcpy(local_addr, static_address, sizeof(local_addr)); - } - have_addr = true; - break; - case HCI_EVENT_COMMAND_COMPLETE: - if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { - reverse_48(&packet[7], static_address); - gap_random_address_set(static_address); - using_static_address = true; - have_addr = true; - } - break; - default: - break; - } -} - -// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the -// following three functions are empty. - -void hal_cpu_disable_irqs(void) { -} - -void hal_cpu_enable_irqs(void) { -} - -void hal_cpu_enable_irqs_and_sleep(void) { -} - -uint32_t hal_time_ms(void) { - return mp_hal_ticks_ms(); -} - -void mp_bluetooth_btstack_port_init(void) { - static bool run_loop_init = false; - if (!run_loop_init) { - run_loop_init = true; - btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - } else { - btstack_run_loop_embedded_get_instance()->init(); - } - +void mp_bluetooth_btstack_port_init_usb(void) { // MICROPYBTUSB can be a ':'' or '-' separated port list. char *path = getenv("MICROPYBTUSB"); if (path != NULL) { @@ -128,11 +67,7 @@ void mp_bluetooth_btstack_port_init(void) { hci_transport_usb_set_path(usb_path_len, usb_path); } - // hci_dump_open(NULL, HCI_DUMP_STDOUT); hci_init(hci_transport_usb_instance(), NULL); - - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); } STATIC pthread_t bstack_thread_id; @@ -142,9 +77,12 @@ void mp_bluetooth_btstack_port_deinit(void) { // Wait for the poll loop to terminate when the state is set to OFF. pthread_join(bstack_thread_id, NULL); - have_addr = false; } + +// Provided by mpbstackport_common.c. +extern bool mp_bluetooth_hci_poll(void); + STATIC void *btstack_thread(void *arg) { (void)arg; hci_power_control(HCI_POWER_ON); @@ -155,19 +93,15 @@ STATIC void *btstack_thread(void *arg) { // in modbluetooth_btstack.c setting the state back to OFF. // Or, if a timeout results in it being set to TIMEOUT. - while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - btstack_run_loop_embedded_execute_once(); - MICROPY_END_ATOMIC_SECTION(atomic_state); + while (true) { + if (!mp_bluetooth_hci_poll()) { + break; + } // The USB transport schedules events to the run loop at 1ms intervals, // and the implementation currently polls rather than selects. usleep(USB_POLL_INTERVAL_US); } - - hci_close(); - return NULL; } @@ -179,13 +113,4 @@ void mp_bluetooth_btstack_port_start(void) { pthread_create(&bstack_thread_id, &attr, &btstack_thread, NULL); } -void mp_hal_get_mac(int idx, uint8_t buf[6]) { - if (idx == MP_HAL_MAC_BDADDR) { - if (!have_addr) { - mp_raise_OSError(MP_ENODEV); - } - memcpy(buf, local_addr, sizeof(local_addr)); - } -} - -#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 08605842fd5bd..9e24ab10cd263 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -309,9 +309,11 @@ void mp_unix_mark_exec(void); #define MP_STATE_PORT MP_STATE_VM -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#if MICROPY_PY_BLUETOOTH +#if MICROPY_BLUETOOTH_BTSTACK struct _mp_bluetooth_btstack_root_pointers_t; #define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers; +#endif #else #define MICROPY_BLUETOOTH_ROOT_POINTERS #endif From 5b08676d6a644487c4ec6f63aef02888f8da8b19 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 17 Aug 2020 18:12:20 +1000 Subject: [PATCH 246/352] extmod/nimble: Set struct alignment correctly on 64-bit arch. --- extmod/nimble/nimble/nimble_npl_os.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/nimble/nimble/nimble_npl_os.h b/extmod/nimble/nimble/nimble_npl_os.h index 55425206612ae..bfa35d0952f25 100644 --- a/extmod/nimble/nimble/nimble_npl_os.h +++ b/extmod/nimble/nimble/nimble_npl_os.h @@ -33,7 +33,7 @@ // --- Configuration of NimBLE data structures -------------------------------- -#define BLE_NPL_OS_ALIGNMENT (4) +#define BLE_NPL_OS_ALIGNMENT (sizeof(uintptr_t)) #define BLE_NPL_TIME_FOREVER (0xffffffff) typedef uint32_t ble_npl_time_t; From f3f31ac9599fcf1c94dca20046da69773cc3418a Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 16:28:55 +1000 Subject: [PATCH 247/352] extmod/nimble: Make nimble_malloc work with allocated size. --- extmod/nimble/nimble/nimble_npl_os.c | 104 ++++++++++++++++++--------- ports/stm32/mpconfigport.h | 2 +- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/extmod/nimble/nimble/nimble_npl_os.c b/extmod/nimble/nimble/nimble_npl_os.c index ba3031a8ff694..500f7c0a51e0b 100644 --- a/extmod/nimble/nimble/nimble_npl_os.c +++ b/extmod/nimble/nimble/nimble_npl_os.c @@ -58,44 +58,58 @@ void *ble_npl_get_current_task_id(void) { // Maintain a linked list of heap memory that we've passed to Nimble, // discoverable via the bluetooth_nimble_memory root pointer. -// MP_STATE_PORT(bluetooth_nimble_memory) is a pointer to [next, prev, data...]. +typedef struct _mp_bluetooth_nimble_malloc_t { + struct _mp_bluetooth_nimble_malloc_t *prev; + struct _mp_bluetooth_nimble_malloc_t *next; + size_t size; + uint8_t data[]; +} mp_bluetooth_nimble_malloc_t; // TODO: This is duplicated from mbedtls. Perhaps make this a generic feature? -void *m_malloc_bluetooth(size_t size) { - void **ptr = m_malloc0(size + 2 * sizeof(uintptr_t)); - if (MP_STATE_PORT(bluetooth_nimble_memory) != NULL) { - MP_STATE_PORT(bluetooth_nimble_memory)[0] = ptr; +STATIC void *m_malloc_bluetooth(size_t size) { + size += sizeof(mp_bluetooth_nimble_malloc_t); + mp_bluetooth_nimble_malloc_t *alloc = m_malloc0(size); + alloc->size = size; + alloc->next = MP_STATE_PORT(bluetooth_nimble_memory); + if (alloc->next) { + alloc->next->prev = alloc; } - ptr[0] = NULL; - ptr[1] = MP_STATE_PORT(bluetooth_nimble_memory); - MP_STATE_PORT(bluetooth_nimble_memory) = ptr; - return &ptr[2]; + MP_STATE_PORT(bluetooth_nimble_memory) = alloc; + return alloc->data; } -void m_free_bluetooth(void *ptr_in) { - void **ptr = &((void**)ptr_in)[-2]; - if (ptr[1] != NULL) { - ((void**)ptr[1])[0] = ptr[0]; +STATIC mp_bluetooth_nimble_malloc_t* get_nimble_malloc(void *ptr) { + return (mp_bluetooth_nimble_malloc_t*)((uintptr_t)ptr - sizeof(mp_bluetooth_nimble_malloc_t)); +} + +STATIC void m_free_bluetooth(void *ptr) { + mp_bluetooth_nimble_malloc_t *alloc = get_nimble_malloc(ptr); + if (alloc->next) { + alloc->next->prev = alloc->prev; } - if (ptr[0] != NULL) { - ((void**)ptr[0])[1] = ptr[1]; + if (alloc->prev) { + alloc->prev->next = alloc->next; } else { - MP_STATE_PORT(bluetooth_nimble_memory) = ptr[1]; + MP_STATE_PORT(bluetooth_nimble_memory) = NULL; } - m_free(ptr); + m_free(alloc + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + , alloc->size + #endif + ); } // Check if a nimble ptr is tracked. // If it isn't, that means that it's from a previous soft-reset cycle. STATIC bool is_valid_nimble_malloc(void *ptr) { DEBUG_MALLOC_printf("NIMBLE is_valid_nimble_malloc(%p)\n", ptr); - void** search = MP_STATE_PORT(bluetooth_nimble_memory); - while (search) { - if (&search[2] == ptr) { + mp_bluetooth_nimble_malloc_t *alloc = MP_STATE_PORT(bluetooth_nimble_memory); + while (alloc) { + DEBUG_MALLOC_printf("NIMBLE checking: %p\n", alloc->data); + if (alloc->data == ptr) { return true; } - - search = (void**)search[1]; + alloc = alloc->next; } return false; } @@ -110,22 +124,46 @@ void *nimble_malloc(size_t size) { // Only free if it's still a valid pointer. void nimble_free(void *ptr) { DEBUG_MALLOC_printf("NIMBLE free(%p)\n", ptr); - if (ptr && is_valid_nimble_malloc(ptr)) { - m_free_bluetooth(ptr); + + if (ptr) { + // After a stack re-init, NimBLE has variables in BSS that might be + // still pointing to old allocations from a previous init. We can't do + // anything about this (e.g. ble_gatts_free_mem is private). But we + // can identify that this is a non-null, invalid alloc because it + // won't be in our list, so ignore it because it is effectively free'd + // anyway (it's not referenced by anything the GC can find). + if (is_valid_nimble_malloc(ptr)) { + m_free_bluetooth(ptr); + } } } // Only realloc if it's still a valid pointer. Otherwise just malloc. -void *nimble_realloc(void *ptr, size_t size) { - // This is only used by ble_gatts.c to grow the queue of pending services to be registered. - DEBUG_MALLOC_printf("NIMBLE realloc(%p, %u)\n", ptr, (uint)size); - void *ptr2 = nimble_malloc(size); - if (ptr && is_valid_nimble_malloc(ptr)) { - // If it's a realloc and we still have the old data, then copy it. - // This will happen as we add services. - memcpy(ptr2, ptr, size); - m_free_bluetooth(ptr); +void *nimble_realloc(void *ptr, size_t new_size) { + DEBUG_MALLOC_printf("NIMBLE realloc(%p, %u)\n", ptr, (uint)new_size); + + if (!ptr) { + return nimble_malloc(new_size); } + + assert(is_valid_nimble_malloc(ptr)); + + // Existing alloc is big enough. + mp_bluetooth_nimble_malloc_t *alloc = get_nimble_malloc(ptr); + size_t old_size = alloc->size - sizeof(mp_bluetooth_nimble_malloc_t); + if (old_size >= new_size) { + return ptr; + } + + // Allocate a new, larger region. + void *ptr2 = m_malloc_bluetooth(new_size); + + // Copy old, smaller region into new region. + memcpy(ptr2, ptr, old_size); + m_free_bluetooth(ptr); + + DEBUG_MALLOC_printf(" --> %p\n", ptr2); + return ptr2; } diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index e1e1491d64c6d..95b539603fb95 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -299,7 +299,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; #if MICROPY_BLUETOOTH_NIMBLE struct _mp_bluetooth_nimble_root_pointers_t; struct _mp_bluetooth_nimble_malloc_t; -#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE void **bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; +#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE struct _mp_bluetooth_nimble_malloc_t *bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; #else #define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE #endif From aa18ab7db2a055440cb4798487c12809c74935c5 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 16:29:22 +1000 Subject: [PATCH 248/352] extmod/nimble: Implement NimBLE mutex. --- extmod/nimble/nimble/nimble_npl_os.c | 49 +++++++++++++++++++++++++--- extmod/nimble/nimble/nimble_npl_os.h | 1 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/extmod/nimble/nimble/nimble_npl_os.c b/extmod/nimble/nimble/nimble_npl_os.c index 500f7c0a51e0b..46f7a0b291303 100644 --- a/extmod/nimble/nimble/nimble_npl_os.c +++ b/extmod/nimble/nimble/nimble_npl_os.c @@ -249,21 +249,62 @@ void ble_npl_event_set_arg(struct ble_npl_event *ev, void *arg) { /******************************************************************************/ // MUTEX +// This is what MICROPY_BEGIN_ATOMIC_SECTION returns on Unix (i.e. we don't +// need to preserve the atomic state to unlock). +#define ATOMIC_STATE_MUTEX_NOT_HELD 0xffffffff + ble_npl_error_t ble_npl_mutex_init(struct ble_npl_mutex *mu) { DEBUG_MUTEX_printf("ble_npl_mutex_init(%p)\n", mu); mu->locked = 0; + mu->atomic_state = ATOMIC_STATE_MUTEX_NOT_HELD; return BLE_NPL_OK; } ble_npl_error_t ble_npl_mutex_pend(struct ble_npl_mutex *mu, ble_npl_time_t timeout) { - DEBUG_MUTEX_printf("ble_npl_mutex_pend(%p, %u) locked=%u\n", mu, (uint)timeout, (uint)mu->locked); - mu->locked = 1; + DEBUG_MUTEX_printf("ble_npl_mutex_pend(%p, %u) locked=%u irq=%d\n", mu, (uint)timeout, (uint)mu->locked); + + // This is a recursive mutex which we implement on top of the IRQ priority + // scheme. Unfortunately we have a single piece of global storage, where + // enter/exit critical needs an "atomic state". + + // There are two different acquirers, either running in a VM thread (i.e. + // a direct Python call into NimBLE), or in the NimBLE task (i.e. polling + // or UART RX). + + // On STM32 the NimBLE task runs in PENDSV, so cannot be interrupted by a VM thread. + // Therefore we only need to ensure that a VM thread that acquires a currently-unlocked mutex + // now raises the priority (thus preventing context switches to other VM threads and + // the PENDSV irq). If the mutex is already locked, then it must have been acquired + // by us. + + // On Unix, the critical section is completely recursive and doesn't require us to manage + // state so we just acquire and release every time. + + // TODO: The "volatile" on locked/atomic_state isn't enough to protect against memory re-ordering. + + // First acquirer of this mutex always enters the critical section, unless + // we're on Unix where it happens every time. + if (mu->atomic_state == ATOMIC_STATE_MUTEX_NOT_HELD) { + mu->atomic_state = mp_bluetooth_nimble_hci_uart_enter_critical(); + } + + ++mu->locked; + return BLE_NPL_OK; } ble_npl_error_t ble_npl_mutex_release(struct ble_npl_mutex *mu) { - DEBUG_MUTEX_printf("ble_npl_mutex_release(%p) locked=%u\n", mu, (uint)mu->locked); - mu->locked = 0; + DEBUG_MUTEX_printf("ble_npl_mutex_release(%p) locked=%u irq=%d\n", mu, (uint)mu->locked); + assert(mu->locked > 0); + + --mu->locked; + + // Only exit the critical section for the final release, unless we're on Unix. + if (mu->locked == 0 || mu->atomic_state == ATOMIC_STATE_MUTEX_NOT_HELD) { + mp_bluetooth_nimble_hci_uart_exit_critical(mu->atomic_state); + mu->atomic_state = ATOMIC_STATE_MUTEX_NOT_HELD; + } + return BLE_NPL_OK; } diff --git a/extmod/nimble/nimble/nimble_npl_os.h b/extmod/nimble/nimble/nimble_npl_os.h index bfa35d0952f25..3ef07aa9ccd12 100644 --- a/extmod/nimble/nimble/nimble_npl_os.h +++ b/extmod/nimble/nimble/nimble_npl_os.h @@ -61,6 +61,7 @@ struct ble_npl_callout { struct ble_npl_mutex { volatile uint8_t locked; + volatile uint32_t atomic_state; }; struct ble_npl_sem { From feed69aa5c4e9fda037497a1b63c05a27b164920 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 16:40:55 +1000 Subject: [PATCH 249/352] unix/Makefile: Always enable -f*-sections regardless of DEBUG setting. --- ports/unix/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index a9ba5edfad9af..090b58d8ec4e2 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -47,10 +47,12 @@ ifdef DEBUG COPT ?= -O0 else COPT ?= -Os -COPT += -fdata-sections -ffunction-sections COPT += -DNDEBUG endif +# Remove unused sections. +COPT += -fdata-sections -ffunction-sections + # Always enable symbols -- They're occasionally useful, and don't make it into the # final .bin/.hex/.dfu so the extra size doesn't matter. CFLAGS += -g From 1b1b22905e1aa7676c51078f75b1bd73d8383a4a Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:46:53 +1000 Subject: [PATCH 250/352] unix: Implement BLE H4 HCI UART for btstack/nimble. This commit adds support for using Bluetooth on the unix port via a H4 serial interface (distinct from a USB dongle), with both BTstack and NimBLE Bluetooth stacks. Note that MICROPY_PY_BLUETOOTH is now disabled for the coverage variant. Prior to this commit Bluetooth was anyway not being built on Travis because libusb was not detected. But now that bluetooth works in H4 mode it will be built, and will lead to a large decrease in coverage because Bluetooth tests cannot be run on Travis. --- extmod/btstack/btstack.mk | 8 + ports/unix/Makefile | 45 +++- ports/unix/mpbthciport.c | 219 ++++++++++++++++++ ports/unix/mpbtstackport.h | 5 + ports/unix/mpbtstackport_common.c | 7 + ports/unix/mpbtstackport_h4.c | 80 +++++++ ports/unix/mpconfigport.h | 7 +- ports/unix/mpnimbleport.c | 84 +++++++ ports/unix/mpnimbleport.h | 33 +++ .../unix/variants/coverage/mpconfigvariant.mk | 1 - ports/unix/variants/dev/mpconfigvariant.mk | 3 +- 11 files changed, 484 insertions(+), 8 deletions(-) create mode 100644 ports/unix/mpbthciport.c create mode 100644 ports/unix/mpbtstackport_h4.c create mode 100644 ports/unix/mpnimbleport.c create mode 100644 ports/unix/mpnimbleport.h diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index 3084601b85415..7e5d2f646d14c 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -30,10 +30,18 @@ SRC_BTSTACK = \ $(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \ lib/btstack/platform/embedded/btstack_run_loop_embedded.c +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) + $(error Cannot specifiy both MICROPY_BLUETOOTH_BTSTACK_USB and MICROPY_BLUETOOTH_BTSTACK_H4) +endif +endif + ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) SRC_BTSTACK += \ lib/btstack/platform/libusb/hci_transport_h2_libusb.c +CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK_USB=1 + CFLAGS += $(shell pkg-config libusb-1.0 --cflags) LDFLAGS += $(shell pkg-config libusb-1.0 --libs) endif diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 090b58d8ec4e2..ff5f355022bc5 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -134,24 +134,57 @@ CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 LDFLAGS_MOD += $(LIBPTHREAD) endif -# If the variant enables it and we have libusb, enable BTStack support for USB adaptors. +# If the variant enables it, enable modbluetooth. ifeq ($(MICROPY_PY_BLUETOOTH),1) HAVE_LIBUSB := $(shell (which pkg-config > /dev/null && pkg-config --exists libusb-1.0) 2>/dev/null && echo '1') -ifeq ($(HAVE_LIBUSB),1) + +# Only one stack can be enabled. +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +$(error Cannot enable both NimBLE and BTstack at the same time) +endif +endif + +# Default to btstack, but a variant (or make command line) can set NimBLE +# explicitly (which is always via H4 UART). +ifneq ($(MICROPY_BLUETOOTH_NIMBLE),1) +ifneq ($(MICROPY_BLUETOOTH_BTSTACK),1) +MICROPY_BLUETOOTH_BTSTACK ?= 1 +endif +endif CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 # Runs in a thread, cannot make calls into the VM. CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK=0 -MICROPY_BLUETOOTH_BTSTACK ?= 1 +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) + +# Figure out which BTstack transport to use. +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) +$(error Cannot enable BTstack support for USB and H4 UART at the same time) +endif +else +ifeq ($(HAVE_LIBUSB),1) +# Default to btstack-over-usb. MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 +else +# Fallback to HCI controller via a H4 UART (e.g. Zephyr on nRF) over a /dev/tty serial port. +MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 +endif +endif -ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +# BTstack is enabled. GIT_SUBMODULES += lib/btstack include $(TOP)/extmod/btstack/btstack.mk -endif + +else + +# NimBLE is enabled. +GIT_SUBMODULES += lib/mynewt-nimble +include $(TOP)/extmod/nimble/nimble.mk endif @@ -201,7 +234,9 @@ SRC_C = \ alloc.c \ coverage.c \ fatfs_port.c \ + mpbthciport.c \ mpbtstackport_common.c \ + mpbtstackport_h4.c \ mpbtstackport_usb.c \ mpnimbleport.c \ $(SRC_MOD) \ diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c new file mode 100644 index 0000000000000..5a5c1d4a4e79d --- /dev/null +++ b/ports/unix/mpbthciport.c @@ -0,0 +1,219 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && (MICROPY_BLUETOOTH_NIMBLE || (MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4)) + +#if !MICROPY_PY_THREAD +#error Unix HCI UART requires MICROPY_PY_THREAD +#endif + +#include "extmod/modbluetooth.h" +#include "extmod/mpbthci.h" + +#include +#include + +#include +#include +#include +#include + +#define DEBUG_printf(...) // printf(__VA_ARGS__) +#define DEBUG_HCI_DUMP (0) + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +extern bool mp_bluetooth_hci_poll(void); + +STATIC const useconds_t UART_POLL_INTERVAL_US = 1000; + +STATIC int uart_fd = -1; +STATIC pthread_t hci_poll_thread_id; + +STATIC void *hci_poll_thread(void *arg) { + (void)arg; + + // This will return false when the stack is shutdown. + while (mp_bluetooth_hci_poll()) { + usleep(UART_POLL_INTERVAL_US); + } + + return NULL; +} + +STATIC int configure_uart(void) { + struct termios toptions; + + // Get existing config. + if (tcgetattr(uart_fd, &toptions) < 0) { + DEBUG_printf("Couldn't get term attributes"); + return -1; + } + + // Raw mode (disable all processing). + cfmakeraw(&toptions); + + // 8N1, no parity. + toptions.c_cflag &= ~CSTOPB; + toptions.c_cflag |= CS8; + toptions.c_cflag &= ~PARENB; + + // Enable receiver, ignore modem control lines + toptions.c_cflag |= CREAD | CLOCAL; + + // Blocking, single-byte reads. + toptions.c_cc[VMIN] = 1; + toptions.c_cc[VTIME] = 0; + + // Enable HW RTS/CTS flow control. + toptions.c_iflag &= ~(IXON | IXOFF | IXANY); + toptions.c_cflag |= CRTSCTS; + + // 1Mbit (TODO: make this configurable). + speed_t brate = B1000000; + cfsetospeed(&toptions, brate); + cfsetispeed(&toptions, brate); + + // Apply immediately. + if (tcsetattr(uart_fd, TCSANOW, &toptions) < 0) { + DEBUG_printf("Couldn't set term attributes"); + return -1; + } + + return 0; +} + +// HCI UART bindings. +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + (void)port; + (void)baudrate; + + DEBUG_printf("mp_bluetooth_hci_uart_init (unix)\n"); + + char uart_device_name[256] = "/dev/ttyUSB0"; + + char *path = getenv("MICROPYBTUART"); + if (path != NULL) { + strcpy(uart_device_name, path); + } + DEBUG_printf("Using HCI UART: %s\n", uart_device_name); + + int flags = O_RDWR | O_NOCTTY | O_NONBLOCK; + uart_fd = open(uart_device_name, flags); + if (uart_fd == -1) { + DEBUG_printf("Unable to open port %s", uart_device_name); + return -1; + } + + if (configure_uart()) { + return -1; + } + + // Create a thread to run the polling loop. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&hci_poll_thread_id, &attr, &hci_poll_thread, NULL); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_uart_deinit\n"); + + // Wait for the poll loop to terminate when the state is set to OFF. + pthread_join(hci_poll_thread_id, NULL); + + // Close the UART. + close(uart_fd); + uart_fd = -1; + + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + (void)baudrate; + DEBUG_printf("mp_bluetooth_hci_uart_set_baudrate\n"); + return 0; +} + +int mp_bluetooth_hci_uart_readchar(void) { + // DEBUG_printf("mp_bluetooth_hci_uart_readchar\n"); + + uint8_t c; + ssize_t bytes_read = read(uart_fd, &c, 1); + + if (bytes_read == 1) { + #if DEBUG_HCI_DUMP + printf("[% 8ld] RX: %02x\n", mp_hal_ticks_ms(), c); + #endif + return c; + } else { + return -1; + } +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + // DEBUG_printf("mp_bluetooth_hci_uart_write\n"); + + #if DEBUG_HCI_DUMP + printf("[% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]); + for (size_t i = 1; i < len; ++i) { + printf(":%02x", buf[i]); + } + printf("\n"); + #endif + + return write(uart_fd, buf, len); +} + +// No-op implementations of HCI controller interface. +int mp_bluetooth_hci_controller_init(void) { + return 0; +} + +int mp_bluetooth_hci_controller_deinit(void) { + return 0; +} + +int mp_bluetooth_hci_controller_sleep_maybe(void) { + return 0; +} + +bool mp_bluetooth_hci_controller_woken(void) { + return true; +} + +int mp_bluetooth_hci_controller_wakeup(void) { + return 0; +} + +#endif // MICROPY_PY_BLUETOOTH && (MICROPY_BLUETOOTH_NIMBLE || (MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4)) diff --git a/ports/unix/mpbtstackport.h b/ports/unix/mpbtstackport.h index 29433cc7fb006..c82e8bd812d7a 100644 --- a/ports/unix/mpbtstackport.h +++ b/ports/unix/mpbtstackport.h @@ -32,6 +32,11 @@ bool mp_bluetooth_hci_poll(void); +#if MICROPY_BLUETOOTH_BTSTACK_H4 +void mp_bluetooth_hci_poll_h4(void); +void mp_bluetooth_btstack_port_init_h4(void); +#endif + #if MICROPY_BLUETOOTH_BTSTACK_USB void mp_bluetooth_btstack_port_init_usb(void); #endif diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index 402cf95283441..eb1b1c9c8042b 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -45,6 +45,9 @@ bool mp_bluetooth_hci_poll(void) { if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_HALTING) { // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + #if MICROPY_BLUETOOTH_BTSTACK_H4 + mp_bluetooth_hci_poll_h4(); + #endif btstack_run_loop_embedded_execute_once(); MICROPY_END_ATOMIC_SECTION(atomic_state); @@ -81,6 +84,10 @@ void mp_bluetooth_btstack_port_init(void) { // hci_dump_open(NULL, HCI_DUMP_STDOUT); + #if MICROPY_BLUETOOTH_BTSTACK_H4 + mp_bluetooth_btstack_port_init_h4(); + #endif + #if MICROPY_BLUETOOTH_BTSTACK_USB mp_bluetooth_btstack_port_init_usb(); #endif diff --git a/ports/unix/mpbtstackport_h4.c b/ports/unix/mpbtstackport_h4.c new file mode 100644 index 0000000000000..4fdc20c22b69b --- /dev/null +++ b/ports/unix/mpbtstackport_h4.c @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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 "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4 + +#include "lib/btstack/chipset/zephyr/btstack_chipset_zephyr.h" + +#include "extmod/btstack/btstack_hci_uart.h" +#include "extmod/btstack/modbluetooth_btstack.h" + +#include "mpbtstackport.h" + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +STATIC hci_transport_config_uart_t hci_transport_config_uart = { + HCI_TRANSPORT_CONFIG_UART, + 1000000, // initial baudrate + 0, // main baudrate + 1, // flow control + NULL, // device name +}; + +void mp_bluetooth_hci_poll_h4(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { + mp_bluetooth_btstack_hci_uart_process(); + } +} + +void mp_bluetooth_btstack_port_init_h4(void) { + DEBUG_printf("mp_bluetooth_btstack_port_init_h4\n"); + + const hci_transport_t *transport = hci_transport_h4_instance(&mp_bluetooth_btstack_hci_uart_block); + hci_init(transport, &hci_transport_config_uart); + + hci_set_chipset(btstack_chipset_zephyr_instance()); +} + +void mp_bluetooth_btstack_port_deinit(void) { + DEBUG_printf("mp_bluetooth_btstack_port_deinit\n"); + + hci_power_control(HCI_POWER_OFF); + hci_close(); +} + +void mp_bluetooth_btstack_port_start(void) { + DEBUG_printf("mp_bluetooth_btstack_port_start\n"); + + hci_power_control(HCI_POWER_ON); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4 diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 9e24ab10cd263..c74d2fd84aa5f 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -314,6 +314,11 @@ void mp_unix_mark_exec(void); struct _mp_bluetooth_btstack_root_pointers_t; #define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers; #endif +#if MICROPY_BLUETOOTH_NIMBLE +struct _mp_bluetooth_nimble_root_pointers_t; +struct _mp_bluetooth_nimble_malloc_t; +#define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_nimble_malloc_t *bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; +#endif #else #define MICROPY_BLUETOOTH_ROOT_POINTERS #endif @@ -353,7 +358,7 @@ struct _mp_bluetooth_btstack_root_pointers_t; #endif #if MICROPY_PY_THREAD -#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0) +#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0xffffffff) #define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() #endif diff --git a/ports/unix/mpnimbleport.c b/ports/unix/mpnimbleport.c new file mode 100644 index 0000000000000..8961910098c42 --- /dev/null +++ b/ports/unix/mpnimbleport.c @@ -0,0 +1,84 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#include "nimble/nimble_npl.h" + +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/hal/hal_uart.h" + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +// Called by the UART polling thread in mpbthciport.c. +bool mp_bluetooth_hci_poll(void) { + // DEBUG_printf("mp_bluetooth_hci_poll (unix nimble) %d\n", mp_bluetooth_nimble_ble_state); + + if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + DEBUG_printf("mp_bluetooth_hci_poll (unix nimble) -- shutdown\n"); + return false; + } + + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + + // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + + // Ask NimBLE to process UART data. + mp_bluetooth_nimble_hci_uart_process(); + + // Run pending background operations and events, but only after HCI sync. + mp_bluetooth_nimble_os_callout_process(); + mp_bluetooth_nimble_os_eventq_run_all(); + + MICROPY_END_ATOMIC_SECTION(atomic_state); + } + + return true; +} + +// Extra port-specific helpers. +void mp_bluetooth_nimble_hci_uart_wfi(void) { + // DEBUG_printf("mp_bluetooth_nimble_hci_uart_wfi\n"); + // TODO: this should do a select() on the uart_fd. +} + +uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void) { + // DEBUG_printf("mp_bluetooth_nimble_hci_uart_enter_critical\n"); + MICROPY_PY_BLUETOOTH_ENTER + return atomic_state; // Always 0xffffffff +} + +void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state) { + MICROPY_PY_BLUETOOTH_EXIT + // DEBUG_printf("mp_bluetooth_nimble_hci_uart_exit_critical\n"); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/unix/mpnimbleport.h b/ports/unix/mpnimbleport.h new file mode 100644 index 0000000000000..a2935e6fdf44a --- /dev/null +++ b/ports/unix/mpnimbleport.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_UNIX_NIMBLE_PORT_H +#define MICROPY_INCLUDED_UNIX_NIMBLE_PORT_H + +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (1000000) + +#endif // MICROPY_INCLUDED_UNIX_NIMBLE_PORT_H diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 66e694e0a9ea0..f11d0b0d28f75 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -17,4 +17,3 @@ MICROPY_ROM_TEXT_COMPRESSION = 1 MICROPY_VFS_FAT = 1 MICROPY_VFS_LFS1 = 1 MICROPY_VFS_LFS2 = 1 -MICROPY_PY_BLUETOOTH = 1 diff --git a/ports/unix/variants/dev/mpconfigvariant.mk b/ports/unix/variants/dev/mpconfigvariant.mk index 1f8611b6fc30b..91bd28da9bd85 100644 --- a/ports/unix/variants/dev/mpconfigvariant.mk +++ b/ports/unix/variants/dev/mpconfigvariant.mk @@ -6,4 +6,5 @@ MICROPY_ROM_TEXT_COMPRESSION = 1 MICROPY_VFS_FAT = 1 MICROPY_VFS_LFS1 = 1 MICROPY_VFS_LFS2 = 1 -MICROPY_PY_BLUETOOTH = 1 + +MICROPY_PY_BLUETOOTH ?= 1 From c4af714d58213bb23b81f3736f785d897e60ac77 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:47:21 +1000 Subject: [PATCH 251/352] extmod/modbluetooth: Implement configuration of address modes. Changes `BLE.config('mac')` to return a tuple (addr_mode, addr). Adds `BLE.config(addr_mode=...)` to set the addressing mode. --- extmod/btstack/modbluetooth_btstack.c | 122 ++++++++++++++++++++++++- extmod/modbluetooth.c | 11 ++- extmod/modbluetooth.h | 21 ++--- extmod/nimble/modbluetooth_nimble.c | 127 +++++++++++++++++--------- ports/unix/mpbtstackport_common.c | 3 - 5 files changed, 222 insertions(+), 62 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 03ec12b734346..b028a5d1ac94c 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -271,6 +271,14 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan } } +#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS +// During startup, the controller (e.g. Zephyr) might give us a static address that we can use. +STATIC uint8_t controller_static_addr[6] = {0}; +STATIC bool controller_static_addr_available = false; + +STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; +#endif + STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { DEBUG_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { @@ -313,6 +321,13 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { DEBUG_printf(" --> hci command complete\n"); + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { + DEBUG_printf(" --> static address available\n"); + reverse_48(&packet[7], controller_static_addr); + controller_static_addr_available = true; + } + #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS } else if (event_type == HCI_EVENT_COMMAND_STATUS) { DEBUG_printf(" --> hci command status\n"); } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { @@ -493,6 +508,60 @@ STATIC void btstack_init_deinit_timeout_handler(btstack_timer_source_t *ds) { mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; } +STATIC void btstack_static_address_ready(void *arg) { + DEBUG_printf("btstack_static_address_ready.\n"); + *(volatile bool *)arg = true; +} + +STATIC bool set_public_address(void) { + bd_addr_t local_addr; + gap_local_bd_addr(local_addr); + bd_addr_t null_addr = {0}; + if (memcmp(local_addr, null_addr, 6) == 0) { + DEBUG_printf("set_public_address: No public address available.\n"); + return false; + } + DEBUG_printf("set_public_address: Using controller's public address.\n"); + gap_random_address_set_mode(GAP_RANDOM_ADDRESS_TYPE_OFF); + return true; +} + +STATIC void set_random_address(void) { + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + if (controller_static_addr_available) { + DEBUG_printf("set_random_address: Using static address supplied by controller.\n"); + gap_random_address_set(controller_static_addr); + } else + #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + { + DEBUG_printf("set_random_address: Generating random static address.\n"); + btstack_crypto_random_t sm_crypto_random_request; + bd_addr_t static_addr; + volatile bool ready = false; + btstack_crypto_random_generate(&sm_crypto_random_request, static_addr, 6, &btstack_static_address_ready, (void *)&ready); + while (!ready) { + MICROPY_EVENT_POLL_HOOK + } + DEBUG_printf("set_random_address: Address generated.\n"); + gap_random_address_set(static_addr); + } + + // Wait for the controller to accept this address. + while (true) { + uint8_t addr_type; + bd_addr_t addr; + gap_le_get_own_address(&addr_type, addr); + + bd_addr_t null_addr = {0}; + if (memcmp(addr, null_addr, 6) != 0) { + break; + } + + MICROPY_EVENT_POLL_HOOK + } + DEBUG_printf("set_random_address: Address loaded by controller\n"); +} + int mp_bluetooth_init(void) { DEBUG_printf("mp_bluetooth_init\n"); @@ -505,6 +574,10 @@ int mp_bluetooth_init(void) { btstack_memory_init(); + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + controller_static_addr_available = false; + #endif + MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db); @@ -563,9 +636,23 @@ int mp_bluetooth_init(void) { // Clean up. MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; + return MP_ETIMEDOUT; } + DEBUG_printf("mp_bluetooth_init: stack startup complete\n"); + + // At this point if the controller has its own public address, btstack will know this. + // However, if this is not available, then attempt to get a static address: + // - For a Zephyr controller on nRF, a static address will be available during startup. + // - Otherwise we ask the controller to generate a static address for us. + // In either case, calling gap_random_address_set will set the mode to STATIC, and then + // immediately set the address on the controller. We then wait until this address becomes available. + + if (!set_public_address()) { + set_random_address(); + } + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); @@ -612,8 +699,39 @@ bool mp_bluetooth_is_active(void) { return mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; } -void mp_bluetooth_get_device_addr(uint8_t *addr) { - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + + DEBUG_printf("mp_bluetooth_get_current_address\n"); + gap_le_get_own_address(addr_type, addr); +} + +void mp_bluetooth_set_address_mode(uint8_t addr_mode) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + + switch (addr_mode) { + case MP_BLUETOOTH_ADDRESS_MODE_PUBLIC: { + DEBUG_printf("mp_bluetooth_set_address_mode: public\n"); + if (!set_public_address()) { + // No public address available. + mp_raise_OSError(MP_EINVAL); + } + break; + } + case MP_BLUETOOTH_ADDRESS_MODE_RANDOM: { + DEBUG_printf("mp_bluetooth_set_address_mode: random\n"); + set_random_address(); + break; + } + case MP_BLUETOOTH_ADDRESS_MODE_RPA: + case MP_BLUETOOTH_ADDRESS_MODE_NRPA: + // Not yet supported. + mp_raise_OSError(MP_EINVAL); + } } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 82efe49385377..a8a762ce32f24 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -308,9 +308,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map return mp_obj_new_bytes(buf, len); } case MP_QSTR_mac: { + uint8_t addr_type; uint8_t addr[6]; - mp_bluetooth_get_device_addr(addr); - return mp_obj_new_bytes(addr, MP_ARRAY_SIZE(addr)); + mp_bluetooth_get_current_address(&addr_type, addr); + mp_obj_t items[] = { MP_OBJ_NEW_SMALL_INT(addr_type), mp_obj_new_bytes(addr, MP_ARRAY_SIZE(addr)) }; + return mp_obj_new_tuple(2, items); } case MP_QSTR_rxbuf: return mp_obj_new_int(self->ringbuf.size); @@ -366,6 +368,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map m_del(uint8_t, old_irq_data_buf, old_irq_data_alloc); break; } + case MP_QSTR_addr_mode: { + mp_int_t addr_mode = mp_obj_get_int(e->value); + mp_bluetooth_set_address_mode(addr_mode); + break; + } default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 2c07683124232..23ff97bc61806 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -79,15 +79,6 @@ #define MP_BLUETOOTH_UUID_TYPE_32 (4) #define MP_BLUETOOTH_UUID_TYPE_128 (16) -// Address types (for the addr_type params). -// Ports will need to map these to their own values. -#define MP_BLUETOOTH_ADDR_PUBLIC (0x00) // Public (identity) address. (Same as NimBLE and NRF SD) -#define MP_BLUETOOTH_ADDR_RANDOM_STATIC (0x01) // Random static (identity) address. (Same as NimBLE and NRF SD) -#define MP_BLUETOOTH_ADDR_PUBLIC_ID (0x02) // (Same as NimBLE) -#define MP_BLUETOOTH_ADDR_RANDOM_ID (0x03) // (Same as NimBLE) -#define MP_BLUETOOTH_ADDR_RANDOM_PRIVATE_RESOLVABLE (0x12) // Random private resolvable address. (NRF SD 0x02) -#define MP_BLUETOOTH_ADDR_RANDOM_PRIVATE_NON_RESOLVABLE (0x13) // Random private non-resolvable address. (NRF SD 0x03) - // Event codes for the IRQ handler. #define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1) #define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (2) @@ -110,6 +101,11 @@ #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) #define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) +#define MP_BLUETOOTH_ADDRESS_MODE_PUBLIC (0) +#define MP_BLUETOOTH_ADDRESS_MODE_RANDOM (1) +#define MP_BLUETOOTH_ADDRESS_MODE_RPA (2) +#define MP_BLUETOOTH_ADDRESS_MODE_NRPA (3) + /* These aren't included in the module for space reasons, but can be used in your Python code if necessary. @@ -173,8 +169,11 @@ void mp_bluetooth_deinit(void); // Returns true when the Bluetooth stack is active. bool mp_bluetooth_is_active(void); -// Gets the MAC addr of this device in big-endian format. -void mp_bluetooth_get_device_addr(uint8_t *addr); +// Gets the current address of this device in big-endian format. +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr); + +// Sets the addressing mode to use. +void mp_bluetooth_set_address_mode(uint8_t addr_mode); // Get or set the GAP device name that will be used by service 0x1800, characteristic 0x2a00. size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 61ba3a3aba696..1c782943ab485 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -50,6 +50,8 @@ #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV +STATIC uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM; + // Any BLE_HS_xxx code not in this table will default to MP_EIO. STATIC int8_t ble_hs_err_to_errno_table[] = { [BLE_HS_EAGAIN] = MP_EAGAIN, @@ -148,9 +150,26 @@ STATIC void reset_cb(int reason) { (void)reason; } -STATIC void sync_cb(void) { +STATIC bool has_public_address(void) { + return ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, NULL, NULL) == 0; +} + +STATIC void set_random_address(bool nrpa) { int rc; + (void)rc; + DEBUG_printf("sync_cb: Generating random static address\n"); ble_addr_t addr; + rc = ble_hs_id_gen_rnd(nrpa ? 1 : 0, &addr); + assert(rc == 0); + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); +} + +STATIC void sync_cb(void) { + int rc; + (void)rc; DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state); @@ -158,25 +177,11 @@ STATIC void sync_cb(void) { return; } - rc = ble_hs_util_ensure_addr(0); // prefer public address - if (rc != 0) { - // https://mynewt.apache.org/latest/tutorials/ble/eddystone.html#configure-the-nimble-stack-with-an-address - #if MICROPY_PY_BLUETOOTH_RANDOM_ADDR - rc = ble_hs_id_gen_rnd(1, &addr); - assert(rc == 0); - rc = ble_hs_id_set_rnd(addr.val); - assert(rc == 0); - #else - uint8_t addr_be[6]; - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr_be); - reverse_addr_byte_order(addr.val, addr_be); - // ble_hs_id_set_pub(addr.val); - rc = ble_hs_id_set_rnd(addr.val); - assert(rc == 0); - #endif - - rc = ble_hs_util_ensure_addr(0); // prefer public address - assert(rc == 0); + if (has_public_address()) { + nimble_address_mode = BLE_OWN_ADDR_PUBLIC; + } else { + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + set_random_address(false); } if (MP_BLUETOOTH_DEFAULT_ATTR_LEN > 20) { @@ -412,19 +417,65 @@ bool mp_bluetooth_is_active(void) { return mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE; } -void mp_bluetooth_get_device_addr(uint8_t *addr) { - #if MICROPY_PY_BLUETOOTH_RANDOM_ADDR +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + uint8_t addr_le[6]; - int rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr_le, NULL); + + switch (nimble_address_mode) { + case BLE_OWN_ADDR_PUBLIC: + *addr_type = BLE_ADDR_PUBLIC; + break; + case BLE_OWN_ADDR_RANDOM: + *addr_type = BLE_ADDR_RANDOM; + break; + case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: + case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT: + default: + // TODO: If RPA/NRPA in use, get the current value. + // Is this even possible in NimBLE? + mp_raise_OSError(MP_EINVAL); + } + + int rc = ble_hs_id_copy_addr(*addr_type, addr_le, NULL); if (rc != 0) { - // Even with MICROPY_PY_BLUETOOTH_RANDOM_ADDR enabled the public address may - // be used instead, in which case there is no random address. - ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr_le, NULL); + mp_raise_OSError(MP_EINVAL); } reverse_addr_byte_order(addr, addr_le); - #else - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); - #endif +} + +void mp_bluetooth_set_address_mode(uint8_t addr_mode) { + switch (addr_mode) { + case MP_BLUETOOTH_ADDRESS_MODE_PUBLIC: + if (!has_public_address()) { + // No public address available. + mp_raise_OSError(MP_EINVAL); + } + nimble_address_mode = BLE_OWN_ADDR_PUBLIC; + break; + case MP_BLUETOOTH_ADDRESS_MODE_RANDOM: + // Generate an static random address. + set_random_address(false); + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + break; + case MP_BLUETOOTH_ADDRESS_MODE_RPA: + if (has_public_address()) { + nimble_address_mode = BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT; + } else { + // Generate an static random address to use as the identity address. + set_random_address(false); + nimble_address_mode = BLE_OWN_ADDR_RPA_RANDOM_DEFAULT; + } + break; + case MP_BLUETOOTH_ADDRESS_MODE_NRPA: + // Generate an NRPA. + set_random_address(true); + // In NimBLE, NRPA is treated like a static random address that happens to be an NRPA. + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + break; + } } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { @@ -473,19 +524,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons .channel_map = 7, // all 3 channels. }; - ret = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RPA_RANDOM_DEFAULT, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); + ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); if (ret == 0) { return 0; } @@ -756,7 +795,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ .passive = active_scan ? 0 : 1, .filter_duplicates = 0, }; - int err = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, duration_ms, &discover_params, gap_scan_cb, NULL); + int err = ble_gap_disc(nimble_address_mode, duration_ms, &discover_params, gap_scan_cb, NULL); return ble_hs_err_to_errno(err); } @@ -847,7 +886,7 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, }; ble_addr_t addr_nimble = create_nimble_addr(addr_type, addr); - int err = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &addr_nimble, duration_ms, ¶ms, &peripheral_gap_event_cb, NULL); + int err = ble_gap_connect(nimble_address_mode, &addr_nimble, duration_ms, ¶ms, &peripheral_gap_event_cb, NULL); return ble_hs_err_to_errno(err); } diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index eb1b1c9c8042b..621e661f9eba3 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -93,7 +93,4 @@ void mp_bluetooth_btstack_port_init(void) { #endif } -void mp_hal_get_mac(int idx, uint8_t buf[6]) { -} - #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK From 26b66804e96b78395cddc37e195fc36862837d97 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:16:29 +1000 Subject: [PATCH 252/352] tests/multi_bluetooth: Update to new config('mac') behaviour. --- tests/multi_bluetooth/ble_characteristic.py | 2 +- tests/multi_bluetooth/ble_gap_advertise.py | 2 +- tests/multi_bluetooth/ble_gap_connect.py | 4 ++-- tests/multi_bluetooth/ble_gap_device_name.py | 2 +- tests/multi_bluetooth/ble_gatt_data_transfer.py | 2 +- tests/multi_bluetooth/ble_gattc_discover_services.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/multi_bluetooth/ble_characteristic.py b/tests/multi_bluetooth/ble_characteristic.py index 0d72c181fdec6..d918d9aefcf03 100644 --- a/tests/multi_bluetooth/ble_characteristic.py +++ b/tests/multi_bluetooth/ble_characteristic.py @@ -146,7 +146,7 @@ def instance1(): try: # Connect to peripheral and then disconnect. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return conn_handle, _, _ = waiting_data diff --git a/tests/multi_bluetooth/ble_gap_advertise.py b/tests/multi_bluetooth/ble_gap_advertise.py index 71a5b58a56f7a..bb6ff8e425461 100644 --- a/tests/multi_bluetooth/ble_gap_advertise.py +++ b/tests/multi_bluetooth/ble_gap_advertise.py @@ -36,7 +36,7 @@ def instance1(): def irq(ev, data): nonlocal finished, adv_types, adv_data if ev == _IRQ_SCAN_RESULT: - if data[1] == BDADDR: + if data[0] == BDADDR[0] and data[1] == BDADDR[1]: adv_types.add(data[2]) if adv_data is None: adv_data = bytes(data[4]) diff --git a/tests/multi_bluetooth/ble_gap_connect.py b/tests/multi_bluetooth/ble_gap_connect.py index 8e3ed8b816b3a..ba9c28230d7da 100644 --- a/tests/multi_bluetooth/ble_gap_connect.py +++ b/tests/multi_bluetooth/ble_gap_connect.py @@ -76,7 +76,7 @@ def instance1(): try: # Connect to peripheral and then disconnect. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return print("gap_disconnect:", ble.gap_disconnect(waiting_data[0])) @@ -88,7 +88,7 @@ def instance1(): # Connect to peripheral and then let the peripheral disconnect us. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) diff --git a/tests/multi_bluetooth/ble_gap_device_name.py b/tests/multi_bluetooth/ble_gap_device_name.py index dafa367128b49..92ea94278a647 100644 --- a/tests/multi_bluetooth/ble_gap_device_name.py +++ b/tests/multi_bluetooth/ble_gap_device_name.py @@ -97,7 +97,7 @@ def instance1(): # Connect to peripheral. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return conn_handle, _, _ = waiting_data diff --git a/tests/multi_bluetooth/ble_gatt_data_transfer.py b/tests/multi_bluetooth/ble_gatt_data_transfer.py index dba0c333be672..240f048607c92 100644 --- a/tests/multi_bluetooth/ble_gatt_data_transfer.py +++ b/tests/multi_bluetooth/ble_gatt_data_transfer.py @@ -137,7 +137,7 @@ def instance1(): try: # Connect to peripheral and then disconnect. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return conn_handle, _, _ = waiting_data diff --git a/tests/multi_bluetooth/ble_gattc_discover_services.py b/tests/multi_bluetooth/ble_gattc_discover_services.py index e746b87458e1b..57f95bf0125e7 100644 --- a/tests/multi_bluetooth/ble_gattc_discover_services.py +++ b/tests/multi_bluetooth/ble_gattc_discover_services.py @@ -85,7 +85,7 @@ def instance1(): try: # Connect to peripheral and then disconnect. print("gap_connect") - ble.gap_connect(0, BDADDR) + ble.gap_connect(*BDADDR) if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): return conn_handle, _, _ = waiting_data From 67d8139e2b4cbaba5c722feef4e5478ae647344d Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 14 Aug 2020 15:17:14 +1000 Subject: [PATCH 253/352] docs/library/ubluetooth.rst: Document BLE address modes. --- docs/library/ubluetooth.rst | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index 0cac16f5f4ad9..f8dad7b8d1ed2 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -45,12 +45,22 @@ Configuration Currently supported values are: - - ``'mac'``: Returns the device MAC address. If a device has a fixed address - (e.g. PYBD) then it will be returned. Otherwise (e.g. ESP32) a random - address will be generated when the BLE interface is made active. + - ``'mac'``: The current address in use, depending on the current address mode. + This returns a tuple of ``(addr_type, addr)``. - **Note:** on some ports, accessing this value requires that the interface is - active (so that the MAC address can be queried from the controller). + See :meth:`gatts_write ` for details about address type. + + This may only be queried while the interface is currently active. + + - ``'addr_mode'``: Sets the address mode. Values can be: + + * 0x00 - PUBLIC - Use the controller's public address. + * 0x01 - RANDOM - Use a generated static address. + * 0x02 - RPA - Use resolvable private addresses. + * 0x03 - NRPA - Use non-resolvable private addresses. + + By default the interface mode will use a PUBLIC address if available, otherwise + it will use a RANDOM address. - ``'gap_name'``: Get/set the GAP device name used by service 0x1800, characteristic 0x2a00. This can be set at any time and changed multiple @@ -219,8 +229,13 @@ Observer Role (Scanner) (background scanning). For each scan result the ``_IRQ_SCAN_RESULT`` event will be raised, with event - data ``(addr_type, addr, adv_type, rssi, adv_data)``. ``adv_type`` values correspond - to the Bluetooth Specification: + data ``(addr_type, addr, adv_type, rssi, adv_data)``. + + ``addr_type`` values indicate public or random addresses: + * 0x00 - PUBLIC + * 0x01 - RANDOM (either static, RPA, or NRPA, the type is encoded in the address itself) + + ``adv_type`` values correspond to the Bluetooth Specification: * 0x00 - ADV_IND - connectable and scannable undirected advertising * 0x01 - ADV_DIRECT_IND - connectable directed advertising @@ -342,6 +357,8 @@ Central Role (GATT Client) Connect to a peripheral. + See :meth:`gatts_write ` for details about address types. + On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised. .. method:: BLE.gap_disconnect(conn_handle) From 52a2ce45de353267776aaa25856c9d144653f142 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 17 Aug 2020 10:53:00 +1000 Subject: [PATCH 254/352] extmod/modbluetooth: Allow using mp_hal_get_mac as a static address. Generally a controller should either have its own public address hardcoded, or loaded by the driver (e.g. cywbt43). However, for a controller that has no public address where you still want a long-term stable address, this allows you to use a static address generated by the port. Typically on STM32 this will be an LAA, but a board might override this. --- extmod/btstack/modbluetooth_btstack.c | 17 ++++++++++++++++- extmod/nimble/modbluetooth_nimble.c | 18 +++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index b028a5d1ac94c..773234b9b2600 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -508,10 +508,12 @@ STATIC void btstack_init_deinit_timeout_handler(btstack_timer_source_t *ds) { mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; } +#if !MICROPY_BLUETOOTH_USE_MP_HAL_GET_MAC_STATIC_ADDRESS STATIC void btstack_static_address_ready(void *arg) { DEBUG_printf("btstack_static_address_ready.\n"); *(volatile bool *)arg = true; } +#endif STATIC bool set_public_address(void) { bd_addr_t local_addr; @@ -534,14 +536,27 @@ STATIC void set_random_address(void) { } else #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS { + bd_addr_t static_addr; + + #if MICROPY_BLUETOOTH_USE_MP_HAL_GET_MAC_STATIC_ADDRESS + + DEBUG_printf("set_random_address: Generating static address using mp_hal_get_mac\n"); + mp_hal_get_mac(MP_HAL_MAC_BDADDR, static_addr); + // Mark it as STATIC (not RPA or NRPA). + static_addr[0] |= 0xc0; + + #else + DEBUG_printf("set_random_address: Generating random static address.\n"); btstack_crypto_random_t sm_crypto_random_request; - bd_addr_t static_addr; volatile bool ready = false; btstack_crypto_random_generate(&sm_crypto_random_request, static_addr, 6, &btstack_static_address_ready, (void *)&ready); while (!ready) { MICROPY_EVENT_POLL_HOOK } + + #endif // MICROPY_BLUETOOTH_USE_MP_HAL_GET_MAC_STATIC_ADDRESS + DEBUG_printf("set_random_address: Address generated.\n"); gap_random_address_set(static_addr); } diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 1c782943ab485..4222f58fa8812 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -157,10 +157,22 @@ STATIC bool has_public_address(void) { STATIC void set_random_address(bool nrpa) { int rc; (void)rc; - DEBUG_printf("sync_cb: Generating random static address\n"); ble_addr_t addr; - rc = ble_hs_id_gen_rnd(nrpa ? 1 : 0, &addr); - assert(rc == 0); + #if MICROPY_BLUETOOTH_USE_MP_HAL_GET_MAC_STATIC_ADDRESS + if (!nrpa) { + DEBUG_printf("set_random_address: Generating static address using mp_hal_get_mac\n"); + uint8_t hal_mac_addr[6]; + mp_hal_get_mac(MP_HAL_MAC_BDADDR, hal_mac_addr); + addr = create_nimble_addr(BLE_ADDR_RANDOM, hal_mac_addr); + // Mark it as STATIC (not RPA or NRPA). + addr.val[5] |= 0xc0; + } else + #endif + { + DEBUG_printf("set_random_address: Generating random static address\n"); + rc = ble_hs_id_gen_rnd(nrpa ? 1 : 0, &addr); + assert(rc == 0); + } rc = ble_hs_id_set_rnd(addr.val); assert(rc == 0); rc = ble_hs_util_ensure_addr(1); From 8b00aeab8fd20f8b8920cf322b5cfb7021ef1504 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 15 Aug 2020 14:06:19 +1000 Subject: [PATCH 255/352] extmod/btstack: Add btstack support for _IRQ_GATTS_READ_REQUEST. --- extmod/btstack/modbluetooth_btstack.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 773234b9b2600..2e1bd47cfa462 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -825,24 +825,37 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { } STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { + // Should return data length, 0 for error, or -1 for delayed response. + // For more details search "*att_read_callback*" in micropython/lib/btstack/doc/manual/docs/profiles.md (void)connection_handle; - DEBUG_printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); + DEBUG_printf("att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); if (!entry) { - DEBUG_printf("btstack: att_read_callback handle not found\n"); - return 0; // TODO: Find status code for not-found. + DEBUG_printf("att_read_callback handle not found\n"); + return 0; + } + + #if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK + // Allow Python code to override value (by using gatts_write), or deny (by returning false) the read. + if ((buffer == NULL) && (buffer_size == 0)) { + if (!mp_bluetooth_gatts_on_read_request(connection_handle, att_handle)) { + DEBUG_printf("att_read_callback: read request denied\n"); + return 0; + } } + #endif - return att_read_callback_handle_blob(entry->data, entry->data_len, offset, buffer, buffer_size); + uint16_t ret = att_read_callback_handle_blob(entry->data, entry->data_len, offset, buffer, buffer_size); + return ret; } STATIC int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { (void)offset; (void)transaction_mode; - DEBUG_printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); + DEBUG_printf("att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); if (!entry) { - DEBUG_printf("btstack: att_write_callback handle not found\n"); + DEBUG_printf("att_write_callback handle not found\n"); return 0; // TODO: Find status code for not-found. } From 6077c63a450d7c2ab5e31e5845ecb5f1a4634b26 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sun, 16 Aug 2020 08:30:19 +1000 Subject: [PATCH 256/352] stm32/mpbthciport: Increase char timeout of BT HCI UART. The 2ms used previously was not long enough and it could lose HCI sync. Also print error on tx failure to make this more obvious in the future. --- ports/stm32/mpbthciport.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ports/stm32/mpbthciport.c b/ports/stm32/mpbthciport.c index 71a9e4fc7f433..a5977ff12c210 100644 --- a/ports/stm32/mpbthciport.c +++ b/ports/stm32/mpbthciport.c @@ -142,8 +142,9 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type; mp_bluetooth_hci_uart_obj.uart_id = port; mp_bluetooth_hci_uart_obj.is_static = true; - mp_bluetooth_hci_uart_obj.timeout = 2; - mp_bluetooth_hci_uart_obj.timeout_char = 2; + // We don't want to block indefinitely, but expect flow control is doing its job. + mp_bluetooth_hci_uart_obj.timeout = 200; + mp_bluetooth_hci_uart_obj.timeout_char = 200; MP_STATE_PORT(pyb_uart_obj_all)[mp_bluetooth_hci_uart_obj.uart_id - 1] = &mp_bluetooth_hci_uart_obj; // This also initialises the UART. @@ -184,7 +185,11 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { // DEBUG_printf("mp_bluetooth_hci_uart_write (stm32)\n"); mp_bluetooth_hci_controller_wakeup(); - uart_tx_strn(&mp_bluetooth_hci_uart_obj, (void *)buf, len); + int errcode; + uart_tx_data(&mp_bluetooth_hci_uart_obj, (void *)buf, len, &errcode); + if (errcode != 0) { + printf("\nmp_bluetooth_hci_uart_write: failed to write to UART %d\n", errcode); + } return 0; } From 99a29ec705b463290d5a2ac1eabc46fb7f2a83b0 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 18 Aug 2020 11:05:34 +1000 Subject: [PATCH 257/352] extmod/btstack: Detect HCI UART init failure. --- extmod/btstack/btstack_hci_uart.c | 14 +++++++++++--- extmod/btstack/modbluetooth_btstack.c | 7 ++++++- ports/unix/mpbthciport.c | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/extmod/btstack/btstack_hci_uart.c b/extmod/btstack/btstack_hci_uart.c index 5c96e02dc0fed..ae18628a9cf0e 100644 --- a/extmod/btstack/btstack_hci_uart.c +++ b/extmod/btstack/btstack_hci_uart.c @@ -50,6 +50,7 @@ STATIC uint8_t *recv_buf; STATIC size_t recv_len; STATIC size_t recv_idx; STATIC void (*recv_handler)(void); +STATIC bool init_success = false; STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) { (void)uart_config; @@ -62,14 +63,21 @@ STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) { // Set up the UART peripheral, attach IRQ and power up the HCI controller. // We haven't been told the baud rate yet, so defer that until btstack_uart_set_baudrate. - mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0); - mp_bluetooth_hci_controller_init(); + if (mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0)) { + init_success = false; + return -1; + } + if (mp_bluetooth_hci_controller_init()) { + init_success = false; + return -1; + } + init_success = true; return 0; } STATIC int btstack_uart_open(void) { - return 0; + return init_success ? 0 : 1; } STATIC int btstack_uart_close(void) { diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 2e1bd47cfa462..cde802a61f5f4 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -317,6 +317,9 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t // Signal that de-initialisation has completed. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; } + } else if (event_type == BTSTACK_EVENT_POWERON_FAILED) { + // Signal that initialisation has failed. + mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; } else if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT) { DEBUG_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { @@ -644,6 +647,8 @@ int mp_bluetooth_init(void) { if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { DEBUG_printf("mp_bluetooth_init: stack startup timed out\n"); + bool timeout = mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; + // Required to stop the polling loop. mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; // Attempt a shutdown (may not do anything). @@ -652,7 +657,7 @@ int mp_bluetooth_init(void) { // Clean up. MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; - return MP_ETIMEDOUT; + return timeout ? MP_ETIMEDOUT : MP_EINVAL; } DEBUG_printf("mp_bluetooth_init: stack startup complete\n"); diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c index 5a5c1d4a4e79d..dbfb5e0d0e5f7 100644 --- a/ports/unix/mpbthciport.c +++ b/ports/unix/mpbthciport.c @@ -124,12 +124,12 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { if (path != NULL) { strcpy(uart_device_name, path); } - DEBUG_printf("Using HCI UART: %s\n", uart_device_name); + DEBUG_printf("mp_bluetooth_hci_uart_init: Using HCI UART: %s\n", uart_device_name); int flags = O_RDWR | O_NOCTTY | O_NONBLOCK; uart_fd = open(uart_device_name, flags); if (uart_fd == -1) { - DEBUG_printf("Unable to open port %s", uart_device_name); + printf("mp_bluetooth_hci_uart_init: Unable to open port %s\n", uart_device_name); return -1; } From 311b8519af75be9fe4215b5ee8f77e91d089d5df Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 19 Aug 2020 10:46:09 +1000 Subject: [PATCH 258/352] esp32: Pin MicroPython and NimBLE tasks to core 0. MicroPython and NimBLE must be on the same core, for synchronisation of the BLE ringbuf and the MicroPython scheduler. However, in the current IDF versions (3.3 and 4.0) there are issues (see e.g. #5489) with running NimBLE on core 1. This change - pinning both tasks to core 0 - makes it possible to reliably run the BLE multitests on esp32 boards. --- ports/esp32/boards/sdkconfig.ble | 9 ++++++--- ports/esp32/mphalport.h | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ports/esp32/boards/sdkconfig.ble b/ports/esp32/boards/sdkconfig.ble index cdbb621a63112..f714ce4629b32 100644 --- a/ports/esp32/boards/sdkconfig.ble +++ b/ports/esp32/boards/sdkconfig.ble @@ -9,9 +9,12 @@ CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4 # Pin to the same core as MP. -CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=n -CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y -CONFIG_BT_NIMBLE_PINNED_TO_CORE=1 +# Until we move to IDF 4.2+, we need NimBLE on core 0, and for synchronisation +# with the ringbuffer and scheduler MP needs to be on the same core. +# See https://github.com/micropython/micropython/issues/5489 +CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y +CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=n +CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 # v3.3-only (renamed in 4.0) CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index 1f78d820a3f17..60cc308d68275 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -35,8 +35,11 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -// The core that the MicroPython task(s) are pinned to -#define MP_TASK_COREID (1) +// The core that the MicroPython task(s) are pinned to. +// Until we move to IDF 4.2+, we need NimBLE on core 0, and for synchronisation +// with the ringbuffer and scheduler MP needs to be on the same core. +// See https://github.com/micropython/micropython/issues/5489 +#define MP_TASK_COREID (0) extern TaskHandle_t mp_main_task_handle; From 126f972c34f25bffc07d0d1cb3e536921fec0979 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 19 Aug 2020 12:55:38 +1000 Subject: [PATCH 259/352] extmod/nimble: Add timeout for HCI sync on startup. This allows `ble.active(1)` to fail correctly if the HCI controller is unavailable. It also avoids an infine loop in the NimBLE event handler where NimBLE doesn't correctly detect that the HCI controller is unavailable and keeps trying to reset. Furthermore, it fixes an issue where GATT service registrations were left allocated, which led to a bad realloc if the stack was activated multiple times. --- extmod/nimble/modbluetooth_nimble.c | 28 +++++++++++++++++++++------- extmod/nimble/nimble/nimble_npl_os.c | 15 ++++++++++++--- ports/unix/mpbthciport.c | 16 ++++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 4222f58fa8812..9ca90b602476f 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -52,6 +52,9 @@ STATIC uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM; +#define NIMBLE_STARTUP_TIMEOUT 2000 +STATIC struct ble_npl_sem startup_sem; + // Any BLE_HS_xxx code not in this table will default to MP_EIO. STATIC int8_t ble_hs_err_to_errno_table[] = { [BLE_HS_EAGAIN] = MP_EAGAIN, @@ -206,6 +209,8 @@ STATIC void sync_cb(void) { ble_svc_gap_device_name_set(MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME); mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE; + + ble_npl_sem_release(&startup_sem); } STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { @@ -355,6 +360,8 @@ int mp_bluetooth_init(void) { ble_hs_cfg.gatts_register_cb = gatts_register_cb; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + ble_npl_sem_init(&startup_sem, 0); + MP_STATE_PORT(bluetooth_nimble_root_pointers) = m_new0(mp_bluetooth_nimble_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db); @@ -370,21 +377,28 @@ int mp_bluetooth_init(void) { // Initialise NimBLE memory and data structures. nimble_port_init(); - // By default, just register the default gap/gatt service. - ble_svc_gap_init(); - ble_svc_gatt_init(); - // Make sure that the HCI UART and event handling task is running. mp_bluetooth_nimble_port_start(); // Static initialization is complete, can start processing events. mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC; - // Wait for sync callback - while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { - MICROPY_EVENT_POLL_HOOK + ble_npl_sem_pend(&startup_sem, NIMBLE_STARTUP_TIMEOUT); + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE) { + mp_bluetooth_deinit(); + return MP_ETIMEDOUT; } + // By default, just register the default gap/gatt service. + ble_svc_gap_init(); + ble_svc_gatt_init(); + // The preceeding two calls allocate service definitions on the heap, + // then we must now call gatts_start to register those services + // and free the heap memory. + // Otherwise it will be realloc'ed on the next stack startup. + ble_gatts_start(); + DEBUG_printf("mp_bluetooth_init: ready\n"); return 0; diff --git a/extmod/nimble/nimble/nimble_npl_os.c b/extmod/nimble/nimble/nimble_npl_os.c index 46f7a0b291303..2ec012940f763 100644 --- a/extmod/nimble/nimble/nimble_npl_os.c +++ b/extmod/nimble/nimble/nimble_npl_os.c @@ -32,6 +32,7 @@ #include "extmod/nimble/hal/hal_uart.h" #include "extmod/modbluetooth.h" +#include "extmod/nimble/modbluetooth_nimble.h" #define DEBUG_OS_printf(...) // printf(__VA_ARGS__) #define DEBUG_MALLOC_printf(...) // printf(__VA_ARGS__) @@ -180,7 +181,8 @@ struct ble_npl_eventq *global_eventq = NULL; void mp_bluetooth_nimble_os_eventq_run_all(void) { for (struct ble_npl_eventq *evq = global_eventq; evq != NULL; evq = evq->nextq) { - while (evq->head != NULL) { + int n = 0; + while (evq->head != NULL && mp_bluetooth_nimble_ble_state > MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { struct ble_npl_event *ev = evq->head; evq->head = ev->next; if (ev->next) { @@ -191,6 +193,13 @@ void mp_bluetooth_nimble_os_eventq_run_all(void) { DEBUG_EVENT_printf("event_run(%p)\n", ev); ev->fn(ev); DEBUG_EVENT_printf("event_run(%p) done\n", ev); + + if (++n > 3) { + // Limit to running 3 tasks per queue. + // Some tasks (such as reset) can enqueue themselves + // making this an infinite loop (while in PENDSV). + break; + } } } } @@ -348,11 +357,11 @@ ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout } if (sem->count == 0) { - printf("NimBLE: HCI ACK timeout\n"); + DEBUG_SEM_printf("ble_npl_sem_pend: semaphore timeout\n"); return BLE_NPL_TIMEOUT; } - DEBUG_SEM_printf("got response in %u ms\n", (int)(mp_hal_ticks_ms() - t0)); + DEBUG_SEM_printf("ble_npl_sem_pend: acquired in %u ms\n", (int)(mp_hal_ticks_ms() - t0)); } sem->count -= 1; return BLE_NPL_OK; diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c index dbfb5e0d0e5f7..316a8831fe97b 100644 --- a/ports/unix/mpbthciport.c +++ b/ports/unix/mpbthciport.c @@ -105,6 +105,10 @@ STATIC int configure_uart(void) { // Apply immediately. if (tcsetattr(uart_fd, TCSANOW, &toptions) < 0) { DEBUG_printf("Couldn't set term attributes"); + + close(uart_fd); + uart_fd = -1; + return -1; } @@ -149,6 +153,10 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { int mp_bluetooth_hci_uart_deinit(void) { DEBUG_printf("mp_bluetooth_hci_uart_deinit\n"); + if (uart_fd == -1) { + return 0; + } + // Wait for the poll loop to terminate when the state is set to OFF. pthread_join(hci_poll_thread_id, NULL); @@ -168,6 +176,10 @@ int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { int mp_bluetooth_hci_uart_readchar(void) { // DEBUG_printf("mp_bluetooth_hci_uart_readchar\n"); + if (uart_fd == -1) { + return -1; + } + uint8_t c; ssize_t bytes_read = read(uart_fd, &c, 1); @@ -184,6 +196,10 @@ int mp_bluetooth_hci_uart_readchar(void) { int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { // DEBUG_printf("mp_bluetooth_hci_uart_write\n"); + if (uart_fd == -1) { + return 0; + } + #if DEBUG_HCI_DUMP printf("[% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]); for (size_t i = 1; i < len; ++i) { From b27edb8073409a3caee921dd4e2c1b182bd4c0fb Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 20 Aug 2020 23:21:06 +1000 Subject: [PATCH 260/352] stm32/make-stmconst.py: Add support for WB55 header files. --- ports/stm32/make-stmconst.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ports/stm32/make-stmconst.py b/ports/stm32/make-stmconst.py index 1b3cc08d951f3..ac5c56f5c79f5 100644 --- a/ports/stm32/make-stmconst.py +++ b/ports/stm32/make-stmconst.py @@ -46,7 +46,7 @@ def __init__(self, line): class Lexer: re_io_reg = r"__IO uint(?P8|16|32)_t +(?P[A-Z0-9]+)" - re_comment = r"(?P[A-Za-z0-9 \-/_()&]+)" + re_comment = r"(?P[A-Za-z0-9 \-/_()&:]+)" re_addr_offset = r"Address offset: (?P0x[0-9A-Z]{2,3})" regexs = ( ( @@ -78,16 +78,16 @@ class Lexer: ( "IO reg", re.compile( - re_io_reg + r"; */\*!< *" + re_comment + r", +" + re_addr_offset + r" *\*/" + re_io_reg + r" *; */\*!< *" + re_comment + r",? +" + re_addr_offset + r" *\*/" ), ), ( "IO reg array", re.compile( re_io_reg - + r"\[(?P[2-8])\]; */\*!< *" + + r"\[(?P[2-8])\] *; */\*!< *" + re_comment - + r", +" + + r",? +" + re_addr_offset + r"-(0x[0-9A-Z]{2,3}) *\*/" ), @@ -160,7 +160,11 @@ def parse_file(filename): if m[0] == "}": pass elif m[0] == "} TypeDef": - reg_defs[m[1].groupdict()["id"]] = regs + d = m[1].groupdict() + n = d["id"] + g = d["global"] + if n not in reg_defs or not g: + reg_defs[n] = regs else: raise LexerError(lexer.line_number) @@ -298,6 +302,7 @@ def main(): "USART", "WWDG", "RNG", + "IPCC", ): if reg in reg_defs: print_regs(reg, reg_defs[reg], needed_qstrs, needed_mpzs) From 30e8162ac49d333b00bae4a7f719a1f24692b062 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 20 Aug 2020 18:12:44 +1000 Subject: [PATCH 261/352] stm32/rfcore: Update rfcore.c to match how ST examples work. - Split tables and buffers into SRAM2A/2B. - Use structs rather than word offsets to access tables. - Use FLASH_IPCCDBA register value rather than option bytes directly. --- ports/stm32/boards/stm32wb55xg.ld | 13 ++- ports/stm32/rfcore.c | 178 ++++++++++++++++++++++-------- 2 files changed, 141 insertions(+), 50 deletions(-) diff --git a/ports/stm32/boards/stm32wb55xg.ld b/ports/stm32/boards/stm32wb55xg.ld index dbeccc1895775..c3dc5f5197a00 100644 --- a/ports/stm32/boards/stm32wb55xg.ld +++ b/ports/stm32/boards/stm32wb55xg.ld @@ -9,7 +9,8 @@ MEMORY FLASH_APP (rx) : ORIGIN = 0x08004000, LENGTH = 496K /* sectors 4-127 */ FLASH_FS (r) : ORIGIN = 0x08080000, LENGTH = 256K /* sectors 128-191 */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K /* SRAM1 */ - RAM2A (xrw) : ORIGIN = 0x20030020, LENGTH = 8K /* SRAM2A */ + RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K /* SRAM2A */ + RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K /* SRAM2B */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -36,10 +37,20 @@ _flash_fs_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); SECTIONS { + /* Put all IPCC tables into SRAM2A. */ .ram2a_bss : { . = ALIGN(4); + . = . + 64; /* Leave room for the mb_ref_table_t (assuming IPCCDBA==0). */ *rfcore.o(.bss.ipcc_mem_*) . = ALIGN(4); } >RAM2A + + /* Put all IPCC buffers into SRAM2B. */ + .ram2b_bss : + { + . = ALIGN(4); + *rfcore.o(.bss.ipcc_membuf_*) + . = ALIGN(4); + } >RAM2B } diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 54b3393435e1e..3565c03898444 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2019 Damien P. George + * Copyright (c) 2020 Jim Mussared * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,6 +35,10 @@ #if defined(STM32WB) +#include "stm32wbxx_ll_ipcc.h" + +#define DEBUG_printf(...) // printf("rfcore: " __VA_ARGS__) + // Define to 1 to print traces of HCI packets #define HCI_TRACE (0) @@ -61,23 +66,88 @@ typedef struct _parse_hci_info_t { bool was_hci_reset_evt; } parse_hci_info_t; -static volatile uint32_t ipcc_mem_dev_info_tab[8]; -static volatile uint32_t ipcc_mem_ble_tab[4]; -static volatile uint32_t ipcc_mem_sys_tab[2]; -static volatile uint32_t ipcc_mem_memmgr_tab[7]; - -static volatile uint32_t ipcc_mem_sys_cmd_buf[272 / 4]; -static volatile tl_list_node_t ipcc_mem_sys_queue; - -static volatile tl_list_node_t ipcc_mem_memmgr_free_buf_queue; -static volatile uint32_t ipcc_mem_memmgr_ble_spare_evt_buf[272 / 4]; -static volatile uint32_t ipcc_mem_memmgr_sys_spare_evt_buf[272 / 4]; -static volatile uint32_t ipcc_mem_memmgr_evt_pool[6 * 272 / 4]; - -static volatile uint32_t ipcc_mem_ble_cmd_buf[272 / 4]; -static volatile uint32_t ipcc_mem_ble_cs_buf[272 / 4]; -static volatile tl_list_node_t ipcc_mem_ble_evt_queue; -static volatile uint32_t ipcc_mem_ble_hci_acl_data_buf[272 / 4]; +// Version +// [0:3] = Build - 0: Untracked - 15:Released - x: Tracked version +// [4:7] = branch - 0: Mass Market - x: ... +// [8:15] = Subversion +// [16:23] = Version minor +// [24:31] = Version major + +// Memory Size +// [0:7] = Flash (Number of 4k sectors) +// [8:15] = Reserved (Shall be set to 0 - may be used as flash extension) +// [16:23] = SRAM2b (Number of 1k sectors) +// [24:31] = SRAM2a (Number of 1k sectors) + +typedef struct __attribute__((packed)) _ipcc_device_info_table_t { + uint32_t safeboot_version; + uint32_t fus_version; + uint32_t fus_memorysize; + uint32_t fus_info; + uint32_t fw_version; + uint32_t fw_memorysize; + uint32_t fw_infostack; + uint32_t fw_reserved; +} ipcc_device_info_table_t; + +typedef struct __attribute__((packed)) _ipcc_ble_table_t { + uint8_t *pcmd_buffer; + uint8_t *pcs_buffer; + tl_list_node_t *pevt_queue; + uint8_t *phci_acl_data_buffer; +} ipcc_ble_table_t; + +// msg +// [0:7] = cmd/evt +// [8:31] = Reserved +typedef struct __attribute__((packed)) _ipcc_sys_table_t { + uint8_t *pcmd_buffer; + tl_list_node_t *sys_queue; +} ipcc_sys_table_t; + +typedef struct __attribute__((packed)) _ipcc_mem_manager_table_t { + uint8_t *spare_ble_buffer; + uint8_t *spare_sys_buffer; + uint8_t *blepool; + uint32_t blepoolsize; + tl_list_node_t *pevt_free_buffer_queue; + uint8_t *traces_evt_pool; + uint32_t tracespoolsize; +} ipcc_mem_manager_table_t; + +typedef struct __attribute__((packed)) _ipcc_ref_table_t { + ipcc_device_info_table_t *p_device_info_table; + ipcc_ble_table_t *p_ble_table; + void *p_thread_table; + ipcc_sys_table_t *p_sys_table; + ipcc_mem_manager_table_t *p_mem_manager_table; + void *p_traces_table; + void *p_mac_802_15_4_table; + void *p_zigbee_table; + void *p_lld_tests_table; + void *p_lld_ble_table; +} ipcc_ref_table_t; + +// The stm32wb55xg.ld script puts .bss.ipcc_mem_* into SRAM2A and .bss_ipcc_membuf_* into SRAM2B. +// It also leaves 64 bytes at the start of SRAM2A for the ref table. + +STATIC ipcc_device_info_table_t ipcc_mem_dev_info_tab; // mem1 +STATIC ipcc_ble_table_t ipcc_mem_ble_tab; // mem1 +STATIC ipcc_sys_table_t ipcc_mem_sys_tab; // mem1 +STATIC ipcc_mem_manager_table_t ipcc_mem_memmgr_tab; // mem1 + +STATIC uint8_t ipcc_membuf_sys_cmd_buf[272]; // mem2 +STATIC tl_list_node_t ipcc_mem_sys_queue; // mem1 + +STATIC tl_list_node_t ipcc_mem_memmgr_free_buf_queue; // mem1 +STATIC uint8_t ipcc_membuf_memmgr_ble_spare_evt_buf[272]; // mem2 +STATIC uint8_t ipcc_membuf_memmgr_sys_spare_evt_buf[272]; // mem2 +STATIC uint8_t ipcc_membuf_memmgr_evt_pool[6 * 272]; // mem2 + +STATIC uint8_t ipcc_membuf_ble_cmd_buf[272]; // mem2 +STATIC uint8_t ipcc_membuf_ble_cs_buf[272]; // mem2 +STATIC tl_list_node_t ipcc_mem_ble_evt_queue; // mem1 +STATIC uint8_t ipcc_membuf_ble_hci_acl_data_buf[272]; // mem2 /******************************************************************************/ // Transport layer linked list @@ -105,21 +175,21 @@ STATIC void tl_list_append(volatile tl_list_node_t *head, volatile tl_list_node_ /******************************************************************************/ // IPCC interface -STATIC uint32_t get_ipccdba(void) { - return *(uint32_t *)(OPTION_BYTE_BASE + 0x68) & 0x3fff; -} - -STATIC volatile void **get_buffer_table(void) { - return (volatile void **)(SRAM2A_BASE + get_ipccdba()); +STATIC volatile ipcc_ref_table_t *get_buffer_table(void) { + // The IPCCDBA option bytes must not be changed without + // making a corresponding change to the linker script. + return (volatile ipcc_ref_table_t *)(SRAM2A_BASE + LL_FLASH_GetIPCCBufferAddr() * 4); } void ipcc_init(uint32_t irq_pri) { + DEBUG_printf("ipcc_init\n"); + // Setup buffer table pointers - volatile void **tab = get_buffer_table(); - tab[0] = &ipcc_mem_dev_info_tab[0]; - tab[1] = &ipcc_mem_ble_tab[0]; - tab[3] = &ipcc_mem_sys_tab[0]; - tab[4] = &ipcc_mem_memmgr_tab[0]; + volatile ipcc_ref_table_t *tab = get_buffer_table(); + tab->p_device_info_table = &ipcc_mem_dev_info_tab; + tab->p_ble_table = &ipcc_mem_ble_tab; + tab->p_sys_table = &ipcc_mem_sys_tab; + tab->p_mem_manager_table = &ipcc_mem_memmgr_tab; // Start IPCC peripheral __HAL_RCC_IPCC_CLK_ENABLE(); @@ -130,32 +200,34 @@ void ipcc_init(uint32_t irq_pri) { NVIC_SetPriority(IPCC_C1_RX_IRQn, irq_pri); HAL_NVIC_EnableIRQ(IPCC_C1_RX_IRQn); - // Device info table will be populated by FUS/WS + // Device info table will be populated by FUS/WS on CPU2 boot. // Populate system table tl_list_init(&ipcc_mem_sys_queue); - ipcc_mem_sys_tab[0] = (uint32_t)&ipcc_mem_sys_cmd_buf[0]; - ipcc_mem_sys_tab[1] = (uint32_t)&ipcc_mem_sys_queue; + ipcc_mem_sys_tab.pcmd_buffer = ipcc_membuf_sys_cmd_buf; + ipcc_mem_sys_tab.sys_queue = &ipcc_mem_sys_queue; // Populate memory manager table tl_list_init(&ipcc_mem_memmgr_free_buf_queue); - ipcc_mem_memmgr_tab[0] = (uint32_t)&ipcc_mem_memmgr_ble_spare_evt_buf[0]; - ipcc_mem_memmgr_tab[1] = (uint32_t)&ipcc_mem_memmgr_sys_spare_evt_buf[0]; - ipcc_mem_memmgr_tab[2] = (uint32_t)&ipcc_mem_memmgr_evt_pool[0]; - ipcc_mem_memmgr_tab[3] = sizeof(ipcc_mem_memmgr_evt_pool); - ipcc_mem_memmgr_tab[4] = (uint32_t)&ipcc_mem_memmgr_free_buf_queue; - ipcc_mem_memmgr_tab[5] = 0; - ipcc_mem_memmgr_tab[6] = 0; + ipcc_mem_memmgr_tab.spare_ble_buffer = ipcc_membuf_memmgr_ble_spare_evt_buf; + ipcc_mem_memmgr_tab.spare_sys_buffer = ipcc_membuf_memmgr_sys_spare_evt_buf; + ipcc_mem_memmgr_tab.blepool = ipcc_membuf_memmgr_evt_pool; + ipcc_mem_memmgr_tab.blepoolsize = sizeof(ipcc_membuf_memmgr_evt_pool); + ipcc_mem_memmgr_tab.pevt_free_buffer_queue = &ipcc_mem_memmgr_free_buf_queue; + ipcc_mem_memmgr_tab.traces_evt_pool = NULL; + ipcc_mem_memmgr_tab.tracespoolsize = 0; // Populate BLE table tl_list_init(&ipcc_mem_ble_evt_queue); - ipcc_mem_ble_tab[0] = (uint32_t)&ipcc_mem_ble_cmd_buf[0]; - ipcc_mem_ble_tab[1] = (uint32_t)&ipcc_mem_ble_cs_buf[0]; - ipcc_mem_ble_tab[2] = (uint32_t)&ipcc_mem_ble_evt_queue; - ipcc_mem_ble_tab[3] = (uint32_t)&ipcc_mem_ble_hci_acl_data_buf[0]; + ipcc_mem_ble_tab.pcmd_buffer = ipcc_membuf_ble_cmd_buf; + ipcc_mem_ble_tab.pcs_buffer = ipcc_membuf_ble_cs_buf; + ipcc_mem_ble_tab.pevt_queue = &ipcc_mem_ble_evt_queue; + ipcc_mem_ble_tab.phci_acl_data_buffer = ipcc_membuf_ble_hci_acl_data_buf; } STATIC int ipcc_wait_ack(unsigned int ch, uint32_t timeout_ms) { + DEBUG_printf("ipcc_wait_ack\n"); + uint32_t t0 = mp_hal_ticks_ms(); while (IPCC->C1TOC2SR & ch) { if (mp_hal_ticks_ms() - t0 > timeout_ms) { @@ -168,6 +240,8 @@ STATIC int ipcc_wait_ack(unsigned int ch, uint32_t timeout_ms) { } STATIC int ipcc_wait_msg(unsigned int ch, uint32_t timeout_ms) { + DEBUG_printf("ipcc_wait_msg\n"); + uint32_t t0 = mp_hal_ticks_ms(); while (!(IPCC->C2TOC1SR & ch)) { if (mp_hal_ticks_ms() - t0 > timeout_ms) { @@ -254,8 +328,8 @@ STATIC void tl_check_msg(volatile tl_list_node_t *head, unsigned int ch, parse_h while (cur != head) { tl_parse_hci_msg((uint8_t *)cur->body, parse); volatile tl_list_node_t *next = tl_list_unlink(cur); - if ((void *)&ipcc_mem_memmgr_evt_pool[0] <= (void *)cur - && (void *)cur < (void *)&ipcc_mem_memmgr_evt_pool[MP_ARRAY_SIZE(ipcc_mem_memmgr_evt_pool)]) { + if ((void *)&ipcc_membuf_memmgr_evt_pool[0] <= (void *)cur + && (void *)cur < (void *)&ipcc_membuf_memmgr_evt_pool[MP_ARRAY_SIZE(ipcc_membuf_memmgr_evt_pool)]) { // Place memory back in free pool tl_list_append(&ipcc_mem_memmgr_free_buf_queue, cur); free = true; @@ -291,12 +365,12 @@ STATIC void tl_sys_wait_resp(const uint8_t *buf, unsigned int ch) { } STATIC void tl_sys_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd((uint8_t *)&ipcc_mem_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, len, buf); - tl_sys_wait_resp((uint8_t *)&ipcc_mem_sys_cmd_buf, IPCC_CH_SYS); + tl_hci_cmd((uint8_t *)&ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, len, buf); + tl_sys_wait_resp((uint8_t *)&ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS); } STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd((uint8_t *)&ipcc_mem_ble_cmd_buf[0], IPCC_CH_BLE, 0x01, opcode, len, buf); + tl_hci_cmd((uint8_t *)&ipcc_membuf_ble_cmd_buf[0], IPCC_CH_BLE, 0x01, opcode, len, buf); ipcc_wait_msg(IPCC_CH_BLE, 250); tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); } @@ -305,6 +379,8 @@ STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) // RF core interface void rfcore_init(void) { + DEBUG_printf("rfcore_init\n"); + // Ensure LSE is running rtc_init_finalise(); @@ -361,6 +437,8 @@ static const struct { }; void rfcore_ble_init(void) { + DEBUG_printf("rfcore_ble_init\n"); + // Clear any outstanding messages from ipcc_init tl_check_msg(&ipcc_mem_sys_queue, IPCC_CH_SYS, NULL); tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); @@ -371,6 +449,8 @@ void rfcore_ble_init(void) { } void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { + DEBUG_printf("rfcore_ble_hci_cmd\n"); + #if HCI_TRACE printf("[% 8d] HCI_CMD(%02x", mp_hal_ticks_ms(), src[0]); for (int i = 1; i < len; ++i) { @@ -382,10 +462,10 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { tl_list_node_t *n; uint32_t ch; if (src[0] == 0x01) { - n = (tl_list_node_t *)&ipcc_mem_ble_cmd_buf[0]; + n = (tl_list_node_t *)&ipcc_membuf_ble_cmd_buf[0]; ch = IPCC_CH_BLE; } else if (src[0] == 0x02) { - n = (tl_list_node_t *)&ipcc_mem_ble_hci_acl_data_buf[0]; + n = (tl_list_node_t *)&ipcc_membuf_ble_hci_acl_data_buf[0]; ch = IPCC_CH_HCI_ACL; } else { printf("** UNEXPECTED HCI HDR: 0x%02x **\n", src[0]); From 9c9cc7a02f6f998d0141989040d68219d6ad0625 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 8 Sep 2020 17:00:41 +1000 Subject: [PATCH 262/352] stm32/boards/USBDONGLE_WB55: Add USE_MBOOT support. --- ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk index 416364df9ef6d..dcec788ed443f 100644 --- a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk @@ -1,9 +1,18 @@ MCU_SERIES = wb CMSIS_MCU = STM32WB55xx AF_FILE = boards/stm32wb55_af.csv -LD_FILES = boards/stm32wb55xg.ld boards/common_basic.ld STARTUP_FILE = lib/stm32lib/CMSIS/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.o +ifeq ($(USE_MBOOT),1) +# When using Mboot all the text goes together after the bootloader +LD_FILES = boards/stm32wb55xg.ld boards/common_bl.ld +TEXT0_ADDR = 0x08004000 +else +# When not using Mboot the text goes at the start of flash +LD_FILES = boards/stm32wb55xg.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 +endif + # MicroPython settings MICROPY_PY_BLUETOOTH = 1 MICROPY_BLUETOOTH_NIMBLE = 1 From 0f28020a68b190d973dc5ef2049c76eb28acddee Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 28 Aug 2020 14:59:24 +1000 Subject: [PATCH 263/352] stm32/powerctrlboot: Acquire HSEM5 on STM32WB during SystemClock_Config. This is required to allow using WS firmware newer than 1.1.1 concurrently with USB (e.g. USB VCP). It prevents CPU2 from modifying the CLK48 config on boot. Tested on WS=1.8 FUS=1.1. See AN5289 and https://github.com/micropython/micropython/issues/6316 --- ports/stm32/powerctrlboot.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 206b19b75067a..880e43e04c2a2 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -154,12 +154,23 @@ void SystemClock_Config(void) { #elif defined(STM32WB) +#include "stm32wbxx_ll_hsem.h" + +// This semaphore protected access to the CLK48 configuration. +// CPU1 should hold this semaphore while the USB peripheral is in use. +// See AN5289 and https://github.com/micropython/micropython/issues/6316. +#define CLK48_SEMID (5) + void SystemClock_Config(void) { // Enable the 32MHz external oscillator RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)) { } + // Prevent CPU2 from disabling CLK48. + while (LL_HSEM_1StepLock(HSEM, CLK48_SEMID)) { + } + // Use HSE and the PLL to get a 64MHz SYSCLK #define PLLM (HSE_VALUE / 8000000) // VCO input is 8MHz #define PLLN (24) // 24*8MHz = 192MHz From 5eda362e0a7ed0eda6a4becdebf76a53cf69f944 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 31 Aug 2020 15:54:20 +1000 Subject: [PATCH 264/352] tests/multi_bluetooth: Make ble_gap_connect robust against event timing. --- tests/multi_bluetooth/ble_gap_connect.py | 61 ++++++++++++-------- tests/multi_bluetooth/ble_gap_connect.py.exp | 1 + 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/tests/multi_bluetooth/ble_gap_connect.py b/tests/multi_bluetooth/ble_gap_connect.py index ba9c28230d7da..8b40a29163119 100644 --- a/tests/multi_bluetooth/ble_gap_connect.py +++ b/tests/multi_bluetooth/ble_gap_connect.py @@ -10,35 +10,36 @@ _IRQ_PERIPHERAL_CONNECT = const(7) _IRQ_PERIPHERAL_DISCONNECT = const(8) -waiting_event = None -waiting_data = None +central_connected = False +central_disconnected = False +peripheral_connected = False +peripheral_disconnected = False +conn_handle = None def irq(event, data): - global waiting_event, waiting_data + global central_connected, central_disconnected, peripheral_connected, peripheral_disconnected, conn_handle if event == _IRQ_CENTRAL_CONNECT: print("_IRQ_CENTRAL_CONNECT") + central_connected = True + conn_handle = data[0] elif event == _IRQ_CENTRAL_DISCONNECT: print("_IRQ_CENTRAL_DISCONNECT") + central_disconnected = True elif event == _IRQ_PERIPHERAL_CONNECT: print("_IRQ_PERIPHERAL_CONNECT") + peripheral_connected = True + conn_handle = data[0] elif event == _IRQ_PERIPHERAL_DISCONNECT: print("_IRQ_PERIPHERAL_DISCONNECT") + peripheral_disconnected = True + remote_addr = data[0] - if waiting_event is not None: - if event == waiting_event: - waiting_event = None - waiting_data = data - - -def wait_for_event(event, timeout_ms): - global waiting_event, waiting_data - waiting_event = event - waiting_data = None +def wait_for(fn, timeout_ms): t0 = time.ticks_ms() while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: - if waiting_data: + if fn(): return True machine.idle() return False @@ -46,25 +47,31 @@ def wait_for_event(event, timeout_ms): # Acting in peripheral role. def instance0(): + global central_connected, central_disconnected + multitest.globals(BDADDR=ble.config("mac")) print("gap_advertise") ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") multitest.next() try: # Wait for central to connect, then wait for it to disconnect. - if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + if not wait_for(lambda: central_connected, TIMEOUT_MS): return - if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS): + if not wait_for(lambda: central_disconnected, TIMEOUT_MS): return + central_connected = False + central_disconnected = False + # Start advertising again. + print("gap_advertise") ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") # Wait for central to connect, then disconnect it. - if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + if not wait_for(lambda: central_connected, TIMEOUT_MS): return - print("gap_disconnect:", ble.gap_disconnect(waiting_data[0])) - if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS): + print("gap_disconnect:", ble.gap_disconnect(conn_handle)) + if not wait_for(lambda: central_disconnected, TIMEOUT_MS): return finally: ble.active(0) @@ -72,26 +79,32 @@ def instance0(): # Acting in central role. def instance1(): + global peripheral_connected, peripheral_disconnected + multitest.next() try: # Connect to peripheral and then disconnect. print("gap_connect") ble.gap_connect(*BDADDR) - if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): + if not wait_for(lambda: peripheral_connected, TIMEOUT_MS): return - print("gap_disconnect:", ble.gap_disconnect(waiting_data[0])) - if not wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS): + print("gap_disconnect:", ble.gap_disconnect(conn_handle)) + if not wait_for(lambda: peripheral_disconnected, TIMEOUT_MS): return + peripheral_connected = False + peripheral_disconnected = False + # Wait for peripheral to start advertising again. time.sleep_ms(100) # Connect to peripheral and then let the peripheral disconnect us. print("gap_connect") ble.gap_connect(*BDADDR) - if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): + if not wait_for(lambda: peripheral_connected, TIMEOUT_MS): + return + if not wait_for(lambda: peripheral_disconnected, TIMEOUT_MS): return - wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) finally: ble.active(0) diff --git a/tests/multi_bluetooth/ble_gap_connect.py.exp b/tests/multi_bluetooth/ble_gap_connect.py.exp index 1c96f1d48ac07..d0dc020703214 100644 --- a/tests/multi_bluetooth/ble_gap_connect.py.exp +++ b/tests/multi_bluetooth/ble_gap_connect.py.exp @@ -2,6 +2,7 @@ gap_advertise _IRQ_CENTRAL_CONNECT _IRQ_CENTRAL_DISCONNECT +gap_advertise _IRQ_CENTRAL_CONNECT gap_disconnect: True _IRQ_CENTRAL_DISCONNECT From 01f2d776141d94b8d15d91aa1e0f0cef4244d5f7 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 31 Aug 2020 15:55:09 +1000 Subject: [PATCH 265/352] stm32/rfcore: Fix length matching in HCI parser. --- ports/stm32/rfcore.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 3565c03898444..99bb405630ba7 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -258,11 +258,13 @@ STATIC int ipcc_wait_msg(unsigned int ch, uint32_t timeout_ms) { STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { const char *kind; - size_t len = 3 + buf[2]; + size_t len = 0; switch (buf[0]) { case 0x02: { // Standard BT HCI ACL packet kind = "HCI_ACL"; + // + len = 5 + buf[3] + (buf[4] << 8); if (parse != NULL) { parse->cb_fun(parse->cb_env, buf, len); } @@ -271,6 +273,8 @@ STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { case 0x04: { // Standard BT HCI event packet kind = "HCI_EVT"; + // + len = 3 + buf[2]; if (parse != NULL) { bool fix = false; if (buf[1] == 0x0e && len == 7 && buf[3] == 0x01 && buf[4] == 0x63 && buf[5] == 0x0c && buf[6] == 0x01) { From 8b4ebd716613ba981c6714510a7adf5c860d7bdf Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 1 Sep 2020 14:13:44 +1000 Subject: [PATCH 266/352] stm32/rfcore: Refactor some helper funcs, and remove some magic numbers. Also explain what the payload fixup code is doing. --- ports/stm32/rfcore.c | 262 +++++++++++++++++++++++++------------------ 1 file changed, 153 insertions(+), 109 deletions(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 99bb405630ba7..5630ea7132db8 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -42,18 +42,33 @@ // Define to 1 to print traces of HCI packets #define HCI_TRACE (0) -#define IPCC_CH_BLE (0x01) // BLE HCI command and response -#define IPCC_CH_SYS (0x02) // system HCI command and response -#define IPCC_CH_MM (0x08) // release buffer -#define IPCC_CH_HCI_ACL (0x20) // HCI ACL outgoing data +#define IPCC_CH_BLE (LL_IPCC_CHANNEL_1) // BLE HCI command and response +#define IPCC_CH_SYS (LL_IPCC_CHANNEL_2) // system HCI command and response +#define IPCC_CH_MM (LL_IPCC_CHANNEL_4) // release buffer +#define IPCC_CH_HCI_ACL (LL_IPCC_CHANNEL_6) // HCI ACL outgoing data -#define OGF_VENDOR (0x3f) -#define OCF_WRITE_CONFIG (0x0c) -#define OCF_SET_TX_POWER (0x0f) -#define OCF_BLE_INIT (0x66) +#define OGF_CTLR_BASEBAND (0x03) +#define OCF_CB_RESET (0x03) +#define OCF_CB_SET_EVENT_MASK2 (0x63) + +#define OGF_VENDOR (0x3f) +#define OCF_WRITE_CONFIG (0x0c) +#define OCF_SET_TX_POWER (0x0f) +#define OCF_BLE_INIT (0x66) #define HCI_OPCODE(ogf, ocf) ((ogf) << 10 | (ocf)) +#define HCI_KIND_BT_CMD (0x01) // ...? +#define HCI_KIND_BT_ACL (0x02) // +#define HCI_KIND_BT_EVENT (0x04) // +#define HCI_KIND_VENDOR_RESPONSE (0x11) +#define HCI_KIND_VENDOR_EVENT (0x12) + +#define HCI_EVENT_COMMAND_COMPLETE (0x0E) // + +#define SYS_ACK_TIMEOUT_MS (250) +#define BLE_ACK_TIMEOUT_MS (250) + typedef struct _tl_list_node_t { volatile struct _tl_list_node_t *next; volatile struct _tl_list_node_t *prev; @@ -194,12 +209,6 @@ void ipcc_init(uint32_t irq_pri) { // Start IPCC peripheral __HAL_RCC_IPCC_CLK_ENABLE(); - // Enable wanted IRQs - IPCC->C1CR = 0; // IPCC_C1CR_RXOIE; - IPCC->C1MR = 0xffffffff; - NVIC_SetPriority(IPCC_C1_RX_IRQn, irq_pri); - HAL_NVIC_EnableIRQ(IPCC_C1_RX_IRQn); - // Device info table will be populated by FUS/WS on CPU2 boot. // Populate system table @@ -225,127 +234,136 @@ void ipcc_init(uint32_t irq_pri) { ipcc_mem_ble_tab.phci_acl_data_buffer = ipcc_membuf_ble_hci_acl_data_buf; } -STATIC int ipcc_wait_ack(unsigned int ch, uint32_t timeout_ms) { - DEBUG_printf("ipcc_wait_ack\n"); - - uint32_t t0 = mp_hal_ticks_ms(); - while (IPCC->C1TOC2SR & ch) { - if (mp_hal_ticks_ms() - t0 > timeout_ms) { - printf("ipcc_wait_ack: timeout\n"); - return -MP_ETIMEDOUT; - } - } - // C2 cleared IPCC flag - return 0; -} - -STATIC int ipcc_wait_msg(unsigned int ch, uint32_t timeout_ms) { - DEBUG_printf("ipcc_wait_msg\n"); - - uint32_t t0 = mp_hal_ticks_ms(); - while (!(IPCC->C2TOC1SR & ch)) { - if (mp_hal_ticks_ms() - t0 > timeout_ms) { - printf("ipcc_wait_msg: timeout\n"); - return -MP_ETIMEDOUT; - } - } - // C2 set IPCC flag - return 0; -} - /******************************************************************************/ // Transport layer HCI interface STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { - const char *kind; + const char *info; size_t len = 0; + bool applied_set_event_event_mask2_fix = false; switch (buf[0]) { - case 0x02: { - // Standard BT HCI ACL packet - kind = "HCI_ACL"; - // + case HCI_KIND_BT_ACL: { + info = "HCI_ACL"; + len = 5 + buf[3] + (buf[4] << 8); if (parse != NULL) { parse->cb_fun(parse->cb_env, buf, len); } break; } - case 0x04: { - // Standard BT HCI event packet - kind = "HCI_EVT"; - // + case HCI_KIND_BT_EVENT: { + info = "HCI_EVT"; + len = 3 + buf[2]; if (parse != NULL) { - bool fix = false; - if (buf[1] == 0x0e && len == 7 && buf[3] == 0x01 && buf[4] == 0x63 && buf[5] == 0x0c && buf[6] == 0x01) { - len -= 1; - fix = true; + + if (buf[1] == HCI_EVENT_COMMAND_COMPLETE && len == 7) { + uint16_t opcode = (buf[5] << 8) | buf[4]; + uint8_t status = buf[6]; + + if (opcode == HCI_OPCODE(OGF_CTLR_BASEBAND, OCF_CB_SET_EVENT_MASK2) && status != 0) { + // The WB doesn't support this command (despite being in CS 4.1), so pretend like + // it succeeded by replacing the final byte (status) with a zero. + applied_set_event_event_mask2_fix = true; + len -= 1; + } + + if (opcode == HCI_OPCODE(OGF_CTLR_BASEBAND, OCF_CB_RESET) && status == 0) { + // Controller acknowledged reset command. + // This will trigger setting the MAC address. + parse->was_hci_reset_evt = true; + } } + parse->cb_fun(parse->cb_env, buf, len); - if (fix) { - len += 1; - uint8_t data = 0x00; // success + + if (applied_set_event_event_mask2_fix) { + // Inject the zero status. + uint8_t data = 0; parse->cb_fun(parse->cb_env, &data, 1); + // Restore the length for the HCI tracing below. + len += 1; } - // Check for successful HCI_Reset event - parse->was_hci_reset_evt = buf[1] == 0x0e && buf[2] == 0x04 && buf[3] == 0x01 - && buf[4] == 0x03 && buf[5] == 0x0c && buf[6] == 0x00; } break; } - case 0x11: { - // Response packet + case HCI_KIND_VENDOR_RESPONSE: { // assert(buf[1] == 0x0e); - kind = "VEND_RESP"; + info = "VEND_RESP"; + len = 3 + buf[2]; // ??? // uint16_t cmd = buf[4] | buf[5] << 8; // uint8_t status = buf[6]; break; } - case 0x12: { - // Event packet + case HCI_KIND_VENDOR_EVENT: { // assert(buf[1] == 0xff); - kind = "VEND_EVT"; + info = "VEND_EVT"; + len = 3 + buf[2]; // ??? // uint16_t evt = buf[3] | buf[4] << 8; break; } default: - kind = "HCI_UNKNOWN"; + info = "HCI_UNKNOWN"; break; } #if HCI_TRACE - printf("[% 8d] %s(%02x", mp_hal_ticks_ms(), kind, buf[0]); + printf("[% 8d] <%s(%02x", mp_hal_ticks_ms(), info, buf[0]); for (int i = 1; i < len; ++i) { printf(":%02x", buf[i]); } - printf(")\n"); + printf(")"); + if (parse && parse->was_hci_reset_evt) { + printf(" (reset)"); + } + if (applied_set_event_event_mask2_fix) { + printf(" (mask2 fix)"); + } + printf("\n"); + #else - (void)kind; + (void)info; #endif } -STATIC void tl_check_msg(volatile tl_list_node_t *head, unsigned int ch, parse_hci_info_t *parse) { - if (IPCC->C2TOC1SR & ch) { - // Message available on CH2 - volatile tl_list_node_t *cur = head->next; - bool free = false; - while (cur != head) { - tl_parse_hci_msg((uint8_t *)cur->body, parse); - volatile tl_list_node_t *next = tl_list_unlink(cur); - if ((void *)&ipcc_membuf_memmgr_evt_pool[0] <= (void *)cur - && (void *)cur < (void *)&ipcc_membuf_memmgr_evt_pool[MP_ARRAY_SIZE(ipcc_membuf_memmgr_evt_pool)]) { - // Place memory back in free pool - tl_list_append(&ipcc_mem_memmgr_free_buf_queue, cur); - free = true; - } - cur = next; - } - if (free) { - // Notify change in free pool - IPCC->C1SCR = IPCC_CH_MM << 16; +STATIC void tl_process_msg(volatile tl_list_node_t *head, unsigned int ch, parse_hci_info_t *parse) { + volatile tl_list_node_t *cur = head->next; + bool added_to_free_queue = false; + while (cur != head) { + tl_parse_hci_msg((uint8_t *)cur->body, parse); + + volatile tl_list_node_t *next = tl_list_unlink(cur); + + // If this node is allocated from the memmgr event pool, then place it into the free buffer. + if ((uint8_t *)cur >= ipcc_membuf_memmgr_evt_pool && (uint8_t *)cur < ipcc_membuf_memmgr_evt_pool + sizeof(ipcc_membuf_memmgr_evt_pool)) { + // Place memory back in free pool. + tl_list_append(&ipcc_mem_memmgr_free_buf_queue, cur); + added_to_free_queue = true; } - // Clear receive channel - IPCC->C1SCR = ch; + + cur = next; + } + + if (added_to_free_queue) { + // Notify change in free pool. + LL_C1_IPCC_SetFlag_CHx(IPCC, IPCC_CH_MM); + } +} + +STATIC void tl_check_msg(volatile tl_list_node_t *head, unsigned int ch, parse_hci_info_t *parse) { + if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, ch)) { + tl_process_msg(head, ch, parse); + + // Clear receive channel. + LL_C1_IPCC_ClearFlag_CHx(IPCC, ch); + } +} + +STATIC void tl_check_msg_ble(volatile tl_list_node_t *head, parse_hci_info_t *parse) { + if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { + tl_process_msg(head, IPCC_CH_BLE, parse); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, IPCC_CH_BLE); } } @@ -358,25 +376,51 @@ STATIC void tl_hci_cmd(uint8_t *cmd, unsigned int ch, uint8_t hdr, uint16_t opco cmd[10] = opcode >> 8; cmd[11] = len; memcpy(&cmd[12], buf, len); - // IPCC indicate - IPCC->C1SCR = ch << 16; + + // Indicate that this channel is ready. + LL_C1_IPCC_SetFlag_CHx(IPCC, ch); } -STATIC void tl_sys_wait_resp(const uint8_t *buf, unsigned int ch) { - if (ipcc_wait_ack(ch, 250) == 0) { - tl_parse_hci_msg(buf, NULL); +STATIC int tl_sys_wait_ack(const uint8_t *buf) { + uint32_t t0 = mp_hal_ticks_ms(); + + // C2 will clear this bit to acknowledge the request. + while (LL_C1_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_SYS)) { + if (mp_hal_ticks_ms() - t0 > SYS_ACK_TIMEOUT_MS) { + printf("tl_sys_wait_ack: timeout\n"); + return -MP_ETIMEDOUT; + } } + + // C1-to-C2 bit cleared, so process (but ignore) the response. + tl_parse_hci_msg(buf, NULL); + return 0; } STATIC void tl_sys_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd((uint8_t *)&ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, len, buf); - tl_sys_wait_resp((uint8_t *)&ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS); + tl_hci_cmd(ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, len, buf); + tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf); +} + +STATIC int tl_ble_wait_resp(void) { + uint32_t t0 = mp_hal_ticks_ms(); + while (!LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { + if (mp_hal_ticks_ms() - t0 > BLE_ACK_TIMEOUT_MS) { + printf("tl_ble_wait_resp: timeout\n"); + return -MP_ETIMEDOUT; + } + } + + // C2 set IPCC flag. + tl_check_msg_ble(&ipcc_mem_ble_evt_queue, NULL); + return 0; } +// Synchronously send a BLE command. STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd((uint8_t *)&ipcc_membuf_ble_cmd_buf[0], IPCC_CH_BLE, 0x01, opcode, len, buf); - ipcc_wait_msg(IPCC_CH_BLE, 250); - tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); + tl_hci_cmd(ipcc_membuf_ble_cmd_buf, IPCC_CH_BLE, HCI_KIND_BT_CMD, opcode, len, buf); + tl_ble_wait_resp(); + } /******************************************************************************/ @@ -445,7 +489,7 @@ void rfcore_ble_init(void) { // Clear any outstanding messages from ipcc_init tl_check_msg(&ipcc_mem_sys_queue, IPCC_CH_SYS, NULL); - tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); + tl_check_msg_ble(&ipcc_mem_ble_evt_queue, NULL); // Configure and reset the BLE controller tl_sys_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_BLE_INIT), sizeof(ble_init_params), (const uint8_t *)&ble_init_params); @@ -456,7 +500,7 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { DEBUG_printf("rfcore_ble_hci_cmd\n"); #if HCI_TRACE - printf("[% 8d] HCI_CMD(%02x", mp_hal_ticks_ms(), src[0]); + printf("[% 8d] >HCI_CMD(%02x", mp_hal_ticks_ms(), src[0]); for (int i = 1; i < len; ++i) { printf(":%02x", src[i]); } @@ -465,10 +509,10 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { tl_list_node_t *n; uint32_t ch; - if (src[0] == 0x01) { + if (src[0] == HCI_KIND_BT_CMD) { n = (tl_list_node_t *)&ipcc_membuf_ble_cmd_buf[0]; ch = IPCC_CH_BLE; - } else if (src[0] == 0x02) { + } else if (src[0] == HCI_KIND_BT_ACL) { n = (tl_list_node_t *)&ipcc_membuf_ble_hci_acl_data_buf[0]; ch = IPCC_CH_HCI_ACL; } else { @@ -480,13 +524,13 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { n->prev = n; memcpy(n->body, src, len); - // IPCC indicate - IPCC->C1SCR = ch << 16; + // IPCC indicate. + LL_C1_IPCC_SetFlag_CHx(IPCC, ch); } void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env) { parse_hci_info_t parse = { cb, env, false }; - tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, &parse); + tl_check_msg_ble(&ipcc_mem_ble_evt_queue, &parse); // Intercept HCI_Reset events and reconfigure the controller following the reset if (parse.was_hci_reset_evt) { From e2390d5a2f4f18912878596965b0c794ac153cb4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 1 Sep 2020 14:13:44 +1000 Subject: [PATCH 267/352] stm32/rfcore: Enable RX IRQ on BLE IPCC channel for better performance. Before this change there was up to a 128ms delay on incoming payloads from CPU2 as it was polled by SysTick. Now the RX IRQ immediately schedules the PendSV. --- ports/stm32/rfcore.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 5630ea7132db8..ca2ad747c4bb8 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -164,6 +164,9 @@ STATIC uint8_t ipcc_membuf_ble_cs_buf[272]; // mem2 STATIC tl_list_node_t ipcc_mem_ble_evt_queue; // mem1 STATIC uint8_t ipcc_membuf_ble_hci_acl_data_buf[272]; // mem2 +// Set by the RX IRQ handler on incoming HCI payload. +STATIC volatile bool had_ble_irq = false; + /******************************************************************************/ // Transport layer linked list @@ -209,6 +212,13 @@ void ipcc_init(uint32_t irq_pri) { // Start IPCC peripheral __HAL_RCC_IPCC_CLK_ENABLE(); + // Enable receive IRQ on the BLE channel. + LL_C1_IPCC_EnableIT_RXO(IPCC); + LL_C1_IPCC_DisableReceiveChannel(IPCC, LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); + LL_C1_IPCC_EnableReceiveChannel(IPCC, IPCC_CH_BLE); + NVIC_SetPriority(IPCC_C1_RX_IRQn, irq_pri); + HAL_NVIC_EnableIRQ(IPCC_C1_RX_IRQn); + // Device info table will be populated by FUS/WS on CPU2 boot. // Populate system table @@ -360,10 +370,10 @@ STATIC void tl_check_msg(volatile tl_list_node_t *head, unsigned int ch, parse_h } STATIC void tl_check_msg_ble(volatile tl_list_node_t *head, parse_hci_info_t *parse) { - if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { + if (had_ble_irq) { tl_process_msg(head, IPCC_CH_BLE, parse); - LL_C1_IPCC_ClearFlag_CHx(IPCC, IPCC_CH_BLE); + had_ble_irq = false; } } @@ -404,7 +414,7 @@ STATIC void tl_sys_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) STATIC int tl_ble_wait_resp(void) { uint32_t t0 = mp_hal_ticks_ms(); - while (!LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { + while (!had_ble_irq) { if (mp_hal_ticks_ms() - t0 > BLE_ACK_TIMEOUT_MS) { printf("tl_ble_wait_resp: timeout\n"); return -MP_ETIMEDOUT; @@ -553,4 +563,26 @@ void rfcore_ble_set_txpower(uint8_t level) { tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_SET_TX_POWER), 2, buf); } +// IPCC IRQ Handlers +void IPCC_C1_TX_IRQHandler(void) { + IRQ_ENTER(IPCC_C1_TX_IRQn); + IRQ_EXIT(IPCC_C1_TX_IRQn); +} + +void IPCC_C1_RX_IRQHandler(void) { + IRQ_ENTER(IPCC_C1_RX_IRQn); + + if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { + had_ble_irq = true; + + LL_C1_IPCC_ClearFlag_CHx(IPCC, IPCC_CH_BLE); + + // Schedule PENDSV to process incoming HCI payload. + extern void mp_bluetooth_hci_poll_wrapper(uint32_t ticks_ms); + mp_bluetooth_hci_poll_wrapper(0); + } + + IRQ_EXIT(IPCC_C1_RX_IRQn); +} + #endif // defined(STM32WB) From 632e3b7acc51b3b28f310ef5e23b0983c8e8b700 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 28 Aug 2020 15:51:15 +1000 Subject: [PATCH 268/352] stm32/boards/NUCLEO_WB55: Add Python helper code for rfcore. This allows prototyping rfcore.c improvements from Python. This was mostly written by @dpgeorge with small modifications to work after rfcore_init() by @jimmo. --- ports/stm32/boards/NUCLEO_WB55/rfcore.py | 347 +++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 ports/stm32/boards/NUCLEO_WB55/rfcore.py diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore.py b/ports/stm32/boards/NUCLEO_WB55/rfcore.py new file mode 100644 index 0000000000000..e612499a7829f --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore.py @@ -0,0 +1,347 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2020 Damien P. George +# Copyright (c) 2020 Jim Mussared +# +# 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. + +# This script provides some helpers to allow Python code to access the IPCC +# mechanism in the WB55, and works with the memory layout configured in +# ports/stm32/rfcore.c -- i.e. it expects that rfcore_init() has been run. + +# At this stage this is useful for debugging, but can be extended to support +# FUS/WS firmware updates. +# e.g. +# ../../tools/pyboard.py --device /dev/ttyACM0 boards/NUCLEO_WB55/rfcore.py +# to print out SRAM2A, register state and FUS/WS info. + +from machine import mem8, mem16, mem32 +import time, struct, uctypes +import stm + + +class Flash: + FLASH_KEY1 = 0x45670123 + FLASH_KEY2 = 0xCDEF89AB + + def wait_not_busy(self): + while mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: + machine.idle() + + def unlock(self): + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY1 + mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY2 + + def lock(self): + mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + + def erase_page(self, page): + print("erase", page) + assert 0 <= page <= 255 # 1MiB range (4k page) + self.wait_not_busy() + cr = page << 3 | 1 << 1 # PNB # PER + mem32[stm.FLASH + stm.FLASH_CR] = cr + mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT + self.wait_not_busy() + mem32[stm.FLASH + stm.FLASH_CR] = 0 + + def write(self, addr, buf): + assert len(buf) % 4 == 0 + self.wait_not_busy() + cr = 1 << 0 # PG + mem32[stm.FLASH + stm.FLASH_CR] = cr + buf_addr = uctypes.addressof(buf) + off = 0 + while off < len(buf): + mem32[addr + off] = mem32[buf_addr + off] + off += 4 + if off % 8 == 0: + self.wait_not_busy() + if off % 8: + mem32[addr + off] = 0 + self.wait_not_busy() + mem32[stm.FLASH + stm.FLASH_CR] = 0 + + +def copy_file_to_flash(filename, addr): + flash = Flash() + flash.unlock() + try: + with open(filename, "rb") as f: + buf = bytearray(4096) + while 1: + sz = f.readinto(buf) + if sz == 0: + break + print("write", hex(addr), sz) + flash.erase_page((addr - 0x08000000) // 4096) + print("done e") + flash.write(addr, buf) + print("done") + addr += 4096 + finally: + flash.lock() + + +SRAM2A_BASE = const(0x2003_0000) + +# for vendor OGF +OGF_VENDOR = const(0x3F) +OCF_FUS_GET_STATE = const(0x52) +OCF_FUS_FW_UPGRADE = const(0x54) +OCF_FUS_FW_DELETE = const(0x55) +OCF_FUS_START_WS = const(0x5A) +OCF_BLE_INIT = const(0x66) + + +@micropython.asm_thumb +def asm_sev_wfe(): + data(2, 0xBF40) # sev + data(2, 0xBF20) # wfe + + +TABLE_DEVICE_INFO = const(0) +TABLE_BLE = const(1) +TABLE_SYS = const(3) +TABLE_MEM_MANAGER = const(4) + +CHANNEL_BLE = const(1) +CHANNEL_SYS = const(2) +CHANNEL_TRACES = const(4) +CHANNEL_ACL = const(6) + +INDICATOR_HCI_COMMAND = const(0x01) +INDICATOR_HCI_EVENT = const(0x04) +INDICATOR_FUS_COMMAND = const(0x10) +INDICATOR_FUS_RESPONSE = const(0x11) +INDICATOR_FUS_EVENT = const(0x12) + +MAGIC_FUS_ACTIVE = const(0xA94656B9) + + +def get_ipccdba(): + return mem32[stm.FLASH + stm.FLASH_IPCCBR] & 0x3FFF + + +def get_ipcc_table(table): + return mem32[SRAM2A_BASE + get_ipccdba() + table * 4] + + +def get_ipcc_table_word(table, offset): + return mem32[get_ipcc_table(table) + offset * 4] & 0xFFFFFFFF + + +def get_ipcc_table_byte(table, offset): + return mem8[get_ipcc_table(table) + offset] & 0xFF + + +def sram2a_dump(num_words=64, width=8): + print("SRAM2A @%08x" % SRAM2A_BASE) + for i in range((num_words + width - 1) // width): + print(" %04x " % (i * 4 * width), end="") + for j in range(width): + print(" %08x" % (mem32[SRAM2A_BASE + (i * width + j) * 4] & 0xFFFFFFFF), end="") + print() + + +SYS_CMD_BUF = 0 # next*,prev*,type8,...; 272 bytes +SYS_SYS_QUEUE = 0 # next*,prev* + +MM_BLE_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes +MM_SYS_SPARE_EVT_BUF = 0 # next*,prev*; 272 bytes +MM_BLE_POOL = 0 # ? +MM_BLE_POOL_SIZE = 0 # ? +MM_FREE_BUF_QUEUE = 0 # next*,prev* +MM_EV_POOL = 0 # ? +MM_EV_POOL_SIZE = 0 # ? + +BLE_CMD_BUF = 0 +BLE_CS_BUF = 0 +BLE_EVT_QUEUE = 0 +BLE_HCI_ACL_DATA_BUF = 0 + + +def ipcc_init(): + global SYS_CMD_BUF, SYS_SYS_QUEUE + SYS_CMD_BUF = get_ipcc_table_word(TABLE_SYS, 0) + SYS_SYS_QUEUE = get_ipcc_table_word(TABLE_SYS, 1) + + global MM_BLE_SPARE_EVT_BUF, MM_SYS_SPARE_EVT_BUF, MM_BLE_POOL, MM_BLE_POOL_SIZE, MM_FREE_BUF_QUEUE, MM_EV_POOL, MM_EV_POOL_SIZE + MM_BLE_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 0) + MM_SYS_SPARE_EVT_BUF = get_ipcc_table_word(TABLE_MEM_MANAGER, 1) + MM_BLE_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 2) + MM_BLE_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 3) + MM_FREE_BUF_QUEUE = get_ipcc_table_word(TABLE_MEM_MANAGER, 4) + MM_EV_POOL = get_ipcc_table_word(TABLE_MEM_MANAGER, 5) + MM_EV_POOL_SIZE = get_ipcc_table_word(TABLE_MEM_MANAGER, 6) + + global BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE, BLE_HCI_ACL_DATA_BUF + BLE_CMD_BUF = get_ipcc_table_word(TABLE_BLE, 0) + BLE_CS_BUF = get_ipcc_table_word(TABLE_BLE, 1) + BLE_EVT_QUEUE = get_ipcc_table_word(TABLE_BLE, 2) + BLE_HCI_ACL_DATA_BUF = get_ipcc_table_word(TABLE_BLE, 3) + + print("IPCC initialised") + print("SYS: 0x%08x 0x%08x" % (SYS_CMD_BUF, SYS_SYS_QUEUE)) + print("BLE: 0x%08x 0x%08x 0x%08x" % (BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE)) + + +def tl_list_init(addr): + mem32[addr] = addr # next + mem32[addr + 4] = addr # prev + + +def tl_list_append(head, n): + sram2a_dump(1024) + print("Appending 0x%08x to 0x%08x" % (head, n)) + # item->next = head + mem32[n] = head + # item->prev = head->prev + mem32[n + 4] = mem32[head + 4] + # head->prev->next = item + mem32[mem32[head + 4]] = n + # head->prev = item + mem32[head + 4] = n + + +def tl_list_unlink(n): + # next = item->next + next = mem32[n] + # prev = item->prev + prev = mem32[n + 4] + # prev->next = item->next + mem32[prev] = next + # item->next->prev = prev + mem32[next + 4] = prev + + return next + + +def tl_list_dump(head): + print( + "list(%08x, %08x, %08x):" % (head, mem32[head] & 0xFFFFFFFF, mem32[head + 4] & 0xFFFFFFFF), + end="", + ) + cur = mem32[head] + while cur != head: + print(" %08x" % (cur & 0xFFFFFFFF), end="") + cur = mem32[cur] + print() + + +def fus_active(): + return get_ipcc_table_word(TABLE_DEVICE_INFO, 0) == MAGIC_FUS_ACTIVE + + +def info(): + sfr = mem32[stm.FLASH + stm.FLASH_SFR] + srrvr = mem32[stm.FLASH + stm.FLASH_SRRVR] + + print("IPCCDBA : 0x%08x" % (get_ipccdba() & 0x3FFF)) + print("DDS : %r" % bool(sfr & (1 << 12))) + print("FSD : %r" % bool(sfr & (1 << 8))) + print("SFSA : 0x%08x" % (sfr & 0xFF)) + print("C2OPT : %r" % bool(srrvr & (1 << 31))) + print("NBRSD : %r" % bool(srrvr & (1 << 30))) + print("SNBRSA : 0x%08x" % ((srrvr >> 25) & 0x1F)) + print("BRSD : %r" % bool(srrvr & (1 << 23))) + print("SBRSA : 0x%08x" % ((srrvr >> 18) & 0x1F)) + print("SBRV : 0x%08x" % (srrvr & 0x3FFFF)) + + +def dev_info(): + def dump_version(offset): + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) + print( + "0x%08x (%u.%u.%u.%u.%u)" + % (x, x >> 24, x >> 16 & 0xFF, x >> 8 & 0xFF, x >> 4 & 0xF, x & 0xF) + ) + + def dump_memory_size(offset): + x = get_ipcc_table_word(TABLE_DEVICE_INFO, offset) + print( + "0x%08x (SRAM2b=%uk SRAM2a=%uk flash=%uk)" + % (x, x >> 24, x >> 16 & 0xFF, (x & 0xFF) * 4) + ) + + print("Device information table @%08x:" % get_ipcc_table(TABLE_DEVICE_INFO)) + if fus_active(): + # layout when running FUS + print("FUS is active") + print("state : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 0)) + print("last FUS active state : 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 5)) + print("last wireless stack state: 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 6)) + print("cur wireless stack type : 0x%02x" % get_ipcc_table_byte(TABLE_DEVICE_INFO, 7)) + print("safe boot version : ", end="") + dump_version(2) + print("FUS version : ", end="") + dump_version(3) + print("FUS memory size : ", end="") + dump_memory_size(4) + print("wireless stack version : ", end="") + dump_version(5) + print("wireless stack mem size : ", end="") + dump_memory_size(6) + print("wireless FW-BLE info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + print("wireless FW-thread info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 8)) + print( + "UID64 : 0x%08x 0x%08x" + % ( + get_ipcc_table_word(TABLE_DEVICE_INFO, 9), + get_ipcc_table_word(TABLE_DEVICE_INFO, 10), + ) + ) + print("device ID : 0x%04x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 11)) + else: + # layout when running WS + print("WS is active") + print("safe boot version : ", end="") + dump_version(0) + print("FUS version : ", end="") + dump_version(1) + print("FUS memory size : ", end="") + dump_memory_size(2) + print("FUS info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 3)) + print("wireless stack version : ", end="") + dump_version(4) + print("wireless stack mem size : ", end="") + dump_memory_size(5) + print("wireless stack info : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + print("wireless reserved : 0x%08x" % get_ipcc_table_word(TABLE_DEVICE_INFO, 7)) + + +def ipcc_state(): + print("IPCC:") + print(" C1CR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1CR] & 0xFFFFFFFF), end="") + print(" C2CR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2CR] & 0xFFFFFFFF)) + print(" C1MR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1MR] & 0xFFFFFFFF), end="") + print(" C2MR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2MR] & 0xFFFFFFFF)) + # these always read 0 + # print(' C1SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C1SCR] & 0xffffffff), end='') + # print(' C2SCR: 0x%08x' % (mem32[stm.IPCC + stm.IPCC_C2SCR] & 0xffffffff)) + print(" C1TOC2SR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C1TOC2SR] & 0xFFFFFFFF), end="") + print(" C2TOC1SR: 0x%08x" % (mem32[stm.IPCC + stm.IPCC_C2TOC1SR] & 0xFFFFFFFF)) + + +sram2a_dump(264) +ipcc_init() +info() +dev_info() From 5f50568b1f7ab4f917b8d782d643ac55612910dd Mon Sep 17 00:00:00 2001 From: Albort Xue Date: Tue, 7 Apr 2020 15:54:01 +0800 Subject: [PATCH 269/352] mimxrt/boards: Add MIMXRT1064_EVK board. --- ports/mimxrt/Makefile | 2 +- ports/mimxrt/boards/MIMXRT1064.ld | 8 + .../evkmimxrt1064_flexspi_nor_config.h | 268 ++++++++++++++++++ .../boards/MIMXRT1064_EVK/flash_config.c | 49 ++++ .../boards/MIMXRT1064_EVK/mpconfigboard.h | 8 + .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 9 + ports/mimxrt/boards/MIMXRT1064_EVK/pins.c | 33 +++ ports/mimxrt/boards/MIMXRT1064_EVK/pins.h | 30 ++ 8 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 ports/mimxrt/boards/MIMXRT1064.ld create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/pins.c create mode 100644 ports/mimxrt/boards/MIMXRT1064_EVK/pins.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index c4ea70128e0ad..85e77b978e9f0 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -40,7 +40,7 @@ INC += -I$(TOP)/lib/tinyusb/hw INC += -I$(TOP)/lib/tinyusb/hw/bsp/teensy_40 CFLAGS_MCU = -mtune=cortex-m7 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 -CFLAGS = $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 -nostdlib -mthumb $(CFLAGS_MCU) +CFLAGS += $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 -nostdlib -mthumb $(CFLAGS_MCU) CFLAGS += -DCPU_$(MCU_SERIES) -DCPU_$(MCU_VARIANT) CFLAGS += -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld new file mode 100644 index 0000000000000..bf37c21802581 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -0,0 +1,8 @@ +/* 24kiB stack. */ +__stack_size__ = 0x6000; +_estack = __StackTop; +_sstack = __StackLimit; + +/* Use second OCRAM bank for GC heap. */ +_gc_heap_start = ORIGIN(m_data2); +_gc_heap_end = ORIGIN(m_data2) + LENGTH(m_data2); \ No newline at end of file diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h new file mode 100644 index 0000000000000..efdfe583fb82c --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h @@ -0,0 +1,268 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1064_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1064_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.0. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_120MHz = 7, + kFlexSpiSerialClk_133MHz = 8, + kFlexSpiSerialClk_166MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1064_FLEXSPI_NOR_CONFIG__ */ diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c b/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c new file mode 100644 index 0000000000000..bfb1c2d59aa25 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c @@ -0,0 +1,49 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "evkmimxrt1064_flexspi_nor_config.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_100MHz, + .sflashA1Size = 8u * 1024u * 1024u, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h new file mode 100644 index 0000000000000..e05c8824d26ce --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -0,0 +1,8 @@ +#define MICROPY_HW_BOARD_NAME "i.MX RT1064 EVK" +#define MICROPY_HW_MCU_NAME "MIMXRT1064DVL6A" + + +// MIMXRT1064_EVK has 1 user LED +#define MICROPY_HW_LED1_PIN (GPIO_AD_B0_09) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk new file mode 100644 index 0000000000000..700988919146d --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -0,0 +1,9 @@ +MCU_SERIES = MIMXRT1064 +MCU_VARIANT = MIMXRT1064DVL6A + +JLINK_PATH ?= /media/RT1064-EVK/ + +CFLAGS += -DBOARD_FLASH_SIZE=0x400000 + +deploy: $(BUILD)/firmware.bin + cp $< $(JLINK_PATH) diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/pins.c b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.c new file mode 100644 index 0000000000000..d5da9c6f99275 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 NXP + * + * 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 "pin.h" + +static pin_af_obj_t GPIO_AD_B0_09_af[] = { + PIN_AF(GPIO1_IO09, PIN_AF_MODE_ALT5, GPIO1, 0x10B0U), +}; + +pin_obj_t GPIO_AD_B0_09 = PIN(GPIO_AD_B0_09, GPIO1, 9, GPIO_AD_B0_09_af); diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/pins.h b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.h new file mode 100644 index 0000000000000..baef51c6c8612 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 NXP + * + * 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. + */ + +// NOTE: pins.h shall only be included in in pin.h +// hence no include guards are needed since they will be provided by pin.h + +extern pin_obj_t GPIO_AD_B0_09; From 4f2fe346239b6b3f720708021c868a88d97bb16d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 16:12:09 +1000 Subject: [PATCH 270/352] tools/mpy-tool.py: Fix merge of multiple mpy files to POP_TOP correctly. MP_BC_CALL_FUNCTION will leave the result on the Python stack, so that result must be discarded by MP_BC_POP_TOP. Signed-off-by: Damien George --- tools/mpy-tool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 1ac6c93d7d8b2..de7cfe5d631c7 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -938,7 +938,7 @@ def merge_mpy(raw_codes, output_file): merged_mpy.extend(header) bytecode = bytearray() - bytecode_len = 6 + len(raw_codes) * 4 + 2 + bytecode_len = 6 + len(raw_codes) * 5 + 2 bytecode.append(bytecode_len << 2) # kind and length bytecode.append(0b00000000) # signature prelude bytecode.append(0b00001000) # size prelude @@ -947,7 +947,7 @@ def merge_mpy(raw_codes, output_file): for idx in range(len(raw_codes)): bytecode.append(0x32) # MP_BC_MAKE_FUNCTION bytecode.append(idx) # index raw code - bytecode.extend(b"\x34\x00") # MP_BC_CALL_FUNCTION, 0 args + bytecode.extend(b"\x34\x00\x59") # MP_BC_CALL_FUNCTION, 0 args, MP_BC_POP_TOP bytecode.extend(b"\x51\x63") # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE bytecode.append(0) # n_obj From 75344af4cada48b8b044ae35d0e62c38c601d899 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 8 Sep 2020 13:52:13 +1000 Subject: [PATCH 271/352] nrf/main: Make mp_builtin_open signature match that in py/builtin.h. Signed-off-by: Damien George --- ports/nrf/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 670c88e7cad9d..86ba83342b9f3 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -280,10 +280,10 @@ mp_import_stat_t mp_import_stat(const char *path) { return uos_mbfs_import_stat(path); } -STATIC mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args) { +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { return uos_mbfs_open(n_args, args); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, mp_builtin_open); +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #else // use dummy functions - no filesystem available @@ -295,7 +295,7 @@ mp_import_stat_t mp_import_stat(const char *path) { return MP_IMPORT_STAT_NO_EXIST; } -STATIC mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { mp_raise_OSError(MP_EPERM); } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); From 27e117307d7d5b63955940866e6094bedb7d299e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 8 Sep 2020 13:52:56 +1000 Subject: [PATCH 272/352] nrf: Remove unnecessary includes of mpconfigport.h and its header guard. The mpconfigport.h file is an internal header and should only ever be included once by mpconfig.h. Signed-off-by: Damien George --- ports/nrf/drivers/bluetooth/ble_drv.c | 1 - ports/nrf/modules/uos/microbitfs.c | 1 - ports/nrf/mpconfigport.h | 5 ----- 3 files changed, 7 deletions(-) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 1a64cdfbd24b9..3a15025cb4978 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -32,7 +32,6 @@ #include "py/runtime.h" #include "ble_drv.h" -#include "mpconfigport.h" #include "nrf_sdm.h" #include "ble_gap.h" #include "ble.h" // sd_ble_uuid_encode diff --git a/ports/nrf/modules/uos/microbitfs.c b/ports/nrf/modules/uos/microbitfs.c index 7f16372cf2224..e8628f4945c20 100644 --- a/ports/nrf/modules/uos/microbitfs.c +++ b/ports/nrf/modules/uos/microbitfs.c @@ -36,7 +36,6 @@ #include "py/stream.h" #include "py/runtime.h" #include "extmod/vfs.h" -#include "mpconfigport.h" #if MICROPY_MBFS diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 1197df016cce9..76bb6d7bdf0df 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -24,9 +24,6 @@ * THE SOFTWARE. */ -#ifndef NRF5_MPCONFIGPORT_H__ -#define NRF5_MPCONFIGPORT_H__ - #include #if defined(NRF51822) @@ -342,5 +339,3 @@ extern const struct _mp_obj_module_t ble_module; #include #define MICROPY_PIN_DEFS_PORT_H "pin_defs_nrf5.h" - -#endif From 547688c58cce3436082cc9338929c2699410c0e1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Sep 2020 20:43:07 +1000 Subject: [PATCH 273/352] stm32/usb: Don't nul pyb_hid_report_desc if MICROPY_HW_USB_HID disabled. So this code can be used if pyb_hid_report_desc is not included in the port's root pointer list. Signed-off-by: Damien George --- ports/stm32/usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 657d4cb6e7402..968b2999ced37 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -221,7 +221,9 @@ void pyb_usb_init0(void) { for (int i = 0; i < MICROPY_HW_USB_CDC_NUM; ++i) { usb_device.usbd_cdc_itf[i].attached_to_repl = false; } + #if MICROPY_HW_USB_HID MP_STATE_PORT(pyb_hid_report_desc) = MP_OBJ_NULL; + #endif pyb_usb_vcp_init0(); } From 709398daae14b3630de7050e08b9e77ea1232716 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Sep 2020 20:46:40 +1000 Subject: [PATCH 274/352] stm32/rtc.h: Include py/obj.h to make header self contained. Signed-off-by: Damien George --- ports/stm32/rtc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/rtc.h b/ports/stm32/rtc.h index d3840c1b73592..5c454b3e3937f 100644 --- a/ports/stm32/rtc.h +++ b/ports/stm32/rtc.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_STM32_RTC_H #define MICROPY_INCLUDED_STM32_RTC_H +#include "py/obj.h" + extern RTC_HandleTypeDef RTCHandle; extern const mp_obj_type_t pyb_rtc_type; From 8d5a40c86e384bf3cddb2f687374e0bb1ae6df7d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 11:04:46 +1000 Subject: [PATCH 275/352] py/objfloat: Fix handling of negative float to power of nan. Prior to this commit, pow(-2, float('nan')) would return (nan+nanj), or raise an exception on targets that don't support complex numbers. This is fixed to return simply nan, as CPython does. Signed-off-by: Damien George --- py/objfloat.c | 2 +- tests/float/inf_nan_arith.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/float/inf_nan_arith.py diff --git a/py/objfloat.c b/py/objfloat.c index 09b73c8cda525..f1e401eccecfe 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -293,7 +293,7 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t if (lhs_val == 0 && rhs_val < 0 && !isinf(rhs_val)) { goto zero_division_error; } - if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val)) { + if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val) && !isnan(rhs_val)) { #if MICROPY_PY_BUILTINS_COMPLEX return mp_obj_complex_binary_op(MP_BINARY_OP_POWER, lhs_val, 0, rhs_in); #else diff --git a/tests/float/inf_nan_arith.py b/tests/float/inf_nan_arith.py new file mode 100644 index 0000000000000..c27e38bc520be --- /dev/null +++ b/tests/float/inf_nan_arith.py @@ -0,0 +1,20 @@ +# Test behaviour of inf and nan in basic float operations + +inf = float("inf") +nan = float("nan") + +values = (-2, -1, 0, 1, 2, inf, nan) + +for x in values: + for y in values: + print(x, y) + print(" + - *", x + y, x - y, x * y) + try: + print(" /", x / y) + except ZeroDivisionError: + print(" / ZeroDivisionError") + try: + print(" ** pow", x ** y, pow(x, y)) + except ZeroDivisionError: + print(" ** pow ZeroDivisionError") + print(" == != < <= > >=", x == y, x != y, x < y, x <= y, x > y, x >= y) From 2e54d9d146b34d7ad00e4394c9767f4319244cdf Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 8 Sep 2020 15:22:34 +0200 Subject: [PATCH 276/352] py: Fix handling of NaN in certain pow implementations. Adds a new compile-time option MICROPY_PY_MATH_POW_FIX_NAN for use with toolchains that don't handle pow-of-NaN correctly. --- ports/windows/mpconfigport.h | 2 ++ py/modmath.c | 12 ++++++++++++ py/mpconfig.h | 5 +++++ py/objfloat.c | 6 ++++++ tests/float/math_domain.py | 2 +- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 30389c700eac3..faf10752ee203 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -236,6 +236,8 @@ extern const struct _mp_obj_module_t mp_module_time; #define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1) #ifdef _WIN64 #define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1) +#else +#define MICROPY_PY_MATH_POW_FIX_NAN (1) #endif #endif diff --git a/py/modmath.c b/py/modmath.c index b312eeb3d2c01..b7948f39e7c91 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -98,7 +98,19 @@ mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { // sqrt(x): returns the square root of x MATH_FUN_1(sqrt, sqrt) // pow(x, y): returns x to the power of y +#if MICROPY_PY_MATH_POW_FIX_NAN +mp_float_t pow_func(mp_float_t x, mp_float_t y) { + // pow(base, 0) returns 1 for any base, even when base is NaN + // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN + if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) { + return MICROPY_FLOAT_CONST(1.0); + } + return MICROPY_FLOAT_C_FUN(pow)(x, y); +} +MATH_FUN_2(pow, pow_func) +#else MATH_FUN_2(pow, pow) +#endif // exp(x) MATH_FUN_1(exp, exp) #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS diff --git a/py/mpconfig.h b/py/mpconfig.h index ae4cabfdcf0f2..12517316c6c80 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1160,6 +1160,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0) #endif +// Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN. +#ifndef MICROPY_PY_MATH_POW_FIX_NAN +#define MICROPY_PY_MATH_POW_FIX_NAN (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (0) diff --git a/py/objfloat.c b/py/objfloat.c index f1e401eccecfe..451609492e5c1 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -300,6 +300,12 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t mp_raise_ValueError(MP_ERROR_TEXT("complex values not supported")); #endif } + #if MICROPY_PY_MATH_POW_FIX_NAN // Also see modmath.c. + if (lhs_val == MICROPY_FLOAT_CONST(1.0) || rhs_val == MICROPY_FLOAT_CONST(0.0)) { + lhs_val = MICROPY_FLOAT_CONST(1.0); + break; + } + #endif lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; case MP_BINARY_OP_DIVMOD: { diff --git a/tests/float/math_domain.py b/tests/float/math_domain.py index e63628cf5078a..0c25dc08b6649 100644 --- a/tests/float/math_domain.py +++ b/tests/float/math_domain.py @@ -38,7 +38,7 @@ # double argument functions for name, f, args in ( - ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))), + ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3), (nan, 0), (1, nan))), ("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))), ("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))), ("copysign", math.copysign, ()), From 373b400632bb5371f1e259983850814d2384e667 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Sep 2020 00:53:18 +1000 Subject: [PATCH 277/352] extmod/modussl_axtls: Reduce size of code that makes exception. Change in code size (for ports that use axtls) is: unix x64: -152 -0.030% [incl -160(data)] unix nanbox: -112 -0.025% [incl -96(data)] esp8266: -64 -0.009% GENERIC Signed-off-by: Damien George --- extmod/modussl_axtls.c | 96 ++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 0b0ce35fc8711..da5941a55b33e 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -55,53 +55,65 @@ struct ssl_args { STATIC const mp_obj_type_t ussl_socket_type; -// Table of errors -struct ssl_errs { - int16_t errnum; - const char *errstr; +// Table of error strings corresponding to SSL_xxx error codes. +STATIC const char *const ssl_error_tab1[] = { + "NOT_OK", + "DEAD", + "CLOSE_NOTIFY", + "EAGAIN", }; -STATIC const struct ssl_errs ssl_error_tab[] = { - { SSL_NOT_OK, "NOT_OK" }, - { SSL_ERROR_DEAD, "DEAD" }, - { SSL_CLOSE_NOTIFY, "CLOSE_NOTIFY" }, - { SSL_EAGAIN, "EAGAIN" }, - { SSL_ERROR_CONN_LOST, "CONN_LOST" }, - { SSL_ERROR_RECORD_OVERFLOW, "RECORD_OVERFLOW" }, - { SSL_ERROR_SOCK_SETUP_FAILURE, "SOCK_SETUP_FAILURE" }, - { SSL_ERROR_INVALID_HANDSHAKE, "INVALID_HANDSHAKE" }, - { SSL_ERROR_INVALID_PROT_MSG, "INVALID_PROT_MSG" }, - { SSL_ERROR_INVALID_HMAC, "INVALID_HMAC" }, - { SSL_ERROR_INVALID_VERSION, "INVALID_VERSION" }, - { SSL_ERROR_UNSUPPORTED_EXTENSION, "UNSUPPORTED_EXTENSION" }, - { SSL_ERROR_INVALID_SESSION, "INVALID_SESSION" }, - { SSL_ERROR_NO_CIPHER, "NO_CIPHER" }, - { SSL_ERROR_INVALID_CERT_HASH_ALG, "INVALID_CERT_HASH_ALG" }, - { SSL_ERROR_BAD_CERTIFICATE, "BAD_CERTIFICATE" }, - { SSL_ERROR_INVALID_KEY, "INVALID_KEY" }, - { SSL_ERROR_FINISHED_INVALID, "FINISHED_INVALID" }, - { SSL_ERROR_NO_CERT_DEFINED, "NO_CERT_DEFINED" }, - { SSL_ERROR_NO_CLIENT_RENOG, "NO_CLIENT_RENOG" }, - { SSL_ERROR_NOT_SUPPORTED, "NOT_SUPPORTED" }, +STATIC const char *const ssl_error_tab2[] = { + "CONN_LOST", + "RECORD_OVERFLOW", + "SOCK_SETUP_FAILURE", + NULL, + "INVALID_HANDSHAKE", + "INVALID_PROT_MSG", + "INVALID_HMAC", + "INVALID_VERSION", + "UNSUPPORTED_EXTENSION", + "INVALID_SESSION", + "NO_CIPHER", + "INVALID_CERT_HASH_ALG", + "BAD_CERTIFICATE", + "INVALID_KEY", + NULL, + "FINISHED_INVALID", + "NO_CERT_DEFINED", + "NO_CLIENT_RENOG", + "NOT_SUPPORTED", }; STATIC NORETURN void ussl_raise_error(int err) { - for (size_t i = 0; i < MP_ARRAY_SIZE(ssl_error_tab); i++) { - if (ssl_error_tab[i].errnum == err) { - // construct string object - mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); - if (o_str == NULL) { - break; - } - o_str->base.type = &mp_type_str; - o_str->data = (const byte *)ssl_error_tab[i].errstr; - o_str->len = strlen((char *)o_str->data); - 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)); - } + MP_STATIC_ASSERT(SSL_NOT_OK - 3 == SSL_EAGAIN); + MP_STATIC_ASSERT(SSL_ERROR_CONN_LOST - 18 == SSL_ERROR_NOT_SUPPORTED); + + // Check if err corresponds to something in one of the error string tables. + const char *errstr = NULL; + if (SSL_NOT_OK >= err && err >= SSL_EAGAIN) { + errstr = ssl_error_tab1[SSL_NOT_OK - err]; + } else if (SSL_ERROR_CONN_LOST >= err && err >= SSL_ERROR_NOT_SUPPORTED) { + errstr = ssl_error_tab2[SSL_ERROR_CONN_LOST - err]; + } + + // Unknown error, just raise the error code. + if (errstr == NULL) { + mp_raise_OSError(err); + } + + // Construct string object. + mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + if (o_str == NULL) { + mp_raise_OSError(err); } - mp_raise_OSError(err); + o_str->base.type = &mp_type_str; + o_str->data = (const byte *)errstr; + o_str->len = strlen((char *)o_str->data); + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + + // Raise OSError(err, str). + 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)); } From 4b35aa5730cf26992453e3ce7cf74ced4128fe20 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 10 Sep 2020 12:22:49 +0200 Subject: [PATCH 278/352] tools: Write msvc-compatible frozen content. The msvc compiler doesn't accept zero-sized arrays so let the freezing process generate compatible C code in case no modules are found and the involved arrays are all empty. This doesn't affect the functionality in any way because those arrays only get accessed when mp_frozen_mpy_names contains names, i.e. when modules are actually found. --- tools/make-frozen.py | 4 ++-- tools/makemanifest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/make-frozen.py b/tools/make-frozen.py index a8706bf784fdc..bc35d383429d6 100755 --- a/tools/make-frozen.py +++ b/tools/make-frozen.py @@ -50,7 +50,7 @@ def module_name(f): for f, st in modules: print("%d," % st.st_size) -print("};") +print("0};") print("const char mp_frozen_str_content[] = {") for f, st in modules: @@ -82,4 +82,4 @@ def module_name(f): chrs.append('\\0"') print("".join(chrs)) -print("};") +print('"\\0"};') diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 377f245596fe9..9ef03682607dc 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -327,7 +327,7 @@ def main(): b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0\n" b"};\n" b'const char mp_frozen_mpy_names[1] = {"\\0"};\n' - b"const mp_raw_code_t *const mp_frozen_mpy_content[0] = {};\n" + b"const mp_raw_code_t *const mp_frozen_mpy_content[1] = {NULL};\n" ) # Generate output From 2a9ea69fa93b1b940b802f981f8835f6decdb085 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 10 Sep 2020 12:11:58 +0200 Subject: [PATCH 279/352] windows/msvc: Support freezing modules. Support freezing modules via manifest.py for consistency with the other ports. In essence this comes down to calling makemanifest.py and adding the resulting .c file to the build. Note the file with preprocessed qstrs has been renamed to match what makemanifest.py expects and which is also the name all other ports use. --- ports/windows/micropython.vcxproj | 2 +- ports/windows/msvc/genhdr.targets | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index f70fe96158d07..73a837a8408ab 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -104,7 +104,7 @@ - + diff --git a/ports/windows/msvc/genhdr.targets b/ports/windows/msvc/genhdr.targets index 327f922e4a162..3af0ea263699b 100644 --- a/ports/windows/msvc/genhdr.targets +++ b/ports/windows/msvc/genhdr.targets @@ -55,7 +55,7 @@ using(var outFile = System.IO.File.CreateText(OutputFile)) { - + $([System.String]::new('%(FullPath)').Replace('$(PyBaseDir)', '$(DestDir)qstr\')) @@ -101,8 +101,8 @@ using(var outFile = System.IO.File.CreateText(OutputFile)) { $(QstrGen).tmp - - + + @@ -115,6 +115,17 @@ using(var outFile = System.IO.File.CreateText(OutputFile)) { + + + + + MICROPY_MODULE_FROZEN_MPY=1;MICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool;%(PreprocessorDefinitions) + + + + + + From 5b94c610971334019e7301026e0fbbf5950e094d Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 10 Sep 2020 14:13:25 +0200 Subject: [PATCH 280/352] windows/Makefile: Support freezing modules. Alter the build flags as needed to support freezing modules with a manifest. This makes freezing works just like it does for e.g. the unix port. --- ports/windows/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/windows/Makefile b/ports/windows/Makefile index b2d11872ed3a6..3bfbc18304818 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -58,4 +58,9 @@ SRC_QSTR += $(SRC_C) # SRC_QSTR SRC_QSTR_AUTO_DEPS += +ifneq ($(FROZEN_MANIFEST),) +CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool -DMICROPY_MODULE_FROZEN_MPY=1 -DMPZ_DIG_SIZE=16 +MPY_CROSS_FLAGS += -mcache-lookup-bc +endif + include $(TOP)/py/mkrules.mk From 70bec41089eef26122d6c2cbd06bf359bc0ab253 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 10 Sep 2020 13:21:21 +0200 Subject: [PATCH 281/352] windows: Show test failures in the Appveyor builds. --- ports/windows/.appveyor.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index 2965543b05942..4d2d6bd11dc87 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -38,7 +38,15 @@ test_script: - ps: | cd (Join-Path $env:APPVEYOR_BUILD_FOLDER 'tests') & $env:MICROPY_CPYTHON3 run-tests + if ($LASTEXITCODE -ne 0) { + & $env:MICROPY_CPYTHON3 run-tests --print-failures + throw "Test failure" + } & $env:MICROPY_CPYTHON3 run-tests --via-mpy -d basics float micropython + if ($LASTEXITCODE -ne 0) { + & $env:MICROPY_CPYTHON3 run-tests --print-failures + throw "Test failure" + } # After the build/test phase for the MSVC build completes, # build and test with mingw-w64, release versions only. @@ -69,9 +77,11 @@ after_test: } & $env:MICROPY_CPYTHON3 $testArgs if ($LASTEXITCODE -ne 0) { - throw "$env:MSYSTEM tests exited with code $LASTEXITCODE" + & $env:MICROPY_CPYTHON3 run-tests --print-failures + throw "Test failure" } & $env:MICROPY_CPYTHON3 ($testArgs + @('--via-mpy', '-d', 'basics', 'float', 'micropython')) if ($LASTEXITCODE -ne 0) { - throw "$env:MSYSTEM mpy-cross tests exited with code $LASTEXITCODE" + & $env:MICROPY_CPYTHON3 run-tests --print-failures + throw "Test failure" } From 50efce81740901fee6f5191db36a48ada13ef6df Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 10 Sep 2020 11:31:01 +1000 Subject: [PATCH 282/352] esp32/mpconfigport.h: Remove duplicate uhashlib registration. --- ports/esp32/mpconfigport.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 7c09bdbd9218c..663fed3f683a4 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -200,7 +200,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uhashlib), (mp_obj_t)&mp_module_uhashlib }, \ #define MP_STATE_PORT MP_STATE_VM From 85f2b239d8ff8d29155c63799717e3f88b2f33f3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Sep 2020 17:22:28 +1000 Subject: [PATCH 283/352] py/showbc: Pass in an mp_print_t struct to all bytecode-print functions. So the output can be redirected if needed. Signed-off-by: Damien George --- py/bc.h | 8 +- py/emitglue.c | 2 +- py/showbc.c | 209 +++++++++++++++++++++++++------------------------- py/vm.c | 2 +- 4 files changed, 109 insertions(+), 112 deletions(-) diff --git a/py/bc.h b/py/bc.h index 0c19c516595ac..16f314e19936f 100644 --- a/py/bc.h +++ b/py/bc.h @@ -226,10 +226,10 @@ const byte *mp_decode_uint_skip(const byte *ptr); mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); -void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); -void mp_bytecode_print2(const byte *code, size_t len, const mp_uint_t *const_table); -const byte *mp_bytecode_print_str(const byte *ip); -#define mp_bytecode_print_inst(code, const_table) mp_bytecode_print2(code, 1, const_table) +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); +void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_uint_t *const_table); +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip); +#define mp_bytecode_print_inst(print, code, const_table) mp_bytecode_print2(print, code, 1, const_table) // Helper macros to access pointer with least significant bits holding flags #define MP_TAGPTR_PTR(x) ((void *)((uintptr_t)(x) & ~((uintptr_t)3))) diff --git a/py/emitglue.c b/py/emitglue.c index 98320c426bc63..0ef708a3f3f56 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -92,7 +92,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif #if MICROPY_DEBUG_PRINTERS if (mp_verbose_flag >= 2) { - mp_bytecode_print(rc, code, len, const_table); + mp_bytecode_print(&mp_plat_print, rc, code, len, const_table); } #endif } diff --git a/py/showbc.c b/py/showbc.c index 4d6527fae2e45..8941dd7544487 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -32,9 +32,6 @@ #if MICROPY_DEBUG_PRINTERS -// redirect all printfs in this file to the platform print stream -#define printf(...) mp_printf(&mp_plat_print, __VA_ARGS__) - #define DECODE_UINT { \ unum = 0; \ do { \ @@ -80,7 +77,7 @@ const byte *mp_showbc_code_start; const mp_uint_t *mp_showbc_const_table; -void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; // Decode prelude @@ -96,30 +93,30 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m qstr block_name = mp_decode_uint(&code_info); qstr source_file = mp_decode_uint(&code_info); #endif - printf("File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", + mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); // raw bytecode dump size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; - printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", + mp_printf(print, "Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", prelude_size, len - prelude_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { - printf("\n"); + mp_printf(print, "\n"); } - printf(" %02x", mp_showbc_code_start[i]); + mp_printf(print, " %02x", mp_showbc_code_start[i]); } - printf("\n"); + mp_printf(print, "\n"); // bytecode prelude: arg names (as qstr objects) - printf("arg names:"); + mp_printf(print, "arg names:"); for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) { - printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); + mp_printf(print, " %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); } - printf("\n"); + mp_printf(print, "\n"); - printf("(N_STATE %u)\n", (unsigned)n_state); - printf("(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); + mp_printf(print, "(N_STATE %u)\n", (unsigned)n_state); + mp_printf(print, "(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); // skip over code_info ip += n_info; @@ -127,14 +124,14 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m // bytecode prelude: initialise closed over variables for (size_t i = 0; i < n_cell; ++i) { uint local_num = *ip++; - printf("(INIT_CELL %u)\n", local_num); + mp_printf(print, "(INIT_CELL %u)\n", local_num); } // print out line number info { mp_int_t bc = 0; mp_uint_t source_line = 1; - printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte *ci = code_info; *ci;) { if ((ci[0] & 0x80) == 0) { // 0b0LLBBBBB encoding @@ -147,27 +144,27 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m source_line += ((ci[0] << 4) & 0x700) | ci[1]; ci += 2; } - printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } - mp_bytecode_print2(ip, len - prelude_size, const_table); + mp_bytecode_print2(print, ip, len - prelude_size, const_table); } -const byte *mp_bytecode_print_str(const byte *ip) { +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { mp_uint_t unum; qstr qst; switch (*ip++) { case MP_BC_LOAD_CONST_FALSE: - printf("LOAD_CONST_FALSE"); + mp_printf(print, "LOAD_CONST_FALSE"); break; case MP_BC_LOAD_CONST_NONE: - printf("LOAD_CONST_NONE"); + mp_printf(print, "LOAD_CONST_NONE"); break; case MP_BC_LOAD_CONST_TRUE: - printf("LOAD_CONST_TRUE"); + mp_printf(print, "LOAD_CONST_TRUE"); break; case MP_BC_LOAD_CONST_SMALL_INT: { @@ -179,197 +176,197 @@ const byte *mp_bytecode_print_str(const byte *ip) { do { num = (num << 7) | (*ip & 0x7f); } while ((*ip++ & 0x80) != 0); - printf("LOAD_CONST_SMALL_INT " INT_FMT, num); + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, num); break; } case MP_BC_LOAD_CONST_STRING: DECODE_QSTR; - printf("LOAD_CONST_STRING '%s'", qstr_str(qst)); + mp_printf(print, "LOAD_CONST_STRING '%s'", qstr_str(qst)); break; case MP_BC_LOAD_CONST_OBJ: DECODE_OBJ; - printf("LOAD_CONST_OBJ %p=", MP_OBJ_TO_PTR(unum)); - mp_obj_print_helper(&mp_plat_print, (mp_obj_t)unum, PRINT_REPR); + mp_printf(print, "LOAD_CONST_OBJ %p=", MP_OBJ_TO_PTR(unum)); + mp_obj_print_helper(print, (mp_obj_t)unum, PRINT_REPR); break; case MP_BC_LOAD_NULL: - printf("LOAD_NULL"); + mp_printf(print, "LOAD_NULL"); break; case MP_BC_LOAD_FAST_N: DECODE_UINT; - printf("LOAD_FAST_N " UINT_FMT, unum); + mp_printf(print, "LOAD_FAST_N " UINT_FMT, unum); break; case MP_BC_LOAD_DEREF: DECODE_UINT; - printf("LOAD_DEREF " UINT_FMT, unum); + mp_printf(print, "LOAD_DEREF " UINT_FMT, unum); break; case MP_BC_LOAD_NAME: DECODE_QSTR; - printf("LOAD_NAME %s", qstr_str(qst)); + mp_printf(print, "LOAD_NAME %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_GLOBAL: DECODE_QSTR; - printf("LOAD_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "LOAD_GLOBAL %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_ATTR: DECODE_QSTR; - printf("LOAD_ATTR %s", qstr_str(qst)); + mp_printf(print, "LOAD_ATTR %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_METHOD: DECODE_QSTR; - printf("LOAD_METHOD %s", qstr_str(qst)); + mp_printf(print, "LOAD_METHOD %s", qstr_str(qst)); break; case MP_BC_LOAD_SUPER_METHOD: DECODE_QSTR; - printf("LOAD_SUPER_METHOD %s", qstr_str(qst)); + mp_printf(print, "LOAD_SUPER_METHOD %s", qstr_str(qst)); break; case MP_BC_LOAD_BUILD_CLASS: - printf("LOAD_BUILD_CLASS"); + mp_printf(print, "LOAD_BUILD_CLASS"); break; case MP_BC_LOAD_SUBSCR: - printf("LOAD_SUBSCR"); + mp_printf(print, "LOAD_SUBSCR"); break; case MP_BC_STORE_FAST_N: DECODE_UINT; - printf("STORE_FAST_N " UINT_FMT, unum); + mp_printf(print, "STORE_FAST_N " UINT_FMT, unum); break; case MP_BC_STORE_DEREF: DECODE_UINT; - printf("STORE_DEREF " UINT_FMT, unum); + mp_printf(print, "STORE_DEREF " UINT_FMT, unum); break; case MP_BC_STORE_NAME: DECODE_QSTR; - printf("STORE_NAME %s", qstr_str(qst)); + mp_printf(print, "STORE_NAME %s", qstr_str(qst)); break; case MP_BC_STORE_GLOBAL: DECODE_QSTR; - printf("STORE_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "STORE_GLOBAL %s", qstr_str(qst)); break; case MP_BC_STORE_ATTR: DECODE_QSTR; - printf("STORE_ATTR %s", qstr_str(qst)); + mp_printf(print, "STORE_ATTR %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_STORE_SUBSCR: - printf("STORE_SUBSCR"); + mp_printf(print, "STORE_SUBSCR"); break; case MP_BC_DELETE_FAST: DECODE_UINT; - printf("DELETE_FAST " UINT_FMT, unum); + mp_printf(print, "DELETE_FAST " UINT_FMT, unum); break; case MP_BC_DELETE_DEREF: DECODE_UINT; - printf("DELETE_DEREF " UINT_FMT, unum); + mp_printf(print, "DELETE_DEREF " UINT_FMT, unum); break; case MP_BC_DELETE_NAME: DECODE_QSTR; - printf("DELETE_NAME %s", qstr_str(qst)); + mp_printf(print, "DELETE_NAME %s", qstr_str(qst)); break; case MP_BC_DELETE_GLOBAL: DECODE_QSTR; - printf("DELETE_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "DELETE_GLOBAL %s", qstr_str(qst)); break; case MP_BC_DUP_TOP: - printf("DUP_TOP"); + mp_printf(print, "DUP_TOP"); break; case MP_BC_DUP_TOP_TWO: - printf("DUP_TOP_TWO"); + mp_printf(print, "DUP_TOP_TWO"); break; case MP_BC_POP_TOP: - printf("POP_TOP"); + mp_printf(print, "POP_TOP"); break; case MP_BC_ROT_TWO: - printf("ROT_TWO"); + mp_printf(print, "ROT_TWO"); break; case MP_BC_ROT_THREE: - printf("ROT_THREE"); + mp_printf(print, "ROT_THREE"); break; case MP_BC_JUMP: DECODE_SLABEL; - printf("JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_JUMP_IF_TRUE: DECODE_SLABEL; - printf("POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_JUMP_IF_FALSE: DECODE_SLABEL; - printf("POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_TRUE_OR_POP: DECODE_SLABEL; - printf("JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_FALSE_OR_POP: DECODE_SLABEL; - printf("JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_SETUP_WITH: DECODE_ULABEL; // loop-like labels are always forward - printf("SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_WITH_CLEANUP: - printf("WITH_CLEANUP"); + mp_printf(print, "WITH_CLEANUP"); break; case MP_BC_UNWIND_JUMP: DECODE_SLABEL; - printf("UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - mp_showbc_code_start), *ip); + mp_printf(print, "UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - mp_showbc_code_start), *ip); ip += 1; break; case MP_BC_SETUP_EXCEPT: DECODE_ULABEL; // except labels are always forward - printf("SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_SETUP_FINALLY: DECODE_ULABEL; // except labels are always forward - printf("SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_END_FINALLY: @@ -377,169 +374,169 @@ const byte *mp_bytecode_print_str(const byte *ip) { // if TOS is an integer, does something else // if TOS is None, just pops it and continues // else error - printf("END_FINALLY"); + mp_printf(print, "END_FINALLY"); break; case MP_BC_GET_ITER: - printf("GET_ITER"); + mp_printf(print, "GET_ITER"); break; case MP_BC_GET_ITER_STACK: - printf("GET_ITER_STACK"); + mp_printf(print, "GET_ITER_STACK"); break; case MP_BC_FOR_ITER: DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward - printf("FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_EXCEPT_JUMP: DECODE_ULABEL; // these labels are always forward - printf("POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_BUILD_TUPLE: DECODE_UINT; - printf("BUILD_TUPLE " UINT_FMT, unum); + mp_printf(print, "BUILD_TUPLE " UINT_FMT, unum); break; case MP_BC_BUILD_LIST: DECODE_UINT; - printf("BUILD_LIST " UINT_FMT, unum); + mp_printf(print, "BUILD_LIST " UINT_FMT, unum); break; case MP_BC_BUILD_MAP: DECODE_UINT; - printf("BUILD_MAP " UINT_FMT, unum); + mp_printf(print, "BUILD_MAP " UINT_FMT, unum); break; case MP_BC_STORE_MAP: - printf("STORE_MAP"); + mp_printf(print, "STORE_MAP"); break; case MP_BC_BUILD_SET: DECODE_UINT; - printf("BUILD_SET " UINT_FMT, unum); + mp_printf(print, "BUILD_SET " UINT_FMT, unum); break; #if MICROPY_PY_BUILTINS_SLICE case MP_BC_BUILD_SLICE: DECODE_UINT; - printf("BUILD_SLICE " UINT_FMT, unum); + mp_printf(print, "BUILD_SLICE " UINT_FMT, unum); break; #endif case MP_BC_STORE_COMP: DECODE_UINT; - printf("STORE_COMP " UINT_FMT, unum); + mp_printf(print, "STORE_COMP " UINT_FMT, unum); break; case MP_BC_UNPACK_SEQUENCE: DECODE_UINT; - printf("UNPACK_SEQUENCE " UINT_FMT, unum); + mp_printf(print, "UNPACK_SEQUENCE " UINT_FMT, unum); break; case MP_BC_UNPACK_EX: DECODE_UINT; - printf("UNPACK_EX " UINT_FMT, unum); + mp_printf(print, "UNPACK_EX " UINT_FMT, unum); break; case MP_BC_MAKE_FUNCTION: DECODE_PTR; - printf("MAKE_FUNCTION %p", (void *)(uintptr_t)unum); + mp_printf(print, "MAKE_FUNCTION %p", (void *)(uintptr_t)unum); break; case MP_BC_MAKE_FUNCTION_DEFARGS: DECODE_PTR; - printf("MAKE_FUNCTION_DEFARGS %p", (void *)(uintptr_t)unum); + mp_printf(print, "MAKE_FUNCTION_DEFARGS %p", (void *)(uintptr_t)unum); break; case MP_BC_MAKE_CLOSURE: { DECODE_PTR; mp_uint_t n_closed_over = *ip++; - printf("MAKE_CLOSURE %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + mp_printf(print, "MAKE_CLOSURE %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); break; } case MP_BC_MAKE_CLOSURE_DEFARGS: { DECODE_PTR; mp_uint_t n_closed_over = *ip++; - printf("MAKE_CLOSURE_DEFARGS %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + mp_printf(print, "MAKE_CLOSURE_DEFARGS %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); break; } case MP_BC_CALL_FUNCTION: DECODE_UINT; - printf("CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_FUNCTION_VAR_KW: DECODE_UINT; - printf("CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_METHOD: DECODE_UINT; - printf("CALL_METHOD n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_METHOD n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_METHOD_VAR_KW: DECODE_UINT; - printf("CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_RETURN_VALUE: - printf("RETURN_VALUE"); + mp_printf(print, "RETURN_VALUE"); break; case MP_BC_RAISE_LAST: - printf("RAISE_LAST"); + mp_printf(print, "RAISE_LAST"); break; case MP_BC_RAISE_OBJ: - printf("RAISE_OBJ"); + mp_printf(print, "RAISE_OBJ"); break; case MP_BC_RAISE_FROM: - printf("RAISE_FROM"); + mp_printf(print, "RAISE_FROM"); break; case MP_BC_YIELD_VALUE: - printf("YIELD_VALUE"); + mp_printf(print, "YIELD_VALUE"); break; case MP_BC_YIELD_FROM: - printf("YIELD_FROM"); + mp_printf(print, "YIELD_FROM"); break; case MP_BC_IMPORT_NAME: DECODE_QSTR; - printf("IMPORT_NAME '%s'", qstr_str(qst)); + mp_printf(print, "IMPORT_NAME '%s'", qstr_str(qst)); break; case MP_BC_IMPORT_FROM: DECODE_QSTR; - printf("IMPORT_FROM '%s'", qstr_str(qst)); + mp_printf(print, "IMPORT_FROM '%s'", qstr_str(qst)); break; case MP_BC_IMPORT_STAR: - printf("IMPORT_STAR"); + mp_printf(print, "IMPORT_STAR"); break; default: if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { - printf("LOAD_CONST_SMALL_INT " INT_FMT, (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16); + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16); } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { - printf("LOAD_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI); + mp_printf(print, "LOAD_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI); } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { - printf("STORE_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI); + mp_printf(print, "STORE_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI); } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { - printf("UNARY_OP " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI); + mp_printf(print, "UNARY_OP " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI); } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; - printf("BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); + mp_printf(print, "BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); } else { - printf("code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); + mp_printf(print, "code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); assert(0); return ip; } @@ -549,13 +546,13 @@ const byte *mp_bytecode_print_str(const byte *ip) { return ip; } -void mp_bytecode_print2(const byte *ip, size_t len, const mp_uint_t *const_table) { +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; mp_showbc_const_table = const_table; while (ip < len + mp_showbc_code_start) { - printf("%02u ", (uint)(ip - mp_showbc_code_start)); - ip = mp_bytecode_print_str(ip); - printf("\n"); + mp_printf(print, "%02u ", (uint)(ip - mp_showbc_code_start)); + ip = mp_bytecode_print_str(print, ip); + mp_printf(print, "\n"); } } diff --git a/py/vm.c b/py/vm.c index f30aed8ff1e36..393a2bbbd6513 100644 --- a/py/vm.c +++ b/py/vm.c @@ -39,7 +39,7 @@ // *FORMAT-OFF* #if 0 -#define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); +#define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->const_table); #else #define TRACE(ip) #endif From acdb0608b7622cc7af0bdbfc89b2696583c948ae Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Sep 2020 23:00:03 +1000 Subject: [PATCH 284/352] py/parse: Pass in an mp_print_t to mp_parse_node_print. So the output can be redirected if needed. Signed-off-by: Damien George --- ports/unix/main.c | 2 +- py/parse.c | 36 ++++++++++++++++++------------------ py/parse.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index c38b7b0c287a9..0fe492a554cbd 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -133,7 +133,7 @@ STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu // allow to print the parse tree in the coverage build if (mp_verbose_flag >= 3) { printf("----------------\n"); - mp_parse_node_print(parse_tree.root, 0); + mp_parse_node_print(&mp_plat_print, parse_tree.root, 0); printf("----------------\n"); } #endif diff --git a/py/parse.c b/py/parse.c index b93282165f88a..38cd215a9ff71 100644 --- a/py/parse.c +++ b/py/parse.c @@ -368,35 +368,35 @@ size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_ } #if MICROPY_DEBUG_PRINTERS -void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent) { if (MP_PARSE_NODE_IS_STRUCT(pn)) { - printf("[% 4d] ", (int)((mp_parse_node_struct_t *)pn)->source_line); + mp_printf(print, "[% 4d] ", (int)((mp_parse_node_struct_t *)pn)->source_line); } else { - printf(" "); + mp_printf(print, " "); } for (size_t i = 0; i < indent; i++) { - printf(" "); + mp_printf(print, " "); } if (MP_PARSE_NODE_IS_NULL(pn)) { - printf("NULL\n"); + mp_printf(print, "NULL\n"); } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); - printf("int(" INT_FMT ")\n", arg); + mp_printf(print, "int(" INT_FMT ")\n", arg); } else if (MP_PARSE_NODE_IS_LEAF(pn)) { uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); switch (MP_PARSE_NODE_LEAF_KIND(pn)) { case MP_PARSE_NODE_ID: - printf("id(%s)\n", qstr_str(arg)); + mp_printf(print, "id(%s)\n", qstr_str(arg)); break; case MP_PARSE_NODE_STRING: - printf("str(%s)\n", qstr_str(arg)); + mp_printf(print, "str(%s)\n", qstr_str(arg)); break; case MP_PARSE_NODE_BYTES: - printf("bytes(%s)\n", qstr_str(arg)); + mp_printf(print, "bytes(%s)\n", qstr_str(arg)); break; default: assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); - printf("tok(%u)\n", (uint)arg); + mp_printf(print, "tok(%u)\n", (uint)arg); break; } } else { @@ -404,19 +404,19 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_const_object) { #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - printf("literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); + mp_printf(print, "literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); #else - printf("literal const(%p)\n", (mp_obj_t)pns->nodes[0]); + mp_printf(print, "literal const(%p)\n", (mp_obj_t)pns->nodes[0]); #endif } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); #if USE_RULE_NAME - printf("%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + mp_printf(print, "%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); #else - printf("rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + mp_printf(print, "rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); #endif for (size_t i = 0; i < n; i++) { - mp_parse_node_print(pns->nodes[i], indent + 2); + mp_parse_node_print(print, pns->nodes[i], indent + 2); } } } @@ -424,10 +424,10 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { #endif // MICROPY_DEBUG_PRINTERS /* -STATIC void result_stack_show(parser_t *parser) { - printf("result stack, most recent first\n"); +STATIC void result_stack_show(const mp_print_t *print, parser_t *parser) { + mp_printf(print, "result stack, most recent first\n"); for (ssize_t i = parser->result_stack_top - 1; i >= 0; i--) { - mp_parse_node_print(parser->result_stack[i], 0); + mp_parse_node_print(print, parser->result_stack[i], 0); } } */ diff --git a/py/parse.h b/py/parse.h index 4ed8a7ad12324..a6eb380047dc3 100644 --- a/py/parse.h +++ b/py/parse.h @@ -86,7 +86,7 @@ bool mp_parse_node_is_const_false(mp_parse_node_t pn); bool mp_parse_node_is_const_true(mp_parse_node_t pn); bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o); size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes); -void mp_parse_node_print(mp_parse_node_t pn, size_t indent); +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent); typedef enum { MP_PARSE_SINGLE_INPUT, From b31cb21a3936b97af4a52b082e1f40c2061aeab7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 12 Sep 2020 13:47:59 +1000 Subject: [PATCH 285/352] stm32/servo: Fix angle and speed methods to work again with -ve args. Fixes a regression introduced by 70affd9ba22e7f62666a9a2fafc2a3c0be9ef95a Fixes issue #6403 Signed-off-by: Damien George --- ports/stm32/servo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/servo.c b/ports/stm32/servo.c index a347c6bbd06d4..17084224637ec 100644 --- a/ports/stm32/servo.c +++ b/ports/stm32/servo.c @@ -278,7 +278,7 @@ STATIC mp_obj_t pyb_servo_angle(size_t n_args, const mp_obj_t *args) { return mp_obj_new_int((self->pulse_cur - self->pulse_centre) * 90 / self->pulse_angle_90); } else { #if MICROPY_PY_BUILTINS_FLOAT - self->pulse_dest = self->pulse_centre + (uint16_t)((mp_float_t)self->pulse_angle_90 * mp_obj_get_float(args[1]) / MICROPY_FLOAT_CONST(90.0)); + self->pulse_dest = self->pulse_centre + (int16_t)((mp_float_t)self->pulse_angle_90 * mp_obj_get_float(args[1]) / MICROPY_FLOAT_CONST(90.0)); #else self->pulse_dest = self->pulse_centre + self->pulse_angle_90 * mp_obj_get_int(args[1]) / 90; #endif @@ -308,7 +308,7 @@ STATIC mp_obj_t pyb_servo_speed(size_t n_args, const mp_obj_t *args) { return mp_obj_new_int((self->pulse_cur - self->pulse_centre) * 100 / self->pulse_speed_100); } else { #if MICROPY_PY_BUILTINS_FLOAT - self->pulse_dest = self->pulse_centre + (uint16_t)((mp_float_t)self->pulse_speed_100 * mp_obj_get_float(args[1]) / MICROPY_FLOAT_CONST(100.0)); + self->pulse_dest = self->pulse_centre + (int16_t)((mp_float_t)self->pulse_speed_100 * mp_obj_get_float(args[1]) / MICROPY_FLOAT_CONST(100.0)); #else self->pulse_dest = self->pulse_centre + self->pulse_speed_100 * mp_obj_get_int(args[1]) / 100; #endif From 504522bd02f9d9f9848d5a6e38427df3d1e4a6e3 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 10:53:09 +1000 Subject: [PATCH 286/352] extmod/modbluetooth: Fix handling of optional data/uuid args. For the following 3 functions, previously the code relied on whether the arg was passed at all, to make it optional. Now it allows them to be explicitly `None` to indicate they are not used: - gatts_notify(..., [data]) - gattc_discover_services(..., [uuid]) - gattc_discover_characteristics(..., [uuid]) Also ensure that the uuid arguments are actually instances of the uuid type, and fix handling of the 5th arg in gattc_discover_characteristics(). --- extmod/modbluetooth.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index a8a762ce32f24..14d0921c84f4d 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -679,7 +679,7 @@ STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t value_handle = mp_obj_get_int(args[2]); - if (n_args == 4) { + if (n_args == 4 && args[3] != mp_const_none) { mp_buffer_info_t bufinfo = {0}; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, bufinfo.len); @@ -719,7 +719,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_set_buffer_obj, 3 STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) { mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_obj_bluetooth_uuid_t *uuid = NULL; - if (n_args == 3) { + if (n_args == 3 && args[2] != mp_const_none) { + if (!mp_obj_is_type(args[2], &bluetooth_uuid_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("UUID")); + } uuid = MP_OBJ_TO_PTR(args[2]); } return bluetooth_handle_errno(mp_bluetooth_gattc_discover_primary_services(conn_handle, uuid)); @@ -731,7 +734,10 @@ STATIC mp_obj_t bluetooth_ble_gattc_discover_characteristics(size_t n_args, cons mp_int_t start_handle = mp_obj_get_int(args[2]); mp_int_t end_handle = mp_obj_get_int(args[3]); mp_obj_bluetooth_uuid_t *uuid = NULL; - if (n_args == 3) { + if (n_args == 5 && args[4] != mp_const_none) { + if (!mp_obj_is_type(args[4], &bluetooth_uuid_type)) { + mp_raise_TypeError(MP_ERROR_TEXT("UUID")); + } uuid = MP_OBJ_TO_PTR(args[4]); } return bluetooth_handle_errno(mp_bluetooth_gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid)); From 6a6a5f9e151473bdcc1d14725d680691ff665a82 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 11:43:53 +1000 Subject: [PATCH 287/352] extmod/modbluetooth: Make BLE.irq() method positional only. Simplifcation now that the trigger arg has been removed. --- extmod/modbluetooth.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 14d0921c84f4d..64cd383525665 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -384,27 +384,21 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bluetooth_ble_config_obj, 1, bluetooth_ble_config); -STATIC mp_obj_t bluetooth_ble_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_handler }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_handler, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t callback = args[ARG_handler].u_obj; - if (callback != mp_const_none && !mp_obj_is_callable(callback)) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid callback")); +STATIC mp_obj_t bluetooth_ble_irq(mp_obj_t self_in, mp_obj_t handler_in) { + (void)self_in; + if (handler_in != mp_const_none && !mp_obj_is_callable(handler_in)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid handler")); } // Update the callback. MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); - o->irq_handler = callback; + o->irq_handler = handler_in; MICROPY_PY_BLUETOOTH_EXIT return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bluetooth_ble_irq_obj, 1, bluetooth_ble_irq); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_irq_obj, bluetooth_ble_irq); // ---------------------------------------------------------------------------- // Bluetooth object: GAP From 19faf550904c4e93427450994b7babe367eb9add Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 11:45:09 +1000 Subject: [PATCH 288/352] docs/library/ubluetooth.rst: Clarify position/kw arguments. Almost all ubluetooth functions are positional-only, but some have optional args. Make this clearer and show what the defaults are. --- docs/library/ubluetooth.rst | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index f8dad7b8d1ed2..5635f4d69a763 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -28,15 +28,15 @@ Constructor Configuration ------------- -.. method:: BLE.active([active]) +.. method:: BLE.active([active], /) Optionally changes the active state of the BLE radio, and returns the current state. The radio must be made active before using any other methods on this class. -.. method:: BLE.config('param') - BLE.config(param=value, ...) +.. method:: BLE.config('param', /) + BLE.config(*, param=value, ...) Get or set configuration values of the BLE interface. To get a value the parameter name should be quoted as a string, and just one parameter is @@ -76,7 +76,7 @@ Configuration Event Handling -------------- -.. method:: BLE.irq(handler) +.. method:: BLE.irq(handler, /) Registers a callback for events from the BLE stack. The *handler* takes two arguments, ``event`` (which will be one of the codes below) and ``data`` @@ -195,7 +195,7 @@ program. Broadcaster Role (Advertiser) ----------------------------- -.. method:: BLE.gap_advertise(interval_us, adv_data=None, resp_data=None, connectable=True) +.. method:: BLE.gap_advertise(interval_us, adv_data=None, *, resp_data=None, connectable=True) Starts advertising at the specified interval (in **micro**\ seconds). This interval will be rounded down to the nearest 625us. To stop advertising, set @@ -214,7 +214,7 @@ Broadcaster Role (Advertiser) Observer Role (Scanner) ----------------------- -.. method:: BLE.gap_scan(duration_ms, [interval_us], [window_us], [active]) +.. method:: BLE.gap_scan(duration_ms, interval_us=1280000, window_us=11250, active=False, /) Run a scan operation lasting for the specified duration (in **milli**\ seconds). @@ -244,7 +244,7 @@ Observer Role (Scanner) * 0x04 - SCAN_RSP - scan response ``active`` can be set ``True`` if you want to receive scan responses in the results. - + When scanning is stopped (either due to the duration finishing or when explicitly stopped), the ``_IRQ_SCAN_DONE`` event will be raised. @@ -268,7 +268,7 @@ writes from a central to a given characteristic, use :meth:`gatts_write` after registration. e.g. ``gatts_write(char_handle, bytes(100))``. -.. method:: BLE.gatts_register_services(services_definition) +.. method:: BLE.gatts_register_services(services_definition, /) Configures the peripheral with the specified services, replacing any existing services. @@ -308,26 +308,26 @@ writes from a central to a given characteristic, use **Note:** Advertising must be stopped before registering services. -.. method:: BLE.gatts_read(value_handle) +.. method:: BLE.gatts_read(value_handle, /) Reads the local value for this handle (which has either been written by :meth:`gatts_write ` or by a remote central). -.. method:: BLE.gatts_write(value_handle, data) +.. method:: BLE.gatts_write(value_handle, data, /) Writes the local value for this handle, which can be read by a central. -.. method:: BLE.gatts_notify(conn_handle, value_handle, [data]) +.. method:: BLE.gatts_notify(conn_handle, value_handle, data=None, /) Sends a notification request to a connected central. - If *data* is specified, then that value is sent to the central as part of + If *data* is not ``None``, then that value is sent to the central as part of the notification. The local value will not be modified. - Otherwise, if *data* is not specified, then the current local value (as + Otherwise, if *data* is ``None``, then the current local value (as set with :meth:`gatts_write `) will be sent. -.. method:: BLE.gatts_indicate(conn_handle, value_handle) +.. method:: BLE.gatts_indicate(conn_handle, value_handle, /) Sends an indication request to a connected central. @@ -361,7 +361,7 @@ Central Role (GATT Client) On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised. -.. method:: BLE.gap_disconnect(conn_handle) +.. method:: BLE.gap_disconnect(conn_handle, /) Disconnect the specified connection handle. @@ -370,7 +370,7 @@ Central Role (GATT Client) Returns ``False`` if the connection handle wasn't connected, and ``True`` otherwise. -.. method:: BLE.gattc_discover_services(conn_handle, [uuid]) +.. method:: BLE.gattc_discover_services(conn_handle, uuid=None, /) Query a connected peripheral for its services. @@ -379,7 +379,7 @@ Central Role (GATT Client) For each service discovered, the ``_IRQ_GATTC_SERVICE_RESULT`` event will be raised, followed by ``_IRQ_GATTC_SERVICE_DONE`` on completion. -.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle, [uuid]) +.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid=None, /) Query a connected peripheral for characteristics in the specified range. @@ -392,14 +392,14 @@ Central Role (GATT Client) For each characteristic discovered, the ``_IRQ_GATTC_CHARACTERISTIC_RESULT`` event will be raised, followed by ``_IRQ_GATTC_CHARACTERISTIC_DONE`` on completion. -.. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle) +.. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle, /) Query a connected peripheral for descriptors in the specified range. For each descriptor discovered, the ``_IRQ_GATTC_DESCRIPTOR_RESULT`` event will be raised, followed by ``_IRQ_GATTC_DESCRIPTOR_DONE`` on completion. -.. method:: BLE.gattc_read(conn_handle, value_handle) +.. method:: BLE.gattc_read(conn_handle, value_handle, /) Issue a remote read to a connected peripheral for the specified characteristic or descriptor handle. @@ -433,7 +433,7 @@ class UUID Constructor ----------- -.. class:: UUID(value) +.. class:: UUID(value, /) Creates a UUID instance with the specified **value**. From 5be3bfcb7e065bd95eb8460d2069524b934bcd0f Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 15 Sep 2020 11:38:58 +1000 Subject: [PATCH 289/352] extmod/modbluetooth: Print UUIDs correctly. In particular, the printed string can now be re-evaluated to construct the same UUID object. --- extmod/modbluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 64cd383525665..f77c7eb63cf22 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -181,7 +181,7 @@ STATIC void bluetooth_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_p (void)kind; mp_obj_bluetooth_uuid_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "UUID%u(%s", self->type * 8, self->type <= 4 ? "0x" : "'"); + mp_printf(print, "UUID(%s", self->type <= 4 ? "0x" : "'"); for (int i = 0; i < self->type; ++i) { if (i == 4 || i == 6 || i == 8 || i == 10) { mp_printf(print, "-"); From c200759290bae8ceb74c91d29c0a521292d925f4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 12:26:09 +1000 Subject: [PATCH 290/352] docs/library/ubluetooth.rst: Clarify peripheral/central vs server/client Previously this documentation assumed peripheral=server and central=client, but this is not accurate or true to the implementation. --- docs/library/ubluetooth.rst | 130 +++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 48 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index 5635f4d69a763..e4471f8054a82 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -6,8 +6,8 @@ This module provides an interface to a Bluetooth controller on a board. Currently this supports Bluetooth Low Energy (BLE) in Central, Peripheral, -Broadcaster, and Observer roles, and a device may operate in multiple -roles concurrently. +Broadcaster, and Observer roles, as well as GATT Server and Client. A device +may operate in multiple roles concurrently. This API is intended to match the low-level Bluetooth protocol and provide building-blocks for higher-level abstractions such as specific device types. @@ -70,8 +70,7 @@ Configuration incoming events. This buffer is global to the entire BLE driver and so handles incoming data for all events, including all characteristics. Increasing this allows better handling of bursty incoming data (for - example scan results) and the ability for a central role to receive - larger characteristic values. + example scan results) and the ability to receive larger characteristic values. Event Handling -------------- @@ -99,10 +98,10 @@ Event Handling # A central has disconnected from this peripheral. conn_handle, addr_type, addr = data elif event == _IRQ_GATTS_WRITE: - # A central has written to this characteristic or descriptor. + # A client has written to this characteristic or descriptor. conn_handle, attr_handle = data elif event == _IRQ_GATTS_READ_REQUEST: - # A central has issued a read. Note: this is a hard IRQ. + # A client has issued a read. Note: this is a hard IRQ. # Return None to deny the read. # Note: This event is not supported on ESP32. conn_handle, attr_handle = data @@ -153,13 +152,13 @@ Event Handling # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: - # A peripheral has sent a notify request. + # A server has sent a notify request. conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTC_INDICATE: - # A peripheral has sent an indicate request. + # A server has sent an indicate request. conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTS_INDICATE_DONE: - # A central has acknowledged the indication. + # A client has acknowledged the indication. # Note: Status will be zero on successful acknowledgment, implementation-specific value otherwise. conn_handle, value_handle, status = data @@ -249,28 +248,74 @@ Observer Role (Scanner) explicitly stopped), the ``_IRQ_SCAN_DONE`` event will be raised. -Peripheral Role (GATT Server) ------------------------------ +Central Role +------------ + +A central device can connect to peripherals that it has discovered using the observer role (see :meth:`gap_scan`) or with a known address. + +.. method:: BLE.gap_connect(addr_type, addr, scan_duration_ms=2000, /) + + Connect to a peripheral. + + See :meth:`gap_scan ` for details about address types. + + On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised. + + +Peripheral Role +--------------- + +A peripheral device is expected to send connectable advertisements (see +:meth:`gap_advertise`). It will usually be acting as a GATT +server, having first registered services and characteristics using +:meth:`gatts_register_services`. + +When a central connects, the ``_IRQ_CENTRAL_CONNECT`` event will be raised. + + +Central & Peripheral Roles +-------------------------- + +.. method:: BLE.gap_disconnect(conn_handle, /) + + Disconnect the specified connection handle. This can either be a + central that has connected to this device (if acting as a peripheral) + or a peripheral that was previously connected to by this device (if acting + as a central). + + On success, the ``_IRQ_PERIPHERAL_DISCONNECT`` or ``_IRQ_CENTRAL_DISCONNECT`` + event will be raised. + + Returns ``False`` if the connection handle wasn't connected, and ``True`` + otherwise. + -A BLE peripheral has a set of registered services. Each service may contain +GATT Server +----------- + +A GATT server has a set of registered services. Each service may contain characteristics, which each have a value. Characteristics can also contain descriptors, which themselves have values. These values are stored locally, and are accessed by their "value handle" which is generated during service registration. They can also be read from or written -to by a remote central device. Additionally, a peripheral can "notify" a -characteristic to a connected central via a connection handle. +to by a remote client device. Additionally, a server can "notify" a +characteristic to a connected client via a connection handle. + +A device in either central or peripheral roles may function as a GATT server, +however in most cases it will be more common for a peripheral device to act +as the server. Characteristics and descriptors have a default maximum size of 20 bytes. -Anything written to them by a central will be truncated to this length. However, +Anything written to them by a client will be truncated to this length. However, any local write will increase the maximum size, so if you want to allow larger -writes from a central to a given characteristic, use +writes from a client to a given characteristic, use :meth:`gatts_write` after registration. e.g. ``gatts_write(char_handle, bytes(100))``. .. method:: BLE.gatts_register_services(services_definition, /) - Configures the peripheral with the specified services, replacing any + Configures the server with the specified services, replacing any existing services. *services_definition* is a list of **services**, where each **service** is a @@ -311,17 +356,17 @@ writes from a central to a given characteristic, use .. method:: BLE.gatts_read(value_handle, /) Reads the local value for this handle (which has either been written by - :meth:`gatts_write ` or by a remote central). + :meth:`gatts_write ` or by a remote client). .. method:: BLE.gatts_write(value_handle, data, /) - Writes the local value for this handle, which can be read by a central. + Writes the local value for this handle, which can be read by a client. .. method:: BLE.gatts_notify(conn_handle, value_handle, data=None, /) - Sends a notification request to a connected central. + Sends a notification request to a connected client. - If *data* is not ``None``, then that value is sent to the central as part of + If *data* is not ``None``, then that value is sent to the client as part of the notification. The local value will not be modified. Otherwise, if *data* is ``None``, then the current local value (as @@ -329,7 +374,7 @@ writes from a central to a given characteristic, use .. method:: BLE.gatts_indicate(conn_handle, value_handle, /) - Sends an indication request to a connected central. + Sends an indication request to a connected client. **Note:** This does not currently support sending a custom value, it will always send the current local value (as set with :meth:`gatts_write @@ -349,30 +394,19 @@ writes from a central to a given characteristic, use be cleared after reading. This feature is useful when implementing something like the Nordic UART Service. +GATT Client +----------- -Central Role (GATT Client) --------------------------- - -.. method:: BLE.gap_connect(addr_type, addr, scan_duration_ms=2000, /) - - Connect to a peripheral. - - See :meth:`gatts_write ` for details about address types. - - On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised. - -.. method:: BLE.gap_disconnect(conn_handle, /) - - Disconnect the specified connection handle. - - On success, the ``_IRQ_PERIPHERAL_DISCONNECT`` event will be raised. +A GATT client can discover and read/write characteristics on a remote GATT server. - Returns ``False`` if the connection handle wasn't connected, and ``True`` - otherwise. +It is more common for a central role device to act as the GATT client, however +it's also possible for a peripheral to act as a client in order to discover +information about the central that has connected to it (e.g. to read the +device name from the device information service). .. method:: BLE.gattc_discover_services(conn_handle, uuid=None, /) - Query a connected peripheral for its services. + Query a connected server for its services. Optionally specify a service *uuid* to query for that service only. @@ -381,7 +415,7 @@ Central Role (GATT Client) .. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid=None, /) - Query a connected peripheral for characteristics in the specified range. + Query a connected server for characteristics in the specified range. Optionally specify a characteristic *uuid* to query for that characteristic only. @@ -394,14 +428,14 @@ Central Role (GATT Client) .. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle, /) - Query a connected peripheral for descriptors in the specified range. + Query a connected server for descriptors in the specified range. For each descriptor discovered, the ``_IRQ_GATTC_DESCRIPTOR_RESULT`` event will be raised, followed by ``_IRQ_GATTC_DESCRIPTOR_DONE`` on completion. .. method:: BLE.gattc_read(conn_handle, value_handle, /) - Issue a remote read to a connected peripheral for the specified + Issue a remote read to a connected server for the specified characteristic or descriptor handle. When a value is available, the ``_IRQ_GATTC_READ_RESULT`` event will be @@ -409,20 +443,20 @@ Central Role (GATT Client) .. method:: BLE.gattc_write(conn_handle, value_handle, data, mode=0, /) - Issue a remote write to a connected peripheral for the specified + Issue a remote write to a connected server for the specified characteristic or descriptor handle. The argument *mode* specifies the write behaviour, with the currently supported values being: * ``mode=0`` (default) is a write-without-response: the write will - be sent to the remote peripheral but no confirmation will be + be sent to the remote server but no confirmation will be returned, and no event will be raised. - * ``mode=1`` is a write-with-response: the remote peripheral is + * ``mode=1`` is a write-with-response: the remote server is requested to send a response/acknowledgement that it received the data. - If a response is received from the remote peripheral the + If a response is received from the remote server the ``_IRQ_GATTC_WRITE_DONE`` event will be raised. From fe642ced43809f932eaa8e3396c8bd0f8265de09 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 18 Sep 2020 12:50:05 +1000 Subject: [PATCH 291/352] tests/multi_bluetooth: Update UUID format in .exp files. --- tests/multi_bluetooth/ble_characteristic.py.exp | 2 +- tests/multi_bluetooth/ble_gap_device_name.py.exp | 2 +- tests/multi_bluetooth/ble_gatt_data_transfer.py.exp | 6 +++--- tests/multi_bluetooth/ble_gattc_discover_services.py.exp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/multi_bluetooth/ble_characteristic.py.exp b/tests/multi_bluetooth/ble_characteristic.py.exp index 25b5544efc459..90dc46c06fd57 100644 --- a/tests/multi_bluetooth/ble_characteristic.py.exp +++ b/tests/multi_bluetooth/ble_characteristic.py.exp @@ -14,7 +14,7 @@ _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect _IRQ_PERIPHERAL_CONNECT -_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') gattc_read _IRQ_GATTC_READ_RESULT b'periph0' _IRQ_GATTC_READ_DONE 0 diff --git a/tests/multi_bluetooth/ble_gap_device_name.py.exp b/tests/multi_bluetooth/ble_gap_device_name.py.exp index 377df90dee356..0f4ee6e3dc576 100644 --- a/tests/multi_bluetooth/ble_gap_device_name.py.exp +++ b/tests/multi_bluetooth/ble_gap_device_name.py.exp @@ -11,7 +11,7 @@ _IRQ_CENTRAL_DISCONNECT gap_connect _IRQ_PERIPHERAL_CONNECT gattc_discover_characteristics -_IRQ_GATTC_CHARACTERISTIC_RESULT UUID16(0x2a00) +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID(0x2a00) gattc_read _IRQ_GATTC_READ_RESULT b'GAP_NAME0' gap_disconnect: True diff --git a/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp b/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp index a1b3cbcd07374..94a0718108c1e 100644 --- a/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp +++ b/tests/multi_bluetooth/ble_gatt_data_transfer.py.exp @@ -11,9 +11,9 @@ _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect _IRQ_PERIPHERAL_CONNECT -_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000002-1111-2222-3333-444444444444') -_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000003-1111-2222-3333-444444444444') -_IRQ_GATTC_CHARACTERISTIC_RESULT UUID128('00000004-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000002-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000003-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000004-1111-2222-3333-444444444444') _IRQ_GATTC_CHARACTERISTIC_DONE gattc_write gattc_write diff --git a/tests/multi_bluetooth/ble_gattc_discover_services.py.exp b/tests/multi_bluetooth/ble_gattc_discover_services.py.exp index 06c36b249368a..7f4d8da81ad8c 100644 --- a/tests/multi_bluetooth/ble_gattc_discover_services.py.exp +++ b/tests/multi_bluetooth/ble_gattc_discover_services.py.exp @@ -5,7 +5,7 @@ _IRQ_CENTRAL_DISCONNECT --- instance1 --- gap_connect _IRQ_PERIPHERAL_CONNECT -_IRQ_GATTC_SERVICE_RESULT UUID16(0x180d) -_IRQ_GATTC_SERVICE_RESULT UUID128('a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a') +_IRQ_GATTC_SERVICE_RESULT UUID(0x180d) +_IRQ_GATTC_SERVICE_RESULT UUID('a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a') gap_disconnect: True _IRQ_PERIPHERAL_DISCONNECT From f271b96b5c8d6a4abedc81459a1c5710eb187abb Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 13:24:12 +1000 Subject: [PATCH 292/352] docs/library/ubluetooth.rst: Add docs for MTU API. --- docs/library/ubluetooth.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index e4471f8054a82..03d03583a1cce 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -72,6 +72,13 @@ Configuration Increasing this allows better handling of bursty incoming data (for example scan results) and the ability to receive larger characteristic values. + - ``'mtu'``: Get/set the MTU that will be used during an MTU exchange. The + resulting MTU will be the minimum of this and the remote device's MTU. + MTU exchange will not happen automatically (unless the remote device initiates + it), and must be manually initiated with + :meth:`gattc_exchange_mtu`. + Use the ``_IRQ_MTU_EXCHANGED`` event to discover the MTU for a given connection. + Event Handling -------------- @@ -161,6 +168,9 @@ Event Handling # A client has acknowledged the indication. # Note: Status will be zero on successful acknowledgment, implementation-specific value otherwise. conn_handle, value_handle, status = data + elif event == _IRQ_MTU_EXCHANGED: + # MTU exchange complete (either initiated by us or the remote device). + conn_handle, mtu = data The event codes are:: @@ -185,6 +195,7 @@ The event codes are:: _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) + _IRQ_MTU_EXCHANGED = const(21) In order to save space in the firmware, these constants are not included on the :mod:`ubluetooth` module. Add the ones that you need from the list above to your @@ -459,6 +470,18 @@ device name from the device information service). If a response is received from the remote server the ``_IRQ_GATTC_WRITE_DONE`` event will be raised. +.. method:: BLE.gattc_exchange_mtu(conn_handle, /) + + Initiate MTU exchange with a connected server, using the preferred MTU + set using ``BLE.config(mtu=value)``. + + The ``_IRQ_MTU_EXCHANGED`` event will be raised when MTU exchange + completes. + + **Note:** MTU exchange is typically initiated by the central. When using + the BlueKitchen stack in the central role, it does not support a remote + peripheral initiating the MTU exchange. NimBLE works for both roles. + class UUID ---------- From 857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 11 Sep 2020 18:12:36 +1000 Subject: [PATCH 293/352] extmod/modbluetooth: Implement MTU. --- extmod/btstack/modbluetooth_btstack.c | 36 ++++++++++++++++++++-- extmod/modbluetooth.c | 28 +++++++++++++++++ extmod/modbluetooth.h | 12 ++++++++ extmod/nimble/modbluetooth_nimble.c | 44 +++++++++++++++++++++++++-- 4 files changed, 116 insertions(+), 4 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index cde802a61f5f4..ae96035868877 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -264,6 +264,11 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan uint16_t value_handle = att_event_handle_value_indication_complete_get_attribute_handle(packet); uint8_t status = att_event_handle_value_indication_complete_get_status(packet); mp_bluetooth_gatts_on_indicate_complete(conn_handle, value_handle, status); + } else if (event_type == ATT_EVENT_MTU_EXCHANGE_COMPLETE) { + // This is triggered in peripheral mode, when exchange initiated by us or remote. + uint16_t conn_handle = att_event_mtu_exchange_complete_get_handle(packet); + uint16_t mtu = att_event_mtu_exchange_complete_get_MTU(packet); + mp_bluetooth_gatts_on_mtu_exchanged(conn_handle, mtu); } else if (event_type == HCI_EVENT_LE_META || event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { // Ignore, duplicated by att_server.c. } else { @@ -339,8 +344,6 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_printf(" --> btstack # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { DEBUG_printf(" --> hci vendor specific\n"); - } else if (event_type == GATT_EVENT_MTU) { - DEBUG_printf(" --> hci MTU\n"); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { DEBUG_printf(" --> hci disconnect complete\n"); uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); @@ -621,6 +624,9 @@ int mp_bluetooth_init(void) { #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE gatt_client_init(); + + // We always require explicitly exchanging MTU with ble.gattc_exchange_mtu(). + gatt_client_mtu_enable_auto_negotiation(false); #endif // Register for HCI events. @@ -1042,6 +1048,24 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); } +int mp_bluetooth_get_preferred_mtu(void) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + return l2cap_max_le_mtu(); +} + +int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + l2cap_set_max_le_mtu(mtu); + if (l2cap_max_le_mtu() != mtu) { + return MP_EINVAL; + } + return 0; +} + int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { DEBUG_printf("mp_bluetooth_gap_disconnect\n"); gap_disconnect(conn_handle); @@ -1204,6 +1228,14 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const return btstack_error_to_errno(err); } + +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { + DEBUG_printf("mp_bluetooth_exchange_mtu: conn_handle=%d mtu=%d\n", conn_handle, l2cap_max_le_mtu()); + + gatt_client_send_mtu_negotiation(&btstack_packet_handler_att_server, conn_handle); + + return 0; +} #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index f77c7eb63cf22..daf9cd0d1984b 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -316,6 +316,8 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_rxbuf: return mp_obj_new_int(self->ringbuf.size); + case MP_QSTR_mtu: + return mp_obj_new_int(mp_bluetooth_get_preferred_mtu()); default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } @@ -368,6 +370,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map m_del(uint8_t, old_irq_data_buf, old_irq_data_alloc); break; } + case MP_QSTR_mtu: { + mp_int_t mtu = mp_obj_get_int(e->value); + bluetooth_handle_errno(mp_bluetooth_set_preferred_mtu(mtu)); + break; + } case MP_QSTR_addr_mode: { mp_int_t addr_mode = mp_obj_get_int(e->value); mp_bluetooth_set_address_mode(addr_mode); @@ -770,6 +777,13 @@ STATIC mp_obj_t bluetooth_ble_gattc_write(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_write_obj, 4, 5, bluetooth_ble_gattc_write); +STATIC mp_obj_t bluetooth_ble_gattc_exchange_mtu(mp_obj_t self_in, mp_obj_t conn_handle_in) { + (void)self_in; + uint16_t conn_handle = mp_obj_get_int(conn_handle_in); + return bluetooth_handle_errno(mp_bluetooth_gattc_exchange_mtu(conn_handle)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth_ble_gattc_exchange_mtu); + #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // ---------------------------------------------------------------------------- @@ -802,6 +816,7 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_read), MP_ROM_PTR(&bluetooth_ble_gattc_read_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_write), MP_ROM_PTR(&bluetooth_ble_gattc_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_gattc_exchange_mtu), MP_ROM_PTR(&bluetooth_ble_gattc_exchange_mtu_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(bluetooth_ble_locals_dict, bluetooth_ble_locals_dict_table); @@ -911,6 +926,9 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE) { // conn_handle, value_handle, status ringbuf_extract(&o->ringbuf, data_tuple, 2, 1, NULL, 0, NULL, NULL); + } else if (event == MP_BLUETOOTH_IRQ_MTU_EXCHANGED) { + // conn_handle, mtu + ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, NULL); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT) { // addr_type, addr, adv_type, rssi, adv_data @@ -1184,6 +1202,16 @@ bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_han } #endif +void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) { + MICROPY_PY_BLUETOOTH_ENTER + mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); + if (enqueue_irq(o, 2 + 2, MP_BLUETOOTH_IRQ_MTU_EXCHANGED)) { + ringbuf_put16(&o->ringbuf, conn_handle); + ringbuf_put16(&o->ringbuf, value); + } + schedule_ringbuf(atomic_state); +} + void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len) { mp_map_elem_t *elem = mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); mp_bluetooth_gatts_db_entry_t *entry = m_new(mp_bluetooth_gatts_db_entry_t, 1); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 23ff97bc61806..2df4c3c25f17b 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -100,6 +100,7 @@ #define MP_BLUETOOTH_IRQ_GATTC_NOTIFY (18) #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) #define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) +#define MP_BLUETOOTH_IRQ_MTU_EXCHANGED (21) #define MP_BLUETOOTH_ADDRESS_MODE_PUBLIC (0) #define MP_BLUETOOTH_ADDRESS_MODE_RANDOM (1) @@ -131,6 +132,7 @@ _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) +_IRQ_MTU_EXCHANGED = const(21) */ // Common UUID type. @@ -212,6 +214,10 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append // Disconnect from a central or peripheral. int mp_bluetooth_gap_disconnect(uint16_t conn_handle); +// Set/get the MTU that we will respond to a MTU exchange with. +int mp_bluetooth_get_preferred_mtu(void); +int mp_bluetooth_set_preferred_mtu(uint16_t mtu); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Start a discovery (scan). Set duration to zero to run continuously. int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan); @@ -236,6 +242,9 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle); // Write the value to the remote peripheral. int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode); + +// Initiate MTU exchange for a specific connection using the preferred MTU. +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle); #endif ///////////////////////////////////////////////////////////////////////////// @@ -255,6 +264,9 @@ void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t valu bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle); #endif +// Call this when an MTU exchange completes. +void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Notify modbluetooth that scan has finished, either timeout, manually, or by some other action (e.g. connecting). void mp_bluetooth_gap_on_scan_complete(void); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 9ca90b602476f..c0d6d64cfc1f0 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -288,9 +288,17 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { // Map "done/ack" to 0, otherwise pass the status directly. mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status); } + break; } - } + case BLE_GAP_EVENT_MTU: { + if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) { + DEBUG_printf("gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); + mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value); + } + break; + } + } return 0; } @@ -637,7 +645,7 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { return 0; } -int mp_bluetooth_gatts_register_service_end() { +int mp_bluetooth_gatts_register_service_end(void) { int ret = ble_gatts_start(); if (ret != 0) { return ble_hs_err_to_errno(ret); @@ -769,6 +777,23 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, len, append); } +int mp_bluetooth_get_preferred_mtu(void) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + return ble_att_preferred_mtu(); +} + +int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + if (ble_att_set_preferred_mtu(mtu)) { + return MP_EINVAL; + } + return 0; +} + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) { @@ -884,6 +909,14 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) { // TODO break; + case BLE_GAP_EVENT_MTU: { + if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) { + DEBUG_printf("peripheral_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); + mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value); + } + break; + } + default: break; } @@ -1042,6 +1075,13 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const return ble_hs_err_to_errno(err); } +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { + DEBUG_printf("mp_bluetooth_exchange_mtu: conn_handle=%d mtu=%d\n", conn_handle, ble_att_preferred_mtu()); + + // Using NULL callback (we'll get notified in gap_event_cb instead). + return ble_hs_err_to_errno(ble_gattc_exchange_mtu(conn_handle, NULL, NULL)); +} + #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE From 06dda48144be94ff6bef92b5bbf3fa87dea32cfa Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 14 Sep 2020 18:11:19 +1000 Subject: [PATCH 294/352] tests/run-multitests.py: Show test/truth diff. --- tests/run-multitests.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 7ab4e85c57d79..ad032df38b238 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -7,6 +7,7 @@ import sys, os, time, re, select import argparse import subprocess +import tempfile sys.path.append("../tools") import pyboard @@ -18,6 +19,9 @@ CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython") +# For diff'ing test output +DIFF = os.getenv("MICROPY_DIFF", "diff -u") + PYTHON_TRUTH = CPYTHON3 INSTANCE_READ_TIMEOUT_S = 10 @@ -323,6 +327,18 @@ def run_test_on_instances(test_file, num_instances, instances): return error, skip, output_str +def print_diff(a, b): + a_fd, a_path = tempfile.mkstemp(text=True) + b_fd, b_path = tempfile.mkstemp(text=True) + os.write(a_fd, a.encode()) + os.write(b_fd, b.encode()) + os.close(a_fd) + os.close(b_fd) + subprocess.run(DIFF.split(" ") + [a_path, b_path]) + os.unlink(a_path) + os.unlink(b_path) + + def run_tests(test_files, instances_truth, instances_test): skipped_tests = [] passed_tests = [] @@ -372,6 +388,8 @@ def run_tests(test_files, instances_truth, instances_test): print(output_test, end="") print("### TRUTH ###") print(output_truth, end="") + print("### DIFF ###") + print_diff(output_test, output_truth) if cmd_args.show_output: print() From 3086d35e1637ba2bdc05ae676034d029ddbf302f Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 14 Sep 2020 18:11:46 +1000 Subject: [PATCH 295/352] tests/multi_bluetooth/ble_mtu.py: Add multitest for BLE MTU. --- tests/multi_bluetooth/ble_mtu.py | 208 +++++++++++++++++++++++++++ tests/multi_bluetooth/ble_mtu.py.exp | 143 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 tests/multi_bluetooth/ble_mtu.py create mode 100644 tests/multi_bluetooth/ble_mtu.py.exp diff --git a/tests/multi_bluetooth/ble_mtu.py b/tests/multi_bluetooth/ble_mtu.py new file mode 100644 index 0000000000000..ef3199c5bbb04 --- /dev/null +++ b/tests/multi_bluetooth/ble_mtu.py @@ -0,0 +1,208 @@ +# Test MTU exchange (initiated by both central and peripheral) and the effect on +# notify and write size. + +# Seven connections are made (four central->peripheral, three peripheral->central). +# +# Test | Requested | Preferred | Result | Notes +# 0 | 300 (C) | 256 (P) | 256 | +# 1 | 300 (C) | 200 (P) | 200 | +# 2 | 300 (C) | 400 (P) | 300 | +# 3 | 300 (C) | 50 (P) | 50 | Shorter than 64 so the notification is truncated. +# 4 | 290 (P) | 256 (C) | 256 | +# 5 | 290 (P) | 190 (C) | 190 | +# 6 | 290 (P) | 350 (C) | 290 | +# +# For each connection a notification is sent by the server (peripheral) and a characteristic +# is written by the client (central) to ensure that the expected size is transmitted. +# +# Note: This currently fails on btstack for two reasons: +# - btstack doesn't truncate writes to the MTU (it fails instead) +# - btstack (in central mode) doesn't handle the peripheral initiating the MTU exchange + +from micropython import const +import time, machine, bluetooth + +TIMEOUT_MS = 5000 + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_WRITE_DONE = const(17) +_IRQ_GATTC_NOTIFY = const(18) +_IRQ_MTU_EXCHANGED = const(21) + +SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") +CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444") +CHAR = ( + CHAR_UUID, + bluetooth.FLAG_READ | bluetooth.FLAG_WRITE | bluetooth.FLAG_NOTIFY, +) +SERVICE = ( + SERVICE_UUID, + (CHAR,), +) +SERVICES = (SERVICE,) + +waiting_events = {} + + +def irq(event, data): + global value_handle + if event == _IRQ_CENTRAL_CONNECT: + print("_IRQ_CENTRAL_CONNECT") + waiting_events[event] = data[0] + elif event == _IRQ_CENTRAL_DISCONNECT: + print("_IRQ_CENTRAL_DISCONNECT") + elif event == _IRQ_GATTS_WRITE: + print("_IRQ_GATTS_WRITE") + elif event == _IRQ_PERIPHERAL_CONNECT: + print("_IRQ_PERIPHERAL_CONNECT") + waiting_events[event] = data[0] + elif event == _IRQ_PERIPHERAL_DISCONNECT: + print("_IRQ_PERIPHERAL_DISCONNECT") + elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: + if data[-1] == CHAR_UUID: + print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) + waiting_events[event] = data[2] + elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: + print("_IRQ_GATTC_CHARACTERISTIC_DONE") + elif event == _IRQ_GATTC_WRITE_DONE: + print("_IRQ_GATTC_WRITE_DONE") + elif event == _IRQ_GATTC_NOTIFY: + print("_IRQ_GATTC_NOTIFY", len(data[-1]), chr(data[-1][0])) + elif event == _IRQ_MTU_EXCHANGED: + print("_IRQ_MTU_EXCHANGED", data[-1]) + waiting_events[event] = data[-1] + + if event not in waiting_events: + waiting_events[event] = None + + +def wait_for_event(event, timeout_ms): + t0 = time.ticks_ms() + while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: + if event in waiting_events: + return True + machine.idle() + return False + + +# Acting in peripheral role. +def instance0(): + multitest.globals(BDADDR=ble.config("mac")) + ((char_handle,),) = ble.gatts_register_services(SERVICES) + ble.gatts_set_buffer(char_handle, 500, False) + print("gap_advertise") + ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") + multitest.next() + try: + for i in range(7): + waiting_events.clear() + + if i == 1: + ble.config(mtu=200) + elif i == 2: + ble.config(mtu=400) + elif i == 3: + ble.config(mtu=50) + elif i >= 4: + ble.config(mtu=290) + else: + # This is the NimBLE default. + ble.config(mtu=256) + + # Wait for central to connect to us. + if not wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS): + return + conn_handle = waiting_events[_IRQ_CENTRAL_CONNECT] + + if i >= 4: + print("gattc_exchange_mtu") + ble.gattc_exchange_mtu(conn_handle) + + if not wait_for_event(_IRQ_MTU_EXCHANGED, TIMEOUT_MS): + return + mtu = waiting_events[_IRQ_MTU_EXCHANGED] + + print("gatts_notify") + ble.gatts_notify(conn_handle, char_handle, str(i) * 64) + + if not wait_for_event(_IRQ_GATTS_WRITE, TIMEOUT_MS): + return + + print("gatts_read") + data = ble.gatts_read(char_handle) + print("characteristic len:", len(data), chr(data[0])) + + # Wait for the central to disconnect. + if not wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS): + return + + print("gap_advertise") + ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") + + finally: + ble.active(0) + + +# Acting in central role. +def instance1(): + multitest.next() + try: + for i in range(7): + waiting_events.clear() + + if i < 4: + ble.config(mtu=300) + elif i == 5: + ble.config(mtu=190) + elif i == 6: + ble.config(mtu=350) + else: + ble.config(mtu=256) + + # Connect to peripheral and then disconnect. + print("gap_connect") + ble.gap_connect(*BDADDR) + if not wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS): + return + conn_handle = waiting_events[_IRQ_PERIPHERAL_CONNECT] + + if i < 4: + print("gattc_exchange_mtu") + ble.gattc_exchange_mtu(conn_handle) + + if not wait_for_event(_IRQ_MTU_EXCHANGED, TIMEOUT_MS): + return + mtu = waiting_events[_IRQ_MTU_EXCHANGED] + + if not wait_for_event(_IRQ_GATTC_NOTIFY, TIMEOUT_MS): + return + + print("gattc_discover_characteristics") + ble.gattc_discover_characteristics(conn_handle, 1, 65535) + if not wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS): + return + value_handle = waiting_events[_IRQ_GATTC_CHARACTERISTIC_RESULT] + + # Write 20 more than the MTU to test truncation. + print("gattc_write") + ble.gattc_write(conn_handle, value_handle, chr(ord("a") + i) * (mtu + 20), 1) + if not wait_for_event(_IRQ_GATTC_WRITE_DONE, TIMEOUT_MS): + return + + # Disconnect from peripheral. + print("gap_disconnect:", ble.gap_disconnect(conn_handle)) + if not wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS): + return + finally: + ble.active(0) + + +ble = bluetooth.BLE() +ble.active(1) +ble.irq(irq) diff --git a/tests/multi_bluetooth/ble_mtu.py.exp b/tests/multi_bluetooth/ble_mtu.py.exp new file mode 100644 index 0000000000000..1039a5da13c22 --- /dev/null +++ b/tests/multi_bluetooth/ble_mtu.py.exp @@ -0,0 +1,143 @@ +--- instance0 --- +gap_advertise +_IRQ_CENTRAL_CONNECT +_IRQ_MTU_EXCHANGED 256 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 253 a +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +_IRQ_MTU_EXCHANGED 200 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 197 b +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +_IRQ_MTU_EXCHANGED 300 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 297 c +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +_IRQ_MTU_EXCHANGED 50 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 47 d +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 256 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 253 e +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 190 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 187 f +_IRQ_CENTRAL_DISCONNECT +gap_advertise +_IRQ_CENTRAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 290 +gatts_notify +_IRQ_GATTS_WRITE +gatts_read +characteristic len: 287 g +_IRQ_CENTRAL_DISCONNECT +gap_advertise +--- instance1 --- +gap_connect +_IRQ_PERIPHERAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 256 +_IRQ_GATTC_NOTIFY 64 0 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 200 +_IRQ_GATTC_NOTIFY 64 1 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 300 +_IRQ_GATTC_NOTIFY 64 2 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +gattc_exchange_mtu +_IRQ_MTU_EXCHANGED 50 +_IRQ_GATTC_NOTIFY 47 3 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +_IRQ_MTU_EXCHANGED 256 +_IRQ_GATTC_NOTIFY 64 4 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +_IRQ_MTU_EXCHANGED 190 +_IRQ_GATTC_NOTIFY 64 5 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT +gap_connect +_IRQ_PERIPHERAL_CONNECT +_IRQ_MTU_EXCHANGED 290 +_IRQ_GATTC_NOTIFY 64 6 +gattc_discover_characteristics +_IRQ_GATTC_CHARACTERISTIC_RESULT UUID('00000000-1111-2222-3333-444444444444') +_IRQ_GATTC_CHARACTERISTIC_DONE +gattc_write +_IRQ_GATTC_WRITE_DONE +gap_disconnect: True +_IRQ_PERIPHERAL_DISCONNECT From 5c503de52166f6ebd4ef59b58b130a730782823d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Sep 2020 16:34:09 +1000 Subject: [PATCH 296/352] travis: Install setuptools for black code formatting. Signed-off-by: Damien George --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ebf9b55899dc9..45b67dc9fedc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ jobs: install: - sudo apt-get install uncrustify python3-pip - uncrustify --version + - pip3 install --user setuptools - pip3 install --user black - black --version script: From 52d6eeb409e0d4ff3a1fd6811f75b4953912eb38 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 10 Sep 2020 11:13:13 +1000 Subject: [PATCH 297/352] esp32/boards/sdkconfig.base: Set default IDF log level to ERROR. This commit changes the default logging level on all esp32 boards to ERROR. The esp32 port is now stable enough that it makes sense to remove the info logs to make the output cleaner, and to match other ports. More verbose logging can always be reenabled via esp.osdebug(). This also fixes issue #6354, error messages from NimBLE: the problem is that ble.active(True) will cause the IDF's NimBLE port to reset the "NimBLE" tag back to the default level (which was INFO prior to this commit). Even if the user had previously called esp.osdebug(None), because the IDF is setting the "NimBLE" tag back to the default (INFO), the messages will continue to be shown. The one quirk is that if the user does want to see the additional logging, then they must call esp.osdebug(0, 3) after ble.active(True) to undo the IDF setting the level back to the default (now ERROR). This means that it's impossible (via Python/esp.osdebug) to see stack-startup logging, you'd have to recompile with the default level changed back to INFO. --- ports/esp32/boards/sdkconfig.base | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index f44ec4e173e9b..67e2424a1246d 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -11,6 +11,11 @@ CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=y # Bootloader config CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +# Change default log level to "ERROR" (instead of "INFO") +CONFIG_LOG_DEFAULT_LEVEL_INFO=n +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL=1 + # ESP32-specific CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=n From ecb36d243915958e118a2c1d6b4bef8b490f0bc0 Mon Sep 17 00:00:00 2001 From: Mirko Vogt Date: Wed, 16 Sep 2020 23:57:40 +0000 Subject: [PATCH 298/352] esp32/modnetwork: Re-enable PPP support for IDF-SDK >=v4. PPP support was disabled in 96008ff59a8af9883af17d01b951029d9d02eec9 - marked as "unsupported" due to an early IDF v4 release. With the currently supported IDF v4.x version - 4c81978a - it appears to be working just fine. --- ports/esp32/modnetwork.c | 2 +- ports/esp32/network_ppp.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 325b27f74b262..029a8d143127c 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -755,8 +755,8 @@ STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&get_wlan_obj) }, #if !MICROPY_ESP_IDF_4 { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&get_lan_obj) }, - { MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&ppp_make_new_obj) }, #endif + { MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&ppp_make_new_obj) }, { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_phy_mode_obj) }, #if MODNETWORK_INCLUDE_CONSTANTS diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index c3046c9fc77b9..df57b817251d8 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -26,7 +26,6 @@ * THE SOFTWARE. */ -#if !MICROPY_ESP_IDF_4 #include "py/runtime.h" #include "py/mphal.h" #include "py/objtype.h" @@ -284,5 +283,3 @@ const mp_obj_type_t ppp_if_type = { .name = MP_QSTR_PPP, .locals_dict = (mp_obj_dict_t *)&ppp_if_locals_dict, }; - -#endif // !MICROPY_ESP_IDF_4 From b28758054b586fe41f56e462856d89f28c5e3f6a Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Sep 2020 16:47:29 +1000 Subject: [PATCH 299/352] esp8266: Remove release-specific manifest, disable osdebug by default. This commit removes release-specific builds for the esp8266 and makes the normal build of the GENERIC board more like the release build. This makes esp8266 like all the other ports, for which there is no difference between a daily build and a release build, making things less confusing. Release builds were previously defined by UART_OS=-1 (disable OS messages) and using manifest_release.py to include more frozen modules. The changes in this commit are: - Remove manifest_release.py. - Add existing modules from manifest_release.py (except example code) to the GENERIC board's manifest.py file. - Change UART_OS default to -1 to disable OS messages by default. Signed-off-by: Damien George --- ports/esp8266/Makefile | 2 +- ports/esp8266/boards/GENERIC/manifest.py | 19 +++++++++++++++++++ ports/esp8266/boards/manifest_release.py | 23 ----------------------- 3 files changed, 20 insertions(+), 24 deletions(-) delete mode 100644 ports/esp8266/boards/manifest_release.py diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 38b91a545dea3..02ea05f76e064 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -50,7 +50,7 @@ INC += -I$(ESP_SDK)/include # UART for "os" messages. 0 is normal UART as used by MicroPython REPL, # 1 is debug UART (tx only), -1 to disable. -UART_OS = 0 +UART_OS = -1 CFLAGS_XTENSA = -fsingle-precision-constant -Wdouble-promotion \ -D__ets__ -DICACHE_FLASH \ diff --git a/ports/esp8266/boards/GENERIC/manifest.py b/ports/esp8266/boards/GENERIC/manifest.py index 4e65b256f92c8..46f3b837be07f 100644 --- a/ports/esp8266/boards/GENERIC/manifest.py +++ b/ports/esp8266/boards/GENERIC/manifest.py @@ -1,2 +1,21 @@ +# base modules include("$(PORT_DIR)/boards/manifest.py") + +# uasyncio include("$(MPY_DIR)/extmod/uasyncio/manifest.py") + +# drivers +freeze("$(MPY_DIR)/drivers/display", "ssd1306.py") + +# Libraries from micropython-lib, include only if the library directory exists +if os.path.isdir(convert_path("$(MPY_LIB_DIR)")): + # file utilities + freeze("$(MPY_LIB_DIR)/upysh", "upysh.py") + + # requests + freeze("$(MPY_LIB_DIR)/urequests", "urequests.py") + freeze("$(MPY_LIB_DIR)/urllib.urequest", "urllib/urequest.py") + + # umqtt + freeze("$(MPY_LIB_DIR)/umqtt.simple", "umqtt/simple.py") + freeze("$(MPY_LIB_DIR)/umqtt.robust", "umqtt/robust.py") diff --git a/ports/esp8266/boards/manifest_release.py b/ports/esp8266/boards/manifest_release.py deleted file mode 100644 index 5a3194ae9bd45..0000000000000 --- a/ports/esp8266/boards/manifest_release.py +++ /dev/null @@ -1,23 +0,0 @@ -include("manifest.py") - -# drivers -freeze("$(MPY_DIR)/drivers/display", "ssd1306.py") - -# file utilities -freeze("$(MPY_LIB_DIR)/upysh", "upysh.py") - -# requests -freeze("$(MPY_LIB_DIR)/urequests", "urequests.py") -freeze("$(MPY_LIB_DIR)/urllib.urequest", "urllib/urequest.py") - -# umqtt with examples -freeze("$(MPY_LIB_DIR)/umqtt.simple", "umqtt/simple.py") -freeze("$(MPY_LIB_DIR)/umqtt.robust", "umqtt/robust.py") -freeze("$(MPY_LIB_DIR)/umqtt.simple", "example_pub_button.py") -freeze("$(MPY_LIB_DIR)/umqtt.simple", "example_sub_led.py") - -# HTTP examples -freeze("$(MPY_DIR)/examples/network", "http_client.py") -freeze("$(MPY_DIR)/examples/network", "http_client_ssl.py") -freeze("$(MPY_DIR)/examples/network", "http_server.py") -freeze("$(MPY_DIR)/examples/network", "http_server_ssl.py") From bd7af6151d605d3fc8f70cb9ddf45b2fd7881f08 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Sep 2020 00:07:12 +1000 Subject: [PATCH 300/352] ports: Add utime.gmtime() function. To portably get the Epoch. This is simply aliased to localtime() on ports that are not timezone aware. Signed-off-by: Damien George --- docs/library/utime.rst | 14 ++++++++++---- ports/cc3200/mods/modutime.c | 1 + ports/esp32/modutime.c | 1 + ports/esp8266/modules/ntptime.py | 5 ++--- ports/esp8266/modutime.c | 1 + ports/stm32/modutime.c | 1 + ports/unix/modtime.c | 14 ++++++++++++-- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/library/utime.rst b/docs/library/utime.rst index 7fe83f5abe0cd..8c222ede5f80b 100644 --- a/docs/library/utime.rst +++ b/docs/library/utime.rst @@ -36,11 +36,17 @@ behave not as expected. Functions --------- -.. function:: localtime([secs]) +.. function:: gmtime([secs]) + localtime([secs]) - Convert a time expressed in seconds since the Epoch (see above) into an 8-tuple which - contains: (year, month, mday, hour, minute, second, weekday, yearday) - If secs is not provided or None, then the current time from the RTC is used. + Convert the time *secs* expressed in seconds since the Epoch (see above) into an + 8-tuple which contains: ``(year, month, mday, hour, minute, second, weekday, yearday)`` + If *secs* is not provided or None, then the current time from the RTC is used. + + The `gmtime()` function returns a date-time tuple in UTC, and `localtime()` returns a + date-time tuple in local time. + + The format of the entries in the 8-tuple are: * year includes the century (for example 2014). * month is 1-12 diff --git a/ports/cc3200/mods/modutime.c b/ports/cc3200/mods/modutime.c index a729d62f9cbb2..e77065ef450db 100644 --- a/ports/cc3200/mods/modutime.c +++ b/ports/cc3200/mods/modutime.c @@ -133,6 +133,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); 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_gmtime), MP_ROM_PTR(&time_localtime_obj) }, { 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) }, diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c index 0325bd469391d..1f93ce6c30c36 100644 --- a/ports/esp32/modutime.c +++ b/ports/esp32/modutime.c @@ -85,6 +85,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); 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_gmtime), MP_ROM_PTR(&time_localtime_obj) }, { 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) }, diff --git a/ports/esp8266/modules/ntptime.py b/ports/esp8266/modules/ntptime.py index 92ae6ab077cfb..dd07e46f1d3b4 100644 --- a/ports/esp8266/modules/ntptime.py +++ b/ports/esp8266/modules/ntptime.py @@ -29,12 +29,11 @@ def time(): return val - NTP_DELTA -# There's currently no timezone support in MicroPython, so -# utime.localtime() will return UTC time (as if it was .gmtime()) +# There's currently no timezone support in MicroPython, and the RTC is set in UTC time. def settime(): t = time() import machine import utime - tm = utime.localtime(t) + tm = utime.gmtime(t) machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0)) diff --git a/ports/esp8266/modutime.c b/ports/esp8266/modutime.c index 9e924121bbc78..a951f8f8d2ccc 100644 --- a/ports/esp8266/modutime.c +++ b/ports/esp8266/modutime.c @@ -108,6 +108,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); 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_gmtime), MP_ROM_PTR(&time_localtime_obj) }, { 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_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, diff --git a/ports/stm32/modutime.c b/ports/stm32/modutime.c index 77ec7468c99b6..9641ddb4733bf 100644 --- a/ports/stm32/modutime.c +++ b/ports/stm32/modutime.c @@ -132,6 +132,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); 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_gmtime), MP_ROM_PTR(&time_localtime_obj) }, { 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) }, diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index 84c83da268627..5343e479c7d92 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -132,7 +132,7 @@ STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_time_sleep_obj, mod_time_sleep); -STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, struct tm *(*time_func)(const time_t *timep)) { time_t t; if (n_args == 0) { t = time(NULL); @@ -144,7 +144,7 @@ STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { t = mp_obj_get_int(args[0]); #endif } - struct tm *tm = localtime(&t); + struct tm *tm = time_func(&t); mp_obj_t ret = mp_obj_new_tuple(9, NULL); @@ -165,6 +165,15 @@ STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { return ret; } + +STATIC mp_obj_t mod_time_gmtime(size_t n_args, const mp_obj_t *args) { + return mod_time_gm_local_time(n_args, args, gmtime); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_time_gmtime_obj, 0, 1, mod_time_gmtime); + +STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { + return mod_time_gm_local_time(n_args, args, localtime); +} STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_time_localtime_obj, 0, 1, mod_time_localtime); STATIC mp_obj_t mod_time_mktime(mp_obj_t tuple) { @@ -210,6 +219,7 @@ STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { { 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_gmtime), MP_ROM_PTR(&mod_time_gmtime_obj) }, { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&mod_time_localtime_obj) }, { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&mod_time_mktime_obj) }, }; From 8f20cdc353a9d1c1e5d43484b58e1eaf81ec52e0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Sep 2020 12:15:03 +1000 Subject: [PATCH 301/352] all: Rename absolute time-based functions to include "epoch". For time-based functions that work with absolute time there is the need for an Epoch, to set the zero-point at which the absolute time starts counting. Such functions include time.time() and filesystem stat return values. And different ports may use a different Epoch. To make it clearer what functions use the Epoch (whatever it may be), and make the ports more consistent with their use of the Epoch, this commit renames all Epoch related functions to include the word "epoch" in their name (and remove references to "2000"). Along with this rename, the following things have changed: - mp_hal_time_ns() is now specified to return the number of nanoseconds since the Epoch, rather than since 1970 (but since this is an internal function it doesn't change anything for the user). - littlefs timestamps on the esp8266 have been fixed (they were previously off by 30 years in nanoseconds). Otherwise, there is no functional change made by this commit. Signed-off-by: Damien George --- extmod/vfs_fat.c | 5 +---- extmod/vfs_lfs.c | 4 +++- extmod/vfs_lfsx.c | 6 ++---- lib/timeutils/timeutils.h | 43 +++++++++++++++++++++++++++---------- ports/esp32/fatfs_port.c | 2 +- ports/esp32/machine_rtc.c | 4 ++-- ports/esp32/modutime.c | 2 +- ports/esp32/mphalport.c | 3 +-- ports/esp8266/esp_mphal.c | 2 +- ports/esp8266/fatfs_port.c | 4 ++-- ports/esp8266/machine_rtc.c | 18 ++++++++-------- ports/esp8266/modmachine.c | 2 +- ports/esp8266/modmachine.h | 4 ++-- ports/esp8266/modutime.c | 8 +++---- ports/stm32/modutime.c | 4 ++-- ports/stm32/rtc.c | 4 ++-- py/mphal.h | 2 +- 17 files changed, 67 insertions(+), 50 deletions(-) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index ace5ba5b6547f..95b7ad9944116 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -311,7 +311,7 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { } else { mode |= MP_S_IFREG; } - mp_int_t seconds = timeutils_seconds_since_2000( + mp_int_t seconds = timeutils_seconds_since_epoch( 1980 + ((fno.fdate >> 9) & 0x7f), (fno.fdate >> 5) & 0x0f, fno.fdate & 0x1f, @@ -319,9 +319,6 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { (fno.ftime >> 5) & 0x3f, 2 * (fno.ftime & 0x1f) ); - #if MICROPY_EPOCH_IS_1970 - seconds += TIMEUTILS_SECONDS_1970_TO_2000; - #endif t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index a53f66f2d63c8..9cf3eb11083cf 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -26,6 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "lib/timeutils/timeutils.h" #include "extmod/vfs.h" #include "extmod/vfs_lfs.h" @@ -126,7 +127,8 @@ const char *mp_vfs_lfs2_make_path(mp_obj_vfs_lfs2_t *self, mp_obj_t path_in); mp_obj_t mp_vfs_lfs2_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in); STATIC void lfs_get_mtime(uint8_t buf[8]) { - uint64_t ns = mp_hal_time_ns(); + // On-disk storage of timestamps uses 1970 as the Epoch, so convert from host's Epoch. + uint64_t ns = timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(mp_hal_time_ns()); // Store "ns" to "buf" in little-endian format (essentially htole64). for (size_t i = 0; i < 8; ++i) { buf[i] = ns; diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index d00df53104f6f..35d5f03c59a2c 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -365,10 +365,8 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { for (size_t i = sizeof(mtime_buf); i > 0; --i) { ns = ns << 8 | mtime_buf[i - 1]; } - mtime = timeutils_seconds_since_2000_from_nanoseconds_since_1970(ns); - #if MICROPY_EPOCH_IS_1970 - mtime += TIMEUTILS_SECONDS_1970_TO_2000; - #endif + // On-disk storage of timestamps uses 1970 as the Epoch, so convert to host's Epoch. + mtime = timeutils_seconds_since_epoch_from_nanoseconds_since_1970(ns); } #endif diff --git a/lib/timeutils/timeutils.h b/lib/timeutils/timeutils.h index 08b0dc2e8559a..14da831dc8b58 100644 --- a/lib/timeutils/timeutils.h +++ b/lib/timeutils/timeutils.h @@ -42,14 +42,6 @@ typedef struct _timeutils_struct_time_t { uint16_t tm_yday; // 1..366 } timeutils_struct_time_t; -static inline uint64_t timeutils_seconds_since_2000_to_nanoseconds_since_1970(mp_uint_t s) { - return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; -} - -static inline mp_uint_t timeutils_seconds_since_2000_from_nanoseconds_since_1970(uint64_t ns) { - return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; -} - bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); @@ -63,10 +55,39 @@ mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); -static inline uint64_t timeutils_nanoseconds_since_1970(mp_uint_t year, mp_uint_t month, +// Select the Epoch used by the port. +#if MICROPY_EPOCH_IS_1970 + +static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - return timeutils_seconds_since_2000_to_nanoseconds_since_1970( - timeutils_seconds_since_2000(year, month, date, hour, minute, second)); + return timeutils_seconds_since_2000(year, month, date, hour, minute, second) + TIMEUTILS_SECONDS_1970_TO_2000; +} + +static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { + return ns / 1000000000ULL; +} + +static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(uint64_t ns) { + return ns; } +#else // Epoch is 2000 + +#define timeutils_seconds_since_epoch_to_struct_time timeutils_seconds_since_2000_to_struct_time +#define timeutils_seconds_since_epoch timeutils_seconds_since_2000 + +static inline uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) { + return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; +} + +static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { + return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; +} + +static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { + return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000ULL; +} + +#endif + #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H diff --git a/ports/esp32/fatfs_port.c b/ports/esp32/fatfs_port.c index 779ba1c88accc..cfc52853d08a9 100644 --- a/ports/esp32/fatfs_port.c +++ b/ports/esp32/fatfs_port.c @@ -34,7 +34,7 @@ DWORD get_fattime(void) { struct timeval tv; gettimeofday(&tv, NULL); timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(tv.tv_sec, &tm); + timeutils_seconds_since_epoch_to_struct_time(tv.tv_sec, &tm); return ((DWORD)(tm.tm_year - 1980) << 25) | ((DWORD)tm.tm_mon << 21) | ((DWORD)tm.tm_mday << 16) | ((DWORD)tm.tm_hour << 11) | ((DWORD)tm.tm_min << 5) | ((DWORD)tm.tm_sec >> 1); diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index c776eaa8d3d1c..6902ce85fd2c7 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -93,7 +93,7 @@ STATIC mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *ar gettimeofday(&tv, NULL); timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(tv.tv_sec, &tm); + timeutils_seconds_since_epoch_to_struct_time(tv.tv_sec, &tm); mp_obj_t tuple[8] = { mp_obj_new_int(tm.tm_year), @@ -114,7 +114,7 @@ STATIC mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *ar mp_obj_get_array_fixed_n(args[1], 8, &items); struct timeval tv = {0}; - tv.tv_sec = timeutils_seconds_since_2000(mp_obj_get_int(items[0]), mp_obj_get_int(items[1]), mp_obj_get_int(items[2]), mp_obj_get_int(items[4]), mp_obj_get_int(items[5]), mp_obj_get_int(items[6])); + tv.tv_sec = timeutils_seconds_since_epoch(mp_obj_get_int(items[0]), mp_obj_get_int(items[1]), mp_obj_get_int(items[2]), mp_obj_get_int(items[4]), mp_obj_get_int(items[5]), mp_obj_get_int(items[6])); tv.tv_usec = mp_obj_get_int(items[7]); settimeofday(&tv, NULL); diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c index 1f93ce6c30c36..ac3edb4e9b76f 100644 --- a/ports/esp32/modutime.c +++ b/ports/esp32/modutime.c @@ -44,7 +44,7 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { } else { seconds = mp_obj_get_int(args[0]); } - timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); mp_obj_t tuple[8] = { tuple[0] = mp_obj_new_int(tm.tm_year), tuple[1] = mp_obj_new_int(tm.tm_mon), diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 3a46edf1f75c1..ad571bf961886 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -200,8 +200,7 @@ void mp_hal_delay_us(uint32_t us) { uint64_t mp_hal_time_ns(void) { struct timeval tv; gettimeofday(&tv, NULL); - // gettimeofday returns seconds since 2000/1/1 - uint64_t ns = timeutils_seconds_since_2000_to_nanoseconds_since_1970(tv.tv_sec); + uint64_t ns = tv.tv_sec * 1000000000ULL; ns += (uint64_t)tv.tv_usec * 1000ULL; return ns; } diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 9e1b72e438ad7..54f9611e56233 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -139,7 +139,7 @@ void MP_FASTCODE(mp_hal_delay_ms)(uint32_t delay) { } uint64_t mp_hal_time_ns(void) { - return pyb_rtc_get_us_since_2000() * 1000ULL; + return pyb_rtc_get_us_since_epoch() * 1000ULL; } void ets_event_poll(void) { diff --git a/ports/esp8266/fatfs_port.c b/ports/esp8266/fatfs_port.c index 8cef0acec3c62..bbd10519359a0 100644 --- a/ports/esp8266/fatfs_port.c +++ b/ports/esp8266/fatfs_port.c @@ -33,10 +33,10 @@ DWORD get_fattime(void) { // TODO: Optimize division (there's no HW division support on ESP8266, // so it's expensive). - uint32_t secs = (uint32_t)(pyb_rtc_get_us_since_2000() / 1000000); + uint32_t secs = (uint32_t)(pyb_rtc_get_us_since_epoch() / 1000000); timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(secs, &tm); + timeutils_seconds_since_epoch_to_struct_time(secs, &tm); return ((DWORD)(tm.tm_year - 1980) << 25) | ((DWORD)tm.tm_mon << 21) | ((DWORD)tm.tm_mday << 16) | ((DWORD)tm.tm_hour << 11) | ((DWORD)tm.tm_min << 5) | ((DWORD)tm.tm_sec >> 1); diff --git a/ports/esp8266/machine_rtc.c b/ports/esp8266/machine_rtc.c index 1aa73f9b317f8..e7b750fd92c8d 100644 --- a/ports/esp8266/machine_rtc.c +++ b/ports/esp8266/machine_rtc.c @@ -84,7 +84,7 @@ STATIC mp_obj_t pyb_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_ return (mp_obj_t)&pyb_rtc_obj; } -void pyb_rtc_set_us_since_2000(uint64_t nowus) { +void pyb_rtc_set_us_since_epoch(uint64_t nowus) { uint32_t cal = system_rtc_clock_cali_proc(); // Save RTC ticks for overflow detection. rtc_last_ticks = system_get_rtc_time(); @@ -96,7 +96,7 @@ void pyb_rtc_set_us_since_2000(uint64_t nowus) { system_rtc_mem_write(MEM_DELTA_ADDR, &delta, sizeof(delta)); }; -uint64_t pyb_rtc_get_us_since_2000() { +uint64_t pyb_rtc_get_us_since_epoch() { uint32_t cal; int64_t delta; uint32_t rtc_ticks; @@ -120,17 +120,17 @@ uint64_t pyb_rtc_get_us_since_2000() { void rtc_prepare_deepsleep(uint64_t sleep_us) { // RTC time will reset at wake up. Let's be preared for this. - int64_t delta = pyb_rtc_get_us_since_2000() + sleep_us; + int64_t delta = pyb_rtc_get_us_since_epoch() + sleep_us; system_rtc_mem_write(MEM_DELTA_ADDR, &delta, sizeof(delta)); } STATIC mp_obj_t pyb_rtc_datetime(size_t n_args, const mp_obj_t *args) { if (n_args == 1) { // Get time - uint64_t msecs = pyb_rtc_get_us_since_2000() / 1000; + uint64_t msecs = pyb_rtc_get_us_since_epoch() / 1000; timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(msecs / 1000, &tm); + timeutils_seconds_since_epoch_to_struct_time(msecs / 1000, &tm); mp_obj_t tuple[8] = { mp_obj_new_int(tm.tm_year), @@ -149,8 +149,8 @@ STATIC mp_obj_t pyb_rtc_datetime(size_t n_args, const mp_obj_t *args) { mp_obj_t *items; mp_obj_get_array_fixed_n(args[1], 8, &items); - pyb_rtc_set_us_since_2000( - ((uint64_t)timeutils_seconds_since_2000( + pyb_rtc_set_us_since_epoch( + ((uint64_t)timeutils_seconds_since_epoch( mp_obj_get_int(items[0]), mp_obj_get_int(items[1]), mp_obj_get_int(items[2]), @@ -209,7 +209,7 @@ STATIC mp_obj_t pyb_rtc_alarm(mp_obj_t self_in, mp_obj_t alarm_id, mp_obj_t time } // set expiry time (in microseconds) - pyb_rtc_alarm0_expiry = pyb_rtc_get_us_since_2000() + (uint64_t)mp_obj_get_int(time_in) * 1000; + pyb_rtc_alarm0_expiry = pyb_rtc_get_us_since_epoch() + (uint64_t)mp_obj_get_int(time_in) * 1000; return mp_const_none; @@ -222,7 +222,7 @@ STATIC mp_obj_t pyb_rtc_alarm_left(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(MP_ERROR_TEXT("invalid alarm")); } - uint64_t now = pyb_rtc_get_us_since_2000(); + uint64_t now = pyb_rtc_get_us_since_epoch(); if (pyb_rtc_alarm0_expiry <= now) { return MP_OBJ_NEW_SMALL_INT(0); } else { diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index bc838b420645d..cb28d5b144827 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -132,7 +132,7 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) { // see if RTC.ALARM0 should wake the device if (pyb_rtc_alarm0_wake & MACHINE_WAKE_DEEPSLEEP) { - uint64_t t = pyb_rtc_get_us_since_2000(); + uint64_t t = pyb_rtc_get_us_since_epoch(); if (pyb_rtc_alarm0_expiry <= t) { sleep_us = 1; // alarm already expired so wake immediately } else { diff --git a/ports/esp8266/modmachine.h b/ports/esp8266/modmachine.h index f5cfc0fa2cd52..be4debd335a64 100644 --- a/ports/esp8266/modmachine.h +++ b/ports/esp8266/modmachine.h @@ -32,8 +32,8 @@ void pin_set(uint pin, int value); extern uint32_t pyb_rtc_alarm0_wake; extern uint64_t pyb_rtc_alarm0_expiry; -void pyb_rtc_set_us_since_2000(uint64_t nowus); -uint64_t pyb_rtc_get_us_since_2000(); +void pyb_rtc_set_us_since_epoch(uint64_t nowus); +uint64_t pyb_rtc_get_us_since_epoch(); void rtc_prepare_deepsleep(uint64_t sleep_us); #endif // MICROPY_INCLUDED_ESP8266_MODMACHINE_H diff --git a/ports/esp8266/modutime.c b/ports/esp8266/modutime.c index a951f8f8d2ccc..86534722c792f 100644 --- a/ports/esp8266/modutime.c +++ b/ports/esp8266/modutime.c @@ -58,11 +58,11 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { timeutils_struct_time_t tm; mp_int_t seconds; if (n_args == 0 || args[0] == mp_const_none) { - seconds = pyb_rtc_get_us_since_2000() / 1000 / 1000; + seconds = pyb_rtc_get_us_since_epoch() / 1000 / 1000; } else { seconds = mp_obj_get_int(args[0]); } - timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); mp_obj_t tuple[8] = { tuple[0] = mp_obj_new_int(tm.tm_year), tuple[1] = mp_obj_new_int(tm.tm_mon), @@ -98,10 +98,10 @@ STATIC mp_obj_t time_mktime(mp_obj_t tuple) { MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); /// \function time() -/// Returns the number of seconds, as an integer, since 1/1/2000. +/// Returns the number of seconds, as an integer, since the Epoch. STATIC mp_obj_t time_time(void) { // get date and time - return mp_obj_new_int(pyb_rtc_get_us_since_2000() / 1000 / 1000); + return mp_obj_new_int(pyb_rtc_get_us_since_epoch() / 1000 / 1000); } MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); diff --git a/ports/stm32/modutime.c b/ports/stm32/modutime.c index 9641ddb4733bf..2a37a130df2bb 100644 --- a/ports/stm32/modutime.c +++ b/ports/stm32/modutime.c @@ -76,7 +76,7 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { } else { mp_int_t seconds = mp_obj_get_int(args[0]); timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); mp_obj_t tuple[8] = { tuple[0] = mp_obj_new_int(tm.tm_year), tuple[1] = mp_obj_new_int(tm.tm_mon), @@ -125,7 +125,7 @@ STATIC mp_obj_t time_time(void) { RTC_TimeTypeDef time; HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN); - return mp_obj_new_int(timeutils_seconds_since_2000(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); + return mp_obj_new_int(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); } MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 4f759c4bc74f0..18ecf7750513b 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -452,8 +452,8 @@ uint64_t mp_hal_time_ns(void) { RTC_DateTypeDef date; HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN); - ns = timeutils_nanoseconds_since_1970( - 2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds); + ns = timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds); + ns *= 1000000000ULL; uint32_t usec = ((RTC_SYNCH_PREDIV - time.SubSeconds) * (1000000 / 64)) / ((RTC_SYNCH_PREDIV + 1) / 64); ns += usec * 1000; #endif diff --git a/py/mphal.h b/py/mphal.h index 6d11f6ddc0836..0d4b1224e5cec 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -76,7 +76,7 @@ mp_uint_t mp_hal_ticks_cpu(void); #endif #ifndef mp_hal_time_ns -// Nanoseconds since 1970/1/1. +// Nanoseconds since the Epoch. uint64_t mp_hal_time_ns(void); #endif From 9d1983f078150b0f1da7bfb2e55c0ac823c328b6 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 16 Sep 2020 12:11:26 +1000 Subject: [PATCH 302/352] py/dynruntime.h: Add mp_import_* and mp_load/store_*. These functions already exist in the fun table, and this commit just adds convenience macros for them. --- py/dynruntime.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/py/dynruntime.h b/py/dynruntime.h index 696746f7510ff..eb8301284e1fc 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -163,9 +163,15 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { /******************************************************************************/ // General runtime functions -#define mp_load_name(qst) (mp_fun_table.load_name(qst)) -#define mp_load_global(qst) (mp_fun_table.load_global(qst)) -#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj))) +#define mp_load_name(qst) (mp_fun_table.load_name((qst))) +#define mp_load_global(qst) (mp_fun_table.load_global((qst))) +#define mp_load_attr(base, attr) (mp_fun_table.load_attr((base), (attr))) +#define mp_load_method(base, attr, dest) (mp_fun_table.load_method((base), (attr), (dest))) +#define mp_load_super_method(attr, dest) (mp_fun_table.load_super_method((attr), (dest))) +#define mp_store_name(qst, obj) (mp_fun_table.store_name((qst), (obj))) +#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj))) +#define mp_store_attr(base, attr, val) (mp_fun_table.store_attr((base), (attr), (val))) + #define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj))) #define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs))) @@ -193,6 +199,13 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { #define MP_DYNRUNTIME_MAKE_FUNCTION(f) \ (mp_make_function_from_raw_code((rc.fun_data = (f), &rc), MP_OBJ_NULL, MP_OBJ_NULL)) +#define mp_import_name(name, fromlist, level) \ + (mp_fun_table.import_name((name), (fromlist), (level))) +#define mp_import_from(module, name) \ + (mp_fun_table.import_from((module), (name))) +#define mp_import_all(module) \ + (mp_fun_table.import_all((module)) + /******************************************************************************/ // Exceptions From 8af9796b1642189f590520062b1a00e965d5985e Mon Sep 17 00:00:00 2001 From: Tweako Date: Wed, 16 Sep 2020 10:29:33 +0200 Subject: [PATCH 303/352] stm32/led: Support PWM output without TIM3. For example, the STM32WB55 doesn't have TIM3 but can still drive LEDs using PWM on other timers. --- ports/stm32/led.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/led.c b/ports/stm32/led.c index 923171884629f..adcb240cc0cc6 100644 --- a/ports/stm32/led.c +++ b/ports/stm32/led.c @@ -141,9 +141,11 @@ STATIC void led_pwm_init(int led) { case 2: __TIM2_CLK_ENABLE(); break; + #if defined(TIM3) case 3: __TIM3_CLK_ENABLE(); break; + #endif default: assert(0); } From c410a86814abde8ce05aaf46383f38ef9f981a8c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Sep 2020 13:26:24 +1000 Subject: [PATCH 304/352] tests/basics: Enable == and != special-method tests now that they work. These work since 3aab54bf434e7f025a91ea05052f1bac439fad8c Signed-off-by: Damien George --- tests/basics/special_methods.py | 4 ++++ tests/basics/special_methods2.py | 9 --------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/basics/special_methods.py b/tests/basics/special_methods.py index b56bc1c9c479d..d8af8e07979f2 100644 --- a/tests/basics/special_methods.py +++ b/tests/basics/special_methods.py @@ -100,6 +100,10 @@ def __int__(self): cud2 = Cud() str(cud1) +cud1 == cud1 +cud1 == cud2 +cud1 != cud1 +cud1 != cud2 cud1 < cud2 cud1 <= cud2 cud1 == cud2 diff --git a/tests/basics/special_methods2.py b/tests/basics/special_methods2.py index 09e43fff29973..31f330ab42b9d 100644 --- a/tests/basics/special_methods2.py +++ b/tests/basics/special_methods2.py @@ -129,12 +129,3 @@ def __dir__(self): # test that dir() does not delegate to __dir__ for the type print('a' in dir(Cud)) - -# TODO: the following operations are not supported on every ports -# -# ne is not supported, !(eq) is called instead -#cud1 != cud2 -# -# in the following test, cpython still calls __eq__ -# cud3=cud1 -# cud3==cud1 From 42342fa3cb30e2eac56ceb1d21b4eb60a0f406f3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Sep 2020 13:37:31 +1000 Subject: [PATCH 305/352] tests/basics: Add test for MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS ops. And enable this feature on unix, the coverage variant. The .exp test file is needed so the test can run on CPython versions prior to "@=" operator support. Signed-off-by: Damien George --- .../unix/variants/coverage/mpconfigvariant.h | 1 + tests/basics/class_inplace_op2.py | 73 +++++++++++++++++++ tests/basics/class_inplace_op2.py.exp | 12 +++ 3 files changed, 86 insertions(+) create mode 100644 tests/basics/class_inplace_op2.py create mode 100644 tests/basics/class_inplace_op2.py.exp diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 802c2fe5f7203..3de5294322502 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -39,6 +39,7 @@ #define MICROPY_WARNINGS_CATEGORY (1) #define MICROPY_MODULE_GETATTR (1) #define MICROPY_PY_DELATTR_SETATTR (1) +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (1) #define MICROPY_PY_BUILTINS_NEXT2 (1) diff --git a/tests/basics/class_inplace_op2.py b/tests/basics/class_inplace_op2.py new file mode 100644 index 0000000000000..0e3f2add412c2 --- /dev/null +++ b/tests/basics/class_inplace_op2.py @@ -0,0 +1,73 @@ +# Test inplace special methods enabled by MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS + + +class A: + def __imul__(self, other): + print("__imul__") + return self + + def __imatmul__(self, other): + print("__imatmul__") + return self + + def __ifloordiv__(self, other): + print("__ifloordiv__") + return self + + def __itruediv__(self, other): + print("__itruediv__") + return self + + def __imod__(self, other): + print("__imod__") + return self + + def __ipow__(self, other): + print("__ipow__") + return self + + def __ior__(self, other): + print("__ior__") + return self + + def __ixor__(self, other): + print("__ixor__") + return self + + def __iand__(self, other): + print("__iand__") + return self + + def __ilshift__(self, other): + print("__ilshift__") + return self + + def __irshift__(self, other): + print("__irshift__") + return self + + +a = A() + +try: + a *= None +except TypeError: + print("SKIP") + raise SystemExit + +a @= None +a //= None +a /= None +a %= None +a **= None +a |= None +a ^= None +a &= None +a <<= None +a >>= None + +# Normal operator should not fallback to inplace operator +try: + a * None +except TypeError: + print("TypeError") diff --git a/tests/basics/class_inplace_op2.py.exp b/tests/basics/class_inplace_op2.py.exp new file mode 100644 index 0000000000000..8c323b5178ef4 --- /dev/null +++ b/tests/basics/class_inplace_op2.py.exp @@ -0,0 +1,12 @@ +__imul__ +__imatmul__ +__ifloordiv__ +__itruediv__ +__imod__ +__ipow__ +__ior__ +__ixor__ +__iand__ +__ilshift__ +__irshift__ +TypeError From 3e16763201d879cfdd58fdf269fbe03a3d326675 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Sep 2020 14:08:22 +1000 Subject: [PATCH 306/352] stm32/rfcore: Fix FUS layout and size of ipcc_device_info_table_t. The device info table has a different layout when core 2 is in FUS mode. In particular it's larger than the 32 bytes used when in WS mode and if the correct amount of space is not allocated then the end of the table may be overwritten with other data (eg with FUS version 0.5.3). So update the structure to fix this. Also update rfcore.py to disable IRQs (which are enabled by rfcore.c), to not depend on uctypes, and to not require the asm_thumb emitter. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_WB55/rfcore.py | 19 ++++++------ ports/stm32/rfcore.c | 38 ++++++++++++++++++------ 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore.py b/ports/stm32/boards/NUCLEO_WB55/rfcore.py index e612499a7829f..cfe315605e541 100644 --- a/ports/stm32/boards/NUCLEO_WB55/rfcore.py +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore.py @@ -34,10 +34,15 @@ # to print out SRAM2A, register state and FUS/WS info. from machine import mem8, mem16, mem32 -import time, struct, uctypes +import time, struct import stm +def addressof(buf): + assert type(buf) is bytearray + return mem32[id(buf) + 12] + + class Flash: FLASH_KEY1 = 0x45670123 FLASH_KEY2 = 0xCDEF89AB @@ -68,7 +73,7 @@ def write(self, addr, buf): self.wait_not_busy() cr = 1 << 0 # PG mem32[stm.FLASH + stm.FLASH_CR] = cr - buf_addr = uctypes.addressof(buf) + buf_addr = addressof(buf) off = 0 while off < len(buf): mem32[addr + off] = mem32[buf_addr + off] @@ -111,13 +116,6 @@ def copy_file_to_flash(filename, addr): OCF_FUS_START_WS = const(0x5A) OCF_BLE_INIT = const(0x66) - -@micropython.asm_thumb -def asm_sev_wfe(): - data(2, 0xBF40) # sev - data(2, 0xBF20) # wfe - - TABLE_DEVICE_INFO = const(0) TABLE_BLE = const(1) TABLE_SYS = const(3) @@ -199,6 +197,9 @@ def ipcc_init(): BLE_EVT_QUEUE = get_ipcc_table_word(TABLE_BLE, 2) BLE_HCI_ACL_DATA_BUF = get_ipcc_table_word(TABLE_BLE, 3) + # Disable interrupts, the code here uses polling + mem32[stm.IPCC + stm.IPCC_C1CR] = 0 + print("IPCC initialised") print("SYS: 0x%08x 0x%08x" % (SYS_CMD_BUF, SYS_SYS_QUEUE)) print("BLE: 0x%08x 0x%08x 0x%08x" % (BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE)) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index ca2ad747c4bb8..dc23bbe0b2a69 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -94,15 +94,35 @@ typedef struct _parse_hci_info_t { // [16:23] = SRAM2b (Number of 1k sectors) // [24:31] = SRAM2a (Number of 1k sectors) -typedef struct __attribute__((packed)) _ipcc_device_info_table_t { - uint32_t safeboot_version; - uint32_t fus_version; - uint32_t fus_memorysize; - uint32_t fus_info; - uint32_t fw_version; - uint32_t fw_memorysize; - uint32_t fw_infostack; - uint32_t fw_reserved; +typedef union __attribute__((packed)) _ipcc_device_info_table_t { + struct { + uint32_t table_state; + uint8_t reserved0; + uint8_t last_fus_state; + uint8_t last_ws_state; + uint8_t ws_type; + uint32_t safeboot_version; + uint32_t fus_version; + uint32_t fus_memorysize; + uint32_t ws_version; + uint32_t ws_memorysize; + uint32_t ws_ble_info; + uint32_t ws_thread_info; + uint32_t reserved1; + uint64_t uid64; + uint16_t device_id; + uint16_t pad; + } fus; + struct { + uint32_t safeboot_version; + uint32_t fus_version; + uint32_t fus_memorysize; + uint32_t fus_info; + uint32_t fw_version; + uint32_t fw_memorysize; + uint32_t fw_infostack; + uint32_t fw_reserved; + } ws; } ipcc_device_info_table_t; typedef struct __attribute__((packed)) _ipcc_ble_table_t { From 71adf506ce43b55b859f81f191ac0826928bbdd5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Sep 2020 15:05:00 +1000 Subject: [PATCH 307/352] extmod/vfs: Fix lookup of entry in root dir so it fails correctly. Prior to this commit, uos.chdir('/') followed by uos.stat('noexist') would succeed that stat even though the entry did not exist (some other functions like listdir would have similar issues). This is because, if the current directory was the root and the path was relative, mp_vfs_lookup_path would return success for bad paths. Signed-off-by: Damien George --- extmod/vfs.c | 8 ++------ tests/extmod/vfs_basic.py | 8 ++++++++ tests/extmod/vfs_basic.py.exp | 12 ++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index d1291068ad8d6..3cb7af1b434f9 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -83,12 +83,8 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { } } - // if we get here then there's nothing mounted on / - - if (is_abs) { - // path began with / and was not found - return MP_VFS_NONE; - } + // if we get here then there's nothing mounted on /, so the path doesn't exist + return MP_VFS_NONE; } // a relative path within a mounted device diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py index 62b2a277381d6..9a9ef2ca61be3 100644 --- a/tests/extmod/vfs_basic.py +++ b/tests/extmod/vfs_basic.py @@ -74,6 +74,14 @@ def open(self, file, mode): # getcwd when in root dir print(uos.getcwd()) +# test operations on the root directory with nothing mounted, they should all fail +for func in ("chdir", "listdir", "mkdir", "remove", "rmdir", "stat"): + for arg in ("x", "/x"): + try: + getattr(uos, func)(arg) + except OSError: + print(func, arg, "OSError") + # basic mounting and listdir uos.mount(Filesystem(1), "/test_mnt") print(uos.listdir()) diff --git a/tests/extmod/vfs_basic.py.exp b/tests/extmod/vfs_basic.py.exp index ebca31030451b..536bb4c805d30 100644 --- a/tests/extmod/vfs_basic.py.exp +++ b/tests/extmod/vfs_basic.py.exp @@ -1,6 +1,18 @@ (16384, 0, 0, 0, 0, 0, 0, 0, 0, 0) True / +chdir x OSError +chdir /x OSError +listdir x OSError +listdir /x OSError +mkdir x OSError +mkdir /x OSError +remove x OSError +remove /x OSError +rmdir x OSError +rmdir /x OSError +stat x OSError +stat /x OSError 1 mount False False ['test_mnt'] ('test_mnt', 16384, 0) From bada8c923195622aee80faf7acf003bc7852459d Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 22 Sep 2020 13:59:47 +0200 Subject: [PATCH 308/352] windows: Update build instructions in README. Make the instructions more complete by documenting all needed steps for starting from scratch. Also add a section for MSYS2 since the Travis build uses it as well and it's a good alternative for Cygwin. Remove the mingw32 reference since it's not readily available anymore in most Linux distros nor compiles successfully. --- ports/windows/README.md | 60 ++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/ports/windows/README.md b/ports/windows/README.md index f1bd405513b99..8d907d1b7243a 100644 --- a/ports/windows/README.md +++ b/ports/windows/README.md @@ -3,46 +3,73 @@ It is based on Unix port, and expected to remain so. The port requires additional testing, debugging, and patches. Please consider to contribute. +All gcc-based builds use the gcc compiler from [Mingw-w64](mingw-w64.org), +which is the advancement of the original mingw project. The latter is +getting obsolete and is not actively supported by MicroPython. + +Build instruction assume you're in the ports/windows directory. Building on Debian/Ubuntu Linux system --------------------------------------- - sudo apt-get install gcc-mingw-w64 + sudo apt-get install python3 build-essential gcc-mingw-w64 + make -C ../../mpy-cross make CROSS_COMPILE=i686-w64-mingw32- -If for some reason the mingw-w64 crosscompiler is not available, you can try -mingw32 instead, but it comes with a really old gcc which may produce some -spurious errors (you may need to disable -Werror): - - sudo apt-get install mingw32 mingw32-binutils mingw32-runtime - make CROSS_COMPILE=i586-mingw32msvc- - Building under Cygwin --------------------- -Install following packages using cygwin's setup.exe: +Install Cygwin, then install following packages using Cygwin's setup.exe: * mingw64-i686-gcc-core * mingw64-x86_64-gcc-core * make +Also install the python3 package, or install Python globally for Windows (see below). + Build using: + make -C ../../mpy-cross CROSS_COMPILE=i686-w64-mingw32- make CROSS_COMPILE=i686-w64-mingw32- Or for 64bit: + make -C ../../mpy-cross CROSS_COMPILE=x86_64-w64-mingw32- make CROSS_COMPILE=x86_64-w64-mingw32- +Building under MSYS2 +-------------------- + +Install MSYS2 from http://repo.msys2.org/distrib, start the msys2.exe shell and +install the build tools: + + pacman -Syuu + pacman -S make mingw-w64-x86_64-gcc pkg-config python3 + +Start the mingw64.exe shell and build: + + make -C ../../mpy-cross STRIP=echo SIZE=echo + make + + Building using MS Visual Studio 2013 (or higher) ------------------------------------------------ -In the IDE, open `micropython.vcxproj` and build. +Install Python. There are several ways to do this, for example: download and install the +latest Python 3 release from https://www.python.org/downloads/windows or from +https://docs.conda.io/en/latest/miniconda.html, +or open the Microsoft Store app and search for Python and install it. + +Install Visual Studio and the C++ toolset (for recent versions: install +the free Visual Studio Community edition and the *Desktop development with C++* workload). + +In the IDE, open `micropython-cross.vcxproj` and `micropython.vcxproj` and build. To build from the command line: + msbuild ../../mpy-cross/mpy-cross.vcxproj msbuild micropython.vcxproj __Stack usage__ @@ -57,6 +84,19 @@ There are several ways to deal with this: See [issue 2927](https://github.com/micropython/micropython/issues/2927) for more information. +Running the tests +----------------- + +This is similar for all ports: + + cd ../../tests + python3 ./run-tests + +Depending on the combination of platform and Python version used it might be +needed to first set the MICROPY_MICROPYTHON environment variable to +the full path of micropython.exe. + + Running on Linux using Wine --------------------------- From ca017841d65b5f9da5eb4e6e3ca66b009bc54fc4 Mon Sep 17 00:00:00 2001 From: Iyassou Shimels Date: Wed, 23 Sep 2020 22:45:22 +0300 Subject: [PATCH 309/352] py/objstr: Make bytes(bytes_obj) return bytes_obj. Calling the bytes constructor on a bytes object returns the original bytes object. This saves allocating a new instance, and matches CPython. Signed-off-by: Iyassou Shimels --- py/objstr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/objstr.c b/py/objstr.c index a276a255e516f..84728e6f2d919 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -205,6 +205,10 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size return mp_const_empty_bytes; } + if (mp_obj_is_type(args[0], &mp_type_bytes)) { + return args[0]; + } + if (mp_obj_is_str(args[0])) { if (n_args < 2 || n_args > 3) { goto wrong_args; From 50e34f979c90584273a67ecd9189640417a60960 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Sep 2020 23:17:56 +1000 Subject: [PATCH 310/352] py/objarray.h: Add mp_obj_memoryview_init() helper function. Signed-off-by: Damien George --- py/objarray.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/py/objarray.h b/py/objarray.h index 2fb6e2c9158f4..94c31c969386b 100644 --- a/py/objarray.h +++ b/py/objarray.h @@ -49,4 +49,14 @@ typedef struct _mp_obj_array_t { void *items; } mp_obj_array_t; +#if MICROPY_PY_BUILTINS_MEMORYVIEW +static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) { + self->base.type = &mp_type_memoryview; + self->typecode = typecode; + self->free = offset; + self->len = len; + self->items = items; +} +#endif + #endif // MICROPY_INCLUDED_PY_OBJARRAY_H From 81f2162ca0e926c9a4a1787a3863d94d86be0b36 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Sep 2020 23:18:16 +1000 Subject: [PATCH 311/352] extmod/modbluetooth: Change module-owned bytes objects to memoryview. A read-only memoryview object is a better representation of the data, which is owned by the ubluetooth module and may change between calls to the user's irq callback function. Signed-off-by: Damien George --- docs/library/ubluetooth.rst | 22 +++++++++++----- extmod/modbluetooth.c | 26 +++++++------------ tests/multi_bluetooth/ble_characteristic.py | 6 ++--- tests/multi_bluetooth/ble_gap_device_name.py | 2 +- .../multi_bluetooth/ble_gatt_data_transfer.py | 2 +- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/library/ubluetooth.rst b/docs/library/ubluetooth.rst index 03d03583a1cce..f94ad3a612c07 100644 --- a/docs/library/ubluetooth.rst +++ b/docs/library/ubluetooth.rst @@ -88,12 +88,22 @@ Event Handling arguments, ``event`` (which will be one of the codes below) and ``data`` (which is an event-specific tuple of values). - **Note:** the ``addr``, ``adv_data``, ``char_data``, ``notify_data``, and - ``uuid`` entries in the tuples are references to data managed by the - :mod:`ubluetooth` module (i.e. the same instance will be re-used across - multiple calls to the event handler). If your program wants to use this - data outside of the handler, then it must copy them first, e.g. by using - ``bytes(addr)`` or ``bluetooth.UUID(uuid)``. + **Note:** As an optimisation to prevent unnecessary allocations, the ``addr``, + ``adv_data``, ``char_data``, ``notify_data``, and ``uuid`` entries in the + tuples are read-only memoryview instances pointing to ubluetooth's internal + ringbuffer, and are only valid during the invocation of the IRQ handler + function. If your program needs to save one of these values to access after + the IRQ handler has returned (e.g. by saving it in a class instance or global + variable), then it needs to take a copy of the data, either by using ``bytes()`` + or ``bluetooth.UUID()``, like this:: + + connected_addr = bytes(addr) # equivalently: adv_data, char_data, or notify_data + matched_uuid = bluetooth.UUID(uuid) + + For example, the IRQ handler for a scan result might inspect the ``adv_data`` + to decide if it's the correct device, and only then copy the address data to be + used elsewhere in the program. And to print data from within the IRQ handler, + ``print(bytes(addr))`` will be needed. An event handler showing all possible events:: diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index daf9cd0d1984b..57f69433a1a97 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -32,7 +32,6 @@ #include "py/mphal.h" #include "py/obj.h" #include "py/objarray.h" -#include "py/objstr.h" #include "py/qstr.h" #include "py/runtime.h" #include "extmod/modbluetooth.h" @@ -62,9 +61,8 @@ typedef struct { mp_obj_t irq_data_tuple; uint8_t irq_data_addr_bytes[6]; uint16_t irq_data_data_alloc; - uint8_t *irq_data_data_bytes; - mp_obj_str_t irq_data_addr; - mp_obj_str_t irq_data_data; + mp_obj_array_t irq_data_addr; + mp_obj_array_t irq_data_data; mp_obj_bluetooth_uuid_t irq_data_uuid; ringbuf_t ringbuf; #if MICROPY_PY_BLUETOOTH_GATTS_ON_READ_CALLBACK @@ -262,11 +260,9 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, #endif // Pre-allocated buffers for address, payload and uuid. - o->irq_data_addr.base.type = &mp_type_bytes; - o->irq_data_addr.data = o->irq_data_addr_bytes; + mp_obj_memoryview_init(&o->irq_data_addr, 'B', 0, 0, o->irq_data_addr_bytes); o->irq_data_data_alloc = MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN(MICROPY_PY_BLUETOOTH_RINGBUF_SIZE); - o->irq_data_data.base.type = &mp_type_bytes; - o->irq_data_data.data = m_new(uint8_t, o->irq_data_data_alloc); + mp_obj_memoryview_init(&o->irq_data_data, 'B', 0, 0, m_new(uint8_t, o->irq_data_data_alloc)); o->irq_data_uuid.base.type = &bluetooth_uuid_type; // Allocate the default ringbuf. @@ -352,7 +348,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map // Get old buffer sizes and pointers uint8_t *old_ringbuf_buf = self->ringbuf.buf; size_t old_ringbuf_alloc = self->ringbuf.size; - uint8_t *old_irq_data_buf = (uint8_t *)self->irq_data_data.data; + uint8_t *old_irq_data_buf = (uint8_t *)self->irq_data_data.items; size_t old_irq_data_alloc = self->irq_data_data_alloc; // Atomically update the ringbuf and irq data @@ -362,7 +358,7 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map self->ringbuf.iget = 0; self->ringbuf.iput = 0; self->irq_data_data_alloc = irq_data_alloc; - self->irq_data_data.data = irq_data; + self->irq_data_data.items = irq_data; MICROPY_PY_BLUETOOTH_EXIT // Free old buffers @@ -850,7 +846,7 @@ const mp_obj_module_t mp_module_ubluetooth = { #include -STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size_t n_u16, size_t n_u8, mp_obj_str_t *bytes_addr, size_t n_i8, mp_obj_bluetooth_uuid_t *uuid, mp_obj_str_t *bytes_data) { +STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size_t n_u16, size_t n_u8, mp_obj_array_t *bytes_addr, size_t n_i8, mp_obj_bluetooth_uuid_t *uuid, mp_obj_array_t *bytes_data) { assert(ringbuf_avail(ringbuf) >= n_u16 * 2 + n_u8 + (bytes_addr ? 6 : 0) + n_i8 + (uuid ? 1 : 0) + (bytes_data ? 1 : 0)); size_t j = 0; @@ -863,8 +859,7 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size if (bytes_addr) { bytes_addr->len = 6; for (size_t i = 0; i < bytes_addr->len; ++i) { - // cast away const, this is actually bt->irq_addr_bytes. - ((uint8_t *)bytes_addr->data)[i] = ringbuf_get(ringbuf); + ((uint8_t *)bytes_addr->items)[i] = ringbuf_get(ringbuf); } data_tuple->items[j++] = MP_OBJ_FROM_PTR(bytes_addr); } @@ -880,12 +875,11 @@ STATIC void ringbuf_extract(ringbuf_t *ringbuf, mp_obj_tuple_t *data_tuple, size #endif // The code that enqueues into the ringbuf should ensure that it doesn't // put more than bt->irq_data_data_alloc bytes into the ringbuf, because - // that's what's available here in bt->irq_data_bytes. + // that's what's available here. if (bytes_data) { bytes_data->len = ringbuf_get16(ringbuf); for (size_t i = 0; i < bytes_data->len; ++i) { - // cast away const, this is actually bt->irq_data_bytes. - ((uint8_t *)bytes_data->data)[i] = ringbuf_get(ringbuf); + ((uint8_t *)bytes_data->items)[i] = ringbuf_get(ringbuf); } data_tuple->items[j++] = MP_OBJ_FROM_PTR(bytes_data); } diff --git a/tests/multi_bluetooth/ble_characteristic.py b/tests/multi_bluetooth/ble_characteristic.py index d918d9aefcf03..026b9d551c2b9 100644 --- a/tests/multi_bluetooth/ble_characteristic.py +++ b/tests/multi_bluetooth/ble_characteristic.py @@ -54,15 +54,15 @@ def irq(event, data): print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) value_handle = data[2] elif event == _IRQ_GATTC_READ_RESULT: - print("_IRQ_GATTC_READ_RESULT", data[-1]) + print("_IRQ_GATTC_READ_RESULT", bytes(data[-1])) elif event == _IRQ_GATTC_READ_DONE: print("_IRQ_GATTC_READ_DONE", data[-1]) elif event == _IRQ_GATTC_WRITE_DONE: print("_IRQ_GATTC_WRITE_DONE", data[-1]) elif event == _IRQ_GATTC_NOTIFY: - print("_IRQ_GATTC_NOTIFY", data[-1]) + print("_IRQ_GATTC_NOTIFY", bytes(data[-1])) elif event == _IRQ_GATTC_INDICATE: - print("_IRQ_GATTC_INDICATE", data[-1]) + print("_IRQ_GATTC_INDICATE", bytes(data[-1])) elif event == _IRQ_GATTS_INDICATE_DONE: print("_IRQ_GATTS_INDICATE_DONE", data[-1]) diff --git a/tests/multi_bluetooth/ble_gap_device_name.py b/tests/multi_bluetooth/ble_gap_device_name.py index 92ea94278a647..fbc9d80baeb08 100644 --- a/tests/multi_bluetooth/ble_gap_device_name.py +++ b/tests/multi_bluetooth/ble_gap_device_name.py @@ -36,7 +36,7 @@ def irq(event, data): print("_IRQ_GATTC_CHARACTERISTIC_RESULT", data[-1]) value_handle = data[2] elif event == _IRQ_GATTC_READ_RESULT: - print("_IRQ_GATTC_READ_RESULT", data[-1]) + print("_IRQ_GATTC_READ_RESULT", bytes(data[-1])) if waiting_event is not None: if isinstance(waiting_event, int) and event == waiting_event: diff --git a/tests/multi_bluetooth/ble_gatt_data_transfer.py b/tests/multi_bluetooth/ble_gatt_data_transfer.py index 240f048607c92..944c9e2d2a55f 100644 --- a/tests/multi_bluetooth/ble_gatt_data_transfer.py +++ b/tests/multi_bluetooth/ble_gatt_data_transfer.py @@ -67,7 +67,7 @@ def irq(event, data): elif event == _IRQ_GATTC_WRITE_DONE: print("_IRQ_GATTC_WRITE_DONE", data[-1]) elif event == _IRQ_GATTC_NOTIFY: - print("_IRQ_GATTC_NOTIFY", data[-1]) + print("_IRQ_GATTC_NOTIFY", bytes(data[-1])) if waiting_event is not None: if (isinstance(waiting_event, int) and event == waiting_event) or ( From c8ade2bd7f5d0e089098bcc07eb77a770f3da726 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Sep 2020 22:47:25 +1000 Subject: [PATCH 312/352] docs/develop: Add notes on prerequisite tools for building native .mpy. Signed-off-by: Damien George --- docs/develop/natmod.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index dc9f82e773874..a4c188719a4d9 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -21,7 +21,8 @@ language which can be compiled to stand-alone machine code can be put into a A native .mpy module is built using the ``mpy_ld.py`` tool, which is found in the ``tools/`` directory of the project. This tool takes a set of object files -(.o files) and links them together to create a native .mpy files. +(.o files) and links them together to create a native .mpy files. It requires +CPython 3 and the library pyelftools v0.25 or greater. Supported features and limitations ---------------------------------- @@ -179,6 +180,14 @@ The file ``Makefile`` contains: Compiling the module -------------------- +The prerequisite tools needed to build a native .mpy file are: + +* The MicroPython repository (at least the ``py/`` and ``tools/`` directories). +* CPython 3, and the library pyelftools (eg ``pip install 'pyelftools>=0.25'``). +* GNU make. +* A C compiler for the target architecture (if C source is used). +* Optionally ``mpy-cross``, built from the MicroPython repository (if .py source is used). + Be sure to select the correct ``ARCH`` for the target you are going to run on. Then build with:: From 9123b67d641ba708a4ea3e5cd50665f0a73c6c8a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Sep 2020 12:03:30 +1000 Subject: [PATCH 313/352] tests/run-tests: Use -BS flags when running CPython. The use of -S ensures that only the CPython standard library is accessible, which makes tests run the same regardless of any site-packages that are installed. It also improves start-up time of CPython, reducing the overall time spent running the test suite. tests/basics/containment.py is updated to work around issue with old Python versions not being able to str-format a dict-keys object, which becomes apparent when -S is used. Signed-off-by: Damien George --- tests/basics/containment.py | 4 ++-- tests/run-tests | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/basics/containment.py b/tests/basics/containment.py index 4c94a9bae4c81..24317ddd233b9 100644 --- a/tests/basics/containment.py +++ b/tests/basics/containment.py @@ -1,8 +1,8 @@ # sets, see set_containment for i in 1, 2: for o in {1:2}, {1:2}.keys(): - print("{} in {}: {}".format(i, o, i in o)) - print("{} not in {}: {}".format(i, o, i not in o)) + print("{} in {}: {}".format(i, str(o), i in o)) + print("{} not in {}: {}".format(i, str(o), i not in o)) haystack = "supercalifragilistc" for needle in [haystack[i:] for i in range(len(haystack))]: diff --git a/tests/run-tests b/tests/run-tests index a7b88ecdd3265..eebc8c4252700 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -26,6 +26,10 @@ else: CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/unix/micropython')) +# Use CPython options to not save .pyc files, to only access the core standard library +# (not site packages which may clash with u-module names), and improve start up time. +CPYTHON3_CMD = [CPYTHON3, "-BS"] + # mpy-cross is only needed if --via-mpy command-line arg is passed MPYCROSS = os.getenv('MICROPY_MPYCROSS', base_path('../mpy-cross/mpy-cross')) @@ -319,7 +323,7 @@ def run_tests(pyb, tests, args, result_dir): upy_float_precision = int(upy_float_precision) has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' - cpy_byteorder = subprocess.check_output([CPYTHON3, base_path('feature_check/byteorder.py')]) + cpy_byteorder = subprocess.check_output(CPYTHON3_CMD + [base_path('feature_check/byteorder.py')]) skip_endian = (upy_byteorder != cpy_byteorder) # These tests don't test slice explicitly but rather use it to perform the test @@ -498,7 +502,7 @@ def run_tests(pyb, tests, args, result_dir): else: # run CPython to work out expected output try: - output_expected = subprocess.check_output([CPYTHON3, '-B', test_file]) + output_expected = subprocess.check_output(CPYTHON3_CMD + [test_file]) if args.write_exp: with open(test_file_expected, 'wb') as f: f.write(output_expected) From 0fd0eb00aa5b9d311046d48d73a8cfabb30d7dd6 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 25 Sep 2020 03:15:22 +1000 Subject: [PATCH 314/352] examples/bluetooth: Update to use positional-only args to irq(). To match 6a6a5f9e151473bdcc1d14725d680691ff665a82. --- examples/bluetooth/ble_simple_central.py | 2 +- examples/bluetooth/ble_simple_peripheral.py | 2 +- examples/bluetooth/ble_temperature.py | 2 +- examples/bluetooth/ble_temperature_central.py | 2 +- examples/bluetooth/ble_uart_peripheral.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/bluetooth/ble_simple_central.py b/examples/bluetooth/ble_simple_central.py index f0a3297d322e1..a6bbdc6ee0795 100644 --- a/examples/bluetooth/ble_simple_central.py +++ b/examples/bluetooth/ble_simple_central.py @@ -45,7 +45,7 @@ class BLESimpleCentral: def __init__(self, ble): self._ble = ble self._ble.active(True) - self._ble.irq(handler=self._irq) + self._ble.irq(self._irq) self._reset() diff --git a/examples/bluetooth/ble_simple_peripheral.py b/examples/bluetooth/ble_simple_peripheral.py index f5e7661926ba8..d2b134e7149e9 100644 --- a/examples/bluetooth/ble_simple_peripheral.py +++ b/examples/bluetooth/ble_simple_peripheral.py @@ -31,7 +31,7 @@ class BLESimplePeripheral: def __init__(self, ble, name="mpy-uart"): self._ble = ble self._ble.active(True) - self._ble.irq(handler=self._irq) + self._ble.irq(self._irq) ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,)) self._connections = set() self._write_callback = None diff --git a/examples/bluetooth/ble_temperature.py b/examples/bluetooth/ble_temperature.py index 001a26b114a0a..d375a62ffb8ac 100644 --- a/examples/bluetooth/ble_temperature.py +++ b/examples/bluetooth/ble_temperature.py @@ -35,7 +35,7 @@ class BLETemperature: def __init__(self, ble, name="mpy-temp"): self._ble = ble self._ble.active(True) - self._ble.irq(handler=self._irq) + self._ble.irq(self._irq) ((self._handle,),) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,)) self._connections = set() self._payload = advertising_payload( diff --git a/examples/bluetooth/ble_temperature_central.py b/examples/bluetooth/ble_temperature_central.py index 79830341789d4..96c4aad1445a1 100644 --- a/examples/bluetooth/ble_temperature_central.py +++ b/examples/bluetooth/ble_temperature_central.py @@ -56,7 +56,7 @@ class BLETemperatureCentral: def __init__(self, ble): self._ble = ble self._ble.active(True) - self._ble.irq(handler=self._irq) + self._ble.irq(self._irq) self._reset() diff --git a/examples/bluetooth/ble_uart_peripheral.py b/examples/bluetooth/ble_uart_peripheral.py index 59b35f7e6ad5e..6d167a871a75e 100644 --- a/examples/bluetooth/ble_uart_peripheral.py +++ b/examples/bluetooth/ble_uart_peripheral.py @@ -31,7 +31,7 @@ class BLEUART: def __init__(self, ble, name="mpy-uart", rxbuf=100): self._ble = ble self._ble.active(True) - self._ble.irq(handler=self._irq) + self._ble.irq(self._irq) ((self._tx_handle, self._rx_handle),) = self._ble.gatts_register_services((_UART_SERVICE,)) # Increase the size of the rx buffer and enable append mode. self._ble.gatts_set_buffer(self._rx_handle, rxbuf, True) From 319437d4bd7c97ab18275d3eaf1bc190554f9df7 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 25 Sep 2020 12:27:02 +1000 Subject: [PATCH 315/352] extmod/modure: Allow \\ in re.sub replacements. --- extmod/modure.c | 3 +++ tests/extmod/ure_sub.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/extmod/modure.c b/extmod/modure.c index 328c897d8890b..220587b42f9a3 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -343,6 +343,9 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { const char *end_match = match->caps[match_no * 2 + 1]; vstr_add_strn(&vstr_return, start_match, end_match - start_match); } + } else if (*repl == '\\') { + // Add the \ character + vstr_add_byte(&vstr_return, *repl++); } } else { // Just add the current byte from the replacement string diff --git a/tests/extmod/ure_sub.py b/tests/extmod/ure_sub.py index 953e7bf62a478..ae6ad28d621f4 100644 --- a/tests/extmod/ure_sub.py +++ b/tests/extmod/ure_sub.py @@ -43,6 +43,9 @@ def A(): ) ) +# \g immediately followed by another \g +print(re.sub("(abc)", r"\g<1>\g<1>", "abc")) + # no matches at all print(re.sub("a", "b", "c")) @@ -69,3 +72,6 @@ def A(): re.sub(123, "a", "a") except TypeError: print("TypeError") + +# Include \ in the sub replacement +print(re.sub("b", "\\\\b", "abc")) From ce49be43b1ac7582edcfec20c56928781a512a7d Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Sat, 12 Sep 2020 14:46:05 -0500 Subject: [PATCH 316/352] zephyr: Replace zephyr integer types with C99 types. Zephyr v2.4.0 stopped using custom integer types in favor of C99 types instead. Signed-off-by: Maureen Helm --- ports/zephyr/machine_i2c.c | 2 +- ports/zephyr/zephyr_storage.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/machine_i2c.c b/ports/zephyr/machine_i2c.c index 4b29f41d1ef71..576cb197777ed 100644 --- a/ports/zephyr/machine_i2c.c +++ b/ports/zephyr/machine_i2c.c @@ -97,7 +97,7 @@ STATIC int machine_hard_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t add struct i2c_msg msg; int ret; - msg.buf = (u8_t *)buf; + msg.buf = (uint8_t *)buf; msg.len = len; msg.flags = 0; diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index 83f19a8feeadd..1c25b32771e66 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -146,7 +146,7 @@ typedef struct _zephyr_flash_area_obj_t { const struct flash_area *area; int block_size; int block_count; - u8_t id; + uint8_t id; } zephyr_flash_area_obj_t; STATIC void zephyr_flash_area_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { From f842e32155d38038f3a2657238be57724b1e0bf1 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Sat, 12 Sep 2020 14:50:21 -0500 Subject: [PATCH 317/352] zephyr: Const-ify struct device instance pointers. Zephyr v2.4.0 added a const qualifier to usages of struct device to allow storing device driver instances exclusively in flash and thereby reduce ram footprint. Signed-off-by: Maureen Helm --- ports/zephyr/machine_i2c.c | 4 ++-- ports/zephyr/machine_pin.c | 4 ++-- ports/zephyr/modmachine.h | 2 +- ports/zephyr/modzsensor.c | 2 +- ports/zephyr/uart_core.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/zephyr/machine_i2c.c b/ports/zephyr/machine_i2c.c index 576cb197777ed..0efcd331fc4d1 100644 --- a/ports/zephyr/machine_i2c.c +++ b/ports/zephyr/machine_i2c.c @@ -42,7 +42,7 @@ STATIC const mp_obj_type_t machine_hard_i2c_type; typedef struct _machine_hard_i2c_obj_t { mp_obj_base_t base; - struct device *dev; + const struct device *dev; bool restart; } machine_hard_i2c_obj_t; @@ -65,7 +65,7 @@ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); const char *dev_name = mp_obj_str_get_str(args[ARG_id].u_obj); - struct device *dev = device_get_binding(dev_name); + const struct device *dev = device_get_binding(dev_name); if (dev == NULL) { mp_raise_ValueError(MP_ERROR_TEXT("device not found")); diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c index c9c6a8c8936e0..34c822c2f05a5 100644 --- a/ports/zephyr/machine_pin.c +++ b/ports/zephyr/machine_pin.c @@ -58,7 +58,7 @@ void machine_pin_deinit(void) { MP_STATE_PORT(machine_pin_irq_list) = NULL; } -STATIC void gpio_callback_handler(struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { +STATIC void gpio_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { machine_pin_irq_obj_t *irq = CONTAINER_OF(cb, machine_pin_irq_obj_t, callback); #if MICROPY_STACK_CHECK @@ -132,7 +132,7 @@ mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, mp_obj_get_array_fixed_n(args[0], 2, &items); const char *drv_name = mp_obj_str_get_str(items[0]); int wanted_pin = mp_obj_get_int(items[1]); - struct device *wanted_port = device_get_binding(drv_name); + const struct device *wanted_port = device_get_binding(drv_name); if (!wanted_port) { mp_raise_ValueError(MP_ERROR_TEXT("invalid port")); } diff --git a/ports/zephyr/modmachine.h b/ports/zephyr/modmachine.h index 766a9d7ea3874..5d7b2e1ed847a 100644 --- a/ports/zephyr/modmachine.h +++ b/ports/zephyr/modmachine.h @@ -9,7 +9,7 @@ MP_DECLARE_CONST_FUN_OBJ_0(machine_info_obj); typedef struct _machine_pin_obj_t { mp_obj_base_t base; - struct device *port; + const struct device *port; uint32_t pin; struct _machine_pin_irq_obj_t *irq; } machine_pin_obj_t; diff --git a/ports/zephyr/modzsensor.c b/ports/zephyr/modzsensor.c index b01ce2693c232..01f05aacd64a0 100644 --- a/ports/zephyr/modzsensor.c +++ b/ports/zephyr/modzsensor.c @@ -35,7 +35,7 @@ typedef struct _mp_obj_sensor_t { mp_obj_base_t base; - struct device *dev; + const struct device *dev; } mp_obj_sensor_t; STATIC mp_obj_t sensor_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c index 63ecc8289e3c9..44bdeb5c2784c 100644 --- a/ports/zephyr/uart_core.c +++ b/ports/zephyr/uart_core.c @@ -53,7 +53,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { } } #else - static struct device *uart_console_dev; + static const struct device *uart_console_dev; if (uart_console_dev == NULL) { uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); } From c2a7aac906a31b0d361b7177fae7fc88736a7705 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Sat, 12 Sep 2020 15:35:29 -0500 Subject: [PATCH 318/352] travis: Update zephyr build to v2.4.0. Updates CI to use the latest zephyr release tag. Signed-off-by: Maureen Helm --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 45b67dc9fedc2..c9fcc21336efd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ jobs: zephyrprojectrtos/ci:v0.11.8 - docker ps -a install: - - docker exec zephyr-ci west init --mr v2.3.0 /zephyrproject + - docker exec zephyr-ci west init --mr v2.4.0 /zephyrproject - docker exec -w /zephyrproject zephyr-ci west update - docker exec -w /zephyrproject zephyr-ci west zephyr-export script: From 997ec9e8ccaf4f1d8a950ef025e098e6b033c66c Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Mon, 28 Sep 2020 09:23:10 -0500 Subject: [PATCH 319/352] zephyr: Update build instructions to v2.4.0. Updates the zephyr port build instructions to use the latest zephyr release tag. Signed-off-by: Maureen Helm --- ports/zephyr/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index a2fb393d90d0f..d69030dc2c712 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -4,7 +4,7 @@ MicroPython port to Zephyr RTOS This is a work-in-progress port of MicroPython to Zephyr RTOS (http://zephyrproject.org). -This port requires Zephyr version 2.3.0, and may also work on higher +This port requires Zephyr version 2.4.0, and may also work on higher versions. All boards supported by Zephyr (with standard level of features support, like UART console) should work with MicroPython (but not all were tested). @@ -38,13 +38,13 @@ setup is correct. If you already have Zephyr installed but are having issues building the MicroPython port then try installing the correct version of Zephyr via: - $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.3.0 + $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.4.0 Alternatively, you don't have to redo the Zephyr installation to just switch from master to a tagged release, you can instead do: $ cd zephyrproject/zephyr - $ git checkout v2.3.0 + $ git checkout v2.4.0 $ west update With Zephyr installed you may then need to configure your environment, From ee7568ca8d4f1a5d09a123cf5f7a1e430b8f4c9d Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 25 Sep 2020 10:47:59 -0500 Subject: [PATCH 320/352] docs/reference/packages.rst: Fix typo, remove duplicate "port". Fixes #6485. --- docs/reference/packages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 2854df23a0240..e9adfc176ea73 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -188,7 +188,7 @@ Few notes: 1. Step 5 in the sequence above assumes that the distribution package is available from PyPI. If that is not the case, you would need to copy Python source files manually to ``modules/`` subdirectory - of the port port directory. (Note that upip does not support + of the port directory. (Note that upip does not support installing from e.g. version control repositories). 2. The firmware for baremetal devices usually has size restrictions, so adding too many frozen modules may overflow it. Usually, you From c711c0049e5f12cae048d2b0e77bc70e68804ea5 Mon Sep 17 00:00:00 2001 From: Mike Wadsten Date: Tue, 22 Sep 2020 11:07:20 -0500 Subject: [PATCH 321/352] py/makeversionhdr.py: Match only git tags which look like versions. Some downstream projects may use tags in their repositories for more than just designating MicroPython releases. In those cases, the makeversionhdr.py script would end up using a different tag than intended. So tell `git describe` to only match tags that look like a MicroPython version tag, such as `v1.12` or `v2.0`. --- py/makeversionhdr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 567b3b8322f9f..970a86e6ca3ab 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -23,7 +23,7 @@ def get_version_info_from_git(): # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( - ["git", "describe", "--dirty", "--always"], + ["git", "describe", "--dirty", "--always", "--match", "v[1-9].*"], stderr=subprocess.STDOUT, universal_newlines=True, ).strip() From c35deb2625efc877b3a0d03d5654e27232b2d101 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Sep 2020 13:30:48 +1000 Subject: [PATCH 322/352] extmod/machine_i2c: Rename type to SoftI2C and add custom print method. Also rename machine_i2c_type to mp_machine_soft_i2c_type. These changes make it clear that it's a soft-I2C implementation, and match SoftSPI. Signed-off-by: Damien George --- extmod/machine_i2c.c | 17 ++++++++++++----- extmod/machine_i2c.h | 3 ++- ports/esp32/modmachine.c | 2 +- ports/esp8266/modmachine.c | 2 +- ports/nrf/modules/machine/i2c.h | 2 +- ports/nrf/modules/machine/modmachine.c | 2 +- ports/nrf/mphalport.h | 2 ++ ports/stm32/modmachine.c | 2 +- ports/zephyr/modmachine.c | 2 +- 9 files changed, 22 insertions(+), 12 deletions(-) diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index c804cf570385d..8aad001f1e42f 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -302,6 +302,12 @@ STATIC int mp_machine_i2c_writeto(mp_obj_base_t *self, uint16_t addr, const uint /******************************************************************************/ // MicroPython bindings for I2C +STATIC void mp_machine_soft_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mp_machine_soft_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SoftI2C(scl=" MP_HAL_PIN_FMT ", sda=" MP_HAL_PIN_FMT ", freq=%u)", + mp_hal_pin_name(self->scl), mp_hal_pin_name(self->sda), 500000 / self->us_delay); +} + STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { @@ -318,7 +324,7 @@ STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, mp_hal_i2c_init(self, args[ARG_freq].u_int); } -STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t mp_machine_soft_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check the id argument, if given if (n_args > 0) { if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { @@ -336,7 +342,7 @@ STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, s // create new soft I2C object machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t); - self->base.type = &machine_i2c_type; + self->base.type = &mp_machine_soft_i2c_type; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_i2c_obj_init_helper(self, n_args, args, &kw_args); @@ -700,10 +706,11 @@ STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = { .transfer = mp_machine_soft_i2c_transfer, }; -const mp_obj_type_t machine_i2c_type = { +const mp_obj_type_t mp_machine_soft_i2c_type = { { &mp_type_type }, - .name = MP_QSTR_I2C, - .make_new = machine_i2c_make_new, + .name = MP_QSTR_SoftI2C, + .print = mp_machine_soft_i2c_print, + .make_new = mp_machine_soft_i2c_make_new, .protocol = &mp_machine_soft_i2c_p, .locals_dict = (mp_obj_dict_t *)&mp_machine_soft_i2c_locals_dict, }; diff --git a/extmod/machine_i2c.h b/extmod/machine_i2c.h index f951c1f214973..9723069d56c99 100644 --- a/extmod/machine_i2c.h +++ b/extmod/machine_i2c.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H #include "py/obj.h" +#include "py/mphal.h" #define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write #define MP_MACHINE_I2C_FLAG_STOP (0x02) @@ -56,7 +57,7 @@ typedef struct _mp_machine_soft_i2c_obj_t { mp_hal_pin_obj_t sda; } mp_machine_soft_i2c_obj_t; -extern const mp_obj_type_t machine_i2c_type; +extern const mp_obj_type_t mp_machine_soft_i2c_type; extern const mp_obj_dict_t mp_machine_soft_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); diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 82a02522f7b41..d56f6fcef12ec 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -255,7 +255,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index cb28d5b144827..4917ed9b904fe 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -421,7 +421,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, #if MICROPY_PY_MACHINE_I2C - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif #if MICROPY_PY_MACHINE_SPI { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hspi_type) }, diff --git a/ports/nrf/modules/machine/i2c.h b/ports/nrf/modules/machine/i2c.h index 92194ce75763f..3c4fde983a62a 100644 --- a/ports/nrf/modules/machine/i2c.h +++ b/ports/nrf/modules/machine/i2c.h @@ -27,7 +27,7 @@ #ifndef I2C_H__ #define I2C_H__ -extern const mp_obj_type_t machine_i2c_type; +#include "extmod/machine_i2c.h" void i2c_init0(void); diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index 48730c849f6bf..786abac7c1ea9 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -208,7 +208,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, #endif #if MICROPY_PY_MACHINE_I2C - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif #if MICROPY_PY_MACHINE_ADC { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index 15b37b7ef22d2..5900559b5dd76 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -54,8 +54,10 @@ void mp_hal_delay_us(mp_uint_t us); const char *nrfx_error_code_lookup(uint32_t err_code); +#define MP_HAL_PIN_FMT "%q" #define mp_hal_pin_obj_t const pin_obj_t * #define mp_hal_get_pin_obj(o) pin_find(o) +#define mp_hal_pin_name(p) ((p)->name) #define mp_hal_pin_high(p) nrf_gpio_pin_set(p->pin) #define mp_hal_pin_low(p) nrf_gpio_pin_clear(p->pin) #define mp_hal_pin_read(p) (nrf_gpio_pin_dir_get(p->pin) == NRF_GPIO_PIN_DIR_OUTPUT) ? nrf_gpio_pin_out_read(p->pin) : nrf_gpio_pin_read(p->pin) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index ac4d8712397e4..ff3b842c870f1 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -415,7 +415,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, #if MICROPY_PY_MACHINE_I2C - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index 72078cf9deabc..fc4d3b3ca8706 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -60,7 +60,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #endif { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, From aaed33896b0fec67a0e2ec7daf3fe908253d8cf7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Sep 2020 13:37:31 +1000 Subject: [PATCH 323/352] extmod/machine_i2c: Remove "id" arg in SoftI2C constructor. The SoftI2C constructor is now used soley to create SoftI2C instances, it can no longer delegate to create a hardware-based I2C instance. Signed-off-by: Damien George --- extmod/machine_i2c.c | 15 --------------- ports/esp32/mpconfigport.h | 1 - ports/nrf/mpconfigport.h | 1 - ports/stm32/mpconfigport.h | 3 --- ports/zephyr/mpconfigport.h | 1 - 5 files changed, 21 deletions(-) diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index 8aad001f1e42f..9203f16f6d785 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -325,21 +325,6 @@ STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, } STATIC mp_obj_t mp_machine_soft_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check the id argument, if given - if (n_args > 0) { - if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { - #if defined(MICROPY_PY_MACHINE_I2C_MAKE_NEW) - // dispatch to port-specific constructor - extern mp_obj_t MICROPY_PY_MACHINE_I2C_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); - return MICROPY_PY_MACHINE_I2C_MAKE_NEW(type, n_args, n_kw, args); - #else - mp_raise_ValueError(MP_ERROR_TEXT("invalid I2C peripheral")); - #endif - } - --n_args; - ++args; - } - // create new soft I2C object machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t); self->base.type = &mp_machine_soft_i2c_type; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 663fed3f683a4..00abab3afd8bd 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -145,7 +145,6 @@ #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_I2C (1) -#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hw_i2c_make_new #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 76bb6d7bdf0df..a025feb2ec65a 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -135,7 +135,6 @@ #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (0) -#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new #define MICROPY_PY_MACHINE_SPI (0) #define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0) #define MICROPY_PY_FRAMEBUF (0) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 95b539603fb95..9ff4765ac25cc 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -183,9 +183,6 @@ #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_I2C (1) -#if MICROPY_HW_ENABLE_HW_I2C -#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new -#endif #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (SPI_FIRSTBIT_MSB) #define MICROPY_PY_MACHINE_SPI_LSB (SPI_FIRSTBIT_LSB) diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index f94ee72707d47..6bfd9ff884f1b 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -61,7 +61,6 @@ #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_I2C (1) -#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_PY_STRUCT (0) From 9e0533b9e158a455be9284b70011d0515096a5f6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Sep 2020 14:07:39 +1000 Subject: [PATCH 324/352] extmod/machine_spi: Remove "id" arg in SoftSPI constructor. The SoftSPI constructor is now used soley to create SoftSPI instances, it can no longer delegate to create a hardware-based SPI instance. Signed-off-by: Damien George --- extmod/machine_spi.c | 24 +----------------------- ports/esp32/mpconfigport.h | 1 - ports/esp8266/machine_hspi.c | 2 +- ports/esp8266/mpconfigport.h | 1 - ports/stm32/machine_spi.c | 4 ++-- ports/stm32/mpconfigport.h | 1 - 6 files changed, 4 insertions(+), 29 deletions(-) diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index a7f96573a720e..c951a5137c5ce 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -41,28 +41,6 @@ /******************************************************************************/ // MicroPython bindings for generic machine.SPI -STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); - -mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - // check the id argument, if given - if (n_args > 0) { - if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { - #if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW) - // dispatch to port-specific constructor - extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); - return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args); - #else - mp_raise_ValueError(MP_ERROR_TEXT("invalid SPI peripheral")); - #endif - } - --n_args; - ++args; - } - - // software SPI - return mp_machine_soft_spi_make_new(type, n_args, n_kw, args); -} - STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(args[0]); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)s->type->protocol; @@ -275,7 +253,7 @@ const mp_obj_type_t mp_machine_soft_spi_type = { { &mp_type_type }, .name = MP_QSTR_SoftSPI, .print = mp_machine_soft_spi_print, - .make_new = mp_machine_spi_make_new, // delegate to master constructor + .make_new = mp_machine_soft_spi_make_new, .protocol = &mp_machine_soft_spi_p, .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, }; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 00abab3afd8bd..5bf0676b238e3 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -148,7 +148,6 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) -#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hw_spi_make_new #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_SOFTSPI_MIN_DELAY (0) #define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly diff --git a/ports/esp8266/machine_hspi.c b/ports/esp8266/machine_hspi.c index 7319194d763d3..55bbcf9f58b46 100644 --- a/ports/esp8266/machine_hspi.c +++ b/ports/esp8266/machine_hspi.c @@ -178,7 +178,7 @@ const mp_obj_type_t machine_hspi_type = { { &mp_type_type }, .name = MP_QSTR_HSPI, .print = machine_hspi_print, - .make_new = mp_machine_spi_make_new, // delegate to master constructor + .make_new = machine_hspi_make_new, .protocol = &machine_hspi_p, .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, }; diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 5344a98d6a548..974310f84da6e 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -80,7 +80,6 @@ #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SPI (1) -#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hspi_make_new #define MICROPY_PY_UWEBSOCKET (1) #define MICROPY_PY_WEBREPL (1) #define MICROPY_PY_WEBREPL_DELAY (20) diff --git a/ports/stm32/machine_spi.c b/ports/stm32/machine_spi.c index cf6e96ab67bf6..edbd500b3c073 100644 --- a/ports/stm32/machine_spi.c +++ b/ports/stm32/machine_spi.c @@ -48,7 +48,7 @@ STATIC void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp 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_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)} }, + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, @@ -137,7 +137,7 @@ const mp_obj_type_t machine_hard_spi_type = { { &mp_type_type }, .name = MP_QSTR_SPI, .print = machine_hard_spi_print, - .make_new = mp_machine_spi_make_new, // delegate to master constructor + .make_new = machine_hard_spi_make_new, .protocol = &machine_hard_spi_p, .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, }; diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 9ff4765ac25cc..7ea41bb6f4655 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -186,7 +186,6 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (SPI_FIRSTBIT_MSB) #define MICROPY_PY_MACHINE_SPI_LSB (SPI_FIRSTBIT_LSB) -#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hard_spi_make_new #define MICROPY_HW_SOFTSPI_MIN_DELAY (0) #define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48) #define MICROPY_PY_UWEBSOCKET (MICROPY_PY_LWIP) From 39d50d129ce428858332523548f0594503d0f45b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Sep 2020 14:09:49 +1000 Subject: [PATCH 325/352] ports: Add SoftI2C and SoftSPI to machine module where appropriate. Previous commits removed the ability for one I2C/SPI constructor to construct both software- or hardware-based peripheral instances. Such construction is now split to explicit soft and non-soft types. This commit makes both types available in all ports that previously could create both software and hardware peripherals: machine.I2C and machine.SPI construct hardware instances, while machine.SoftI2C and machine.SoftSPI create software instances. This is a breaking change for use of software-based I2C and SPI. Code that constructed I2C/SPI peripherals in the following way will need to be changed: machine.I2C(-1, ...) -> machine.SoftI2C(...) machine.I2C(scl=scl, sda=sda) -> machine.SoftI2C(scl=scl, sda=sda) machine.SPI(-1, ...) -> machine.SoftSPI(...) machine.SPI(sck=sck, mosi=mosi, miso=miso) -> machine.SoftSPI(sck=sck, mosi=mosi, miso=miso) Code which uses machine.I2C and machine.SPI classes to access hardware peripherals does not need to change. Signed-off-by: Damien George --- ports/esp32/machine_i2c.c | 4 ++-- ports/esp32/modmachine.c | 6 ++++-- ports/esp32/modmachine.h | 1 + ports/esp8266/modmachine.c | 3 +++ ports/nrf/modules/machine/i2c.c | 4 +--- ports/nrf/modules/machine/i2c.h | 2 ++ ports/nrf/modules/machine/modmachine.c | 3 ++- ports/stm32/machine_i2c.c | 5 ++--- ports/stm32/modmachine.c | 7 +++++++ ports/stm32/modmachine.h | 1 + ports/stm32/spi.h | 1 - ports/zephyr/machine_i2c.c | 5 ++--- ports/zephyr/modmachine.c | 2 +- ports/zephyr/modmachine.h | 1 + ports/zephyr/mphalport.h | 4 ++++ 15 files changed, 33 insertions(+), 16 deletions(-) diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 8362ed5e7be5a..075d0ded6c475 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -28,6 +28,7 @@ #include "py/mphal.h" #include "py/mperrno.h" #include "extmod/machine_i2c.h" +#include "modmachine.h" #include "driver/i2c.h" @@ -45,7 +46,6 @@ typedef struct _machine_hw_i2c_obj_t { gpio_num_t sda : 8; } machine_hw_i2c_obj_t; -STATIC const mp_obj_type_t machine_hw_i2c_type; STATIC machine_hw_i2c_obj_t machine_hw_i2c_obj[I2C_NUM_MAX]; STATIC void machine_hw_i2c_init(machine_hw_i2c_obj_t *self, uint32_t freq, uint32_t timeout_us, bool first_init) { @@ -169,7 +169,7 @@ STATIC const mp_machine_i2c_p_t machine_hw_i2c_p = { .transfer = machine_hw_i2c_transfer, }; -STATIC const mp_obj_type_t machine_hw_i2c_type = { +const mp_obj_type_t machine_hw_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .print = machine_hw_i2c_print, diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index d56f6fcef12ec..303d25ee8b2d1 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -255,10 +255,12 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, - { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hw_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, // Reset reasons diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index fbb43250ff42e..98b36e32b0231 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -16,6 +16,7 @@ extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_pwm_type; +extern const mp_obj_type_t machine_hw_i2c_type; extern const mp_obj_type_t machine_hw_spi_type; extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_rtc_type; diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 4917ed9b904fe..86c1d728d5369 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -39,6 +39,7 @@ #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" +#include "extmod/machine_spi.h" #include "modmachine.h" #include "xtirq.h" @@ -422,9 +423,11 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif #if MICROPY_PY_MACHINE_SPI { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hspi_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, #endif // wake abilities diff --git a/ports/nrf/modules/machine/i2c.c b/ports/nrf/modules/machine/i2c.c index afa906c3598d7..704d8615a810d 100644 --- a/ports/nrf/modules/machine/i2c.c +++ b/ports/nrf/modules/machine/i2c.c @@ -63,8 +63,6 @@ #endif -STATIC const mp_obj_type_t machine_hard_i2c_type; - typedef struct _machine_hard_i2c_obj_t { mp_obj_base_t base; nrfx_twi_t p_twi; // Driver instance @@ -161,7 +159,7 @@ STATIC const mp_machine_i2c_p_t machine_hard_i2c_p = { .transfer_single = machine_hard_i2c_transfer_single, }; -STATIC const mp_obj_type_t machine_hard_i2c_type = { +const mp_obj_type_t machine_hard_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .print = machine_hard_i2c_print, diff --git a/ports/nrf/modules/machine/i2c.h b/ports/nrf/modules/machine/i2c.h index 3c4fde983a62a..1dfb1f077a446 100644 --- a/ports/nrf/modules/machine/i2c.h +++ b/ports/nrf/modules/machine/i2c.h @@ -29,6 +29,8 @@ #include "extmod/machine_i2c.h" +extern const mp_obj_type_t machine_hard_i2c_type; + void i2c_init0(void); #endif // I2C_H__ diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index 786abac7c1ea9..3330349cd5963 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -208,7 +208,8 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, #endif #if MICROPY_PY_MACHINE_I2C - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif #if MICROPY_PY_MACHINE_ADC { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, diff --git a/ports/stm32/machine_i2c.c b/ports/stm32/machine_i2c.c index 14dd88b78f4dc..31defcb17a117 100644 --- a/ports/stm32/machine_i2c.c +++ b/ports/stm32/machine_i2c.c @@ -32,11 +32,10 @@ #include "py/mperrno.h" #include "extmod/machine_i2c.h" #include "i2c.h" +#include "modmachine.h" #if MICROPY_HW_ENABLE_HW_I2C -STATIC const mp_obj_type_t machine_hard_i2c_type; - #define I2C_POLL_DEFAULT_TIMEOUT_US (50000) // 50ms #if defined(STM32F0) || defined(STM32F4) || defined(STM32F7) @@ -266,7 +265,7 @@ STATIC const mp_machine_i2c_p_t machine_hard_i2c_p = { .transfer = machine_hard_i2c_transfer, }; -STATIC const mp_obj_type_t machine_hard_i2c_type = { +const mp_obj_type_t machine_hard_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .print = machine_hard_i2c_print, diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index ff3b842c870f1..9fa00a915186f 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -37,6 +37,7 @@ #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" +#include "extmod/machine_spi.h" #include "lib/utils/pyexec.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs.h" @@ -415,9 +416,15 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, #if MICROPY_PY_MACHINE_I2C + #if MICROPY_HW_ENABLE_HW_I2C + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, + #else { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&pyb_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, diff --git a/ports/stm32/modmachine.h b/ports/stm32/modmachine.h index e4ccf638855e8..e846208540f72 100644 --- a/ports/stm32/modmachine.h +++ b/ports/stm32/modmachine.h @@ -30,6 +30,7 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_hard_i2c_type; void machine_init(void); void machine_deinit(void); diff --git a/ports/stm32/spi.h b/ports/stm32/spi.h index 885fb0bd6fc8c..17f1bf6c4ab90 100644 --- a/ports/stm32/spi.h +++ b/ports/stm32/spi.h @@ -65,7 +65,6 @@ extern const spi_t spi_obj[6]; extern const mp_spi_proto_t spi_proto; extern const mp_obj_type_t pyb_spi_type; -extern const mp_obj_type_t machine_soft_spi_type; extern const mp_obj_type_t machine_hard_spi_type; // A transfer of "len" bytes should take len*8*1000/baudrate milliseconds. diff --git a/ports/zephyr/machine_i2c.c b/ports/zephyr/machine_i2c.c index 0efcd331fc4d1..2d1c7d5c54e8b 100644 --- a/ports/zephyr/machine_i2c.c +++ b/ports/zephyr/machine_i2c.c @@ -37,8 +37,7 @@ #include "py/mphal.h" #include "py/mperrno.h" #include "extmod/machine_i2c.h" - -STATIC const mp_obj_type_t machine_hard_i2c_type; +#include "modmachine.h" typedef struct _machine_hard_i2c_obj_t { mp_obj_base_t base; @@ -127,7 +126,7 @@ STATIC const mp_machine_i2c_p_t machine_hard_i2c_p = { .transfer_single = machine_hard_i2c_transfer_single, }; -STATIC const mp_obj_type_t machine_hard_i2c_type = { +const mp_obj_type_t machine_hard_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .print = machine_hard_i2c_print, diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index fc4d3b3ca8706..29e6c889c4cef 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -60,7 +60,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #endif { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, diff --git a/ports/zephyr/modmachine.h b/ports/zephyr/modmachine.h index 5d7b2e1ed847a..b67da55338cd7 100644 --- a/ports/zephyr/modmachine.h +++ b/ports/zephyr/modmachine.h @@ -4,6 +4,7 @@ #include "py/obj.h" extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_hard_i2c_type; MP_DECLARE_CONST_FUN_OBJ_0(machine_info_obj); diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h index 9ef213ddb0809..b3154b649bbda 100644 --- a/ports/zephyr/mphalport.h +++ b/ports/zephyr/mphalport.h @@ -30,6 +30,10 @@ static inline uint64_t mp_hal_time_ns(void) { } #define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us)) + +// C-level pin API is not currently implemented +#define MP_HAL_PIN_FMT "%d" +#define mp_hal_pin_name(p) (0) #define mp_hal_pin_od_low(p) (mp_raise_NotImplementedError("mp_hal_pin_od_low")) #define mp_hal_pin_od_high(p) (mp_raise_NotImplementedError("mp_hal_pin_od_high")) #define mp_hal_pin_open_drain(p) (mp_raise_NotImplementedError("mp_hal_pin_open_drain")) From 71f3ade770fa7f2637d94f5ba5840b64a16a95db Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Sep 2020 14:47:34 +1000 Subject: [PATCH 326/352] ports: Support legacy soft I2C/SPI construction via id=-1 arg. With a warning that this way of constructing software I2C/SPI is deprecated. The check and warning will be removed in a future release. This should help existing code to migrate to the new SoftI2C/SoftSPI types. Signed-off-by: Damien George --- extmod/machine_i2c.h | 13 +++++++++++++ extmod/machine_spi.h | 13 +++++++++++++ ports/esp32/machine_hw_spi.c | 2 ++ ports/esp32/machine_i2c.c | 2 ++ ports/esp8266/machine_hspi.c | 2 ++ ports/nrf/modules/machine/i2c.c | 2 ++ ports/stm32/machine_i2c.c | 2 ++ ports/stm32/machine_spi.c | 2 ++ ports/zephyr/machine_i2c.c | 2 ++ 9 files changed, 40 insertions(+) diff --git a/extmod/machine_i2c.h b/extmod/machine_i2c.h index 9723069d56c99..e3a87e282a0fe 100644 --- a/extmod/machine_i2c.h +++ b/extmod/machine_i2c.h @@ -29,6 +29,19 @@ #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) diff --git a/extmod/machine_spi.h b/extmod/machine_spi.h index db21e1cd31919..ca92c719a8b5a 100644 --- a/extmod/machine_spi.h +++ b/extmod/machine_spi.h @@ -30,6 +30,19 @@ #include "py/mphal.h" #include "drivers/bus/spi.h" +// Temporary support for legacy construction of SoftSPI via SPI type. +#define MP_MACHINE_SPI_CHECK_FOR_LEGACY_SOFTSPI_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: SPI(-1, ...) is deprecated, use SoftSPI(...) instead\n"); \ + if (n_args != 0) { \ + --n_args; \ + ++all_args; \ + } \ + return mp_machine_soft_spi_type.make_new(&mp_machine_soft_spi_type, n_args, n_kw, all_args); \ + } \ + } while (0) + // SPI protocol typedef struct _mp_machine_spi_p_t { void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 3962e26b78610..3790b4e0cf142 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -351,6 +351,8 @@ STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_ } mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + MP_MACHINE_SPI_CHECK_FOR_LEGACY_SOFTSPI_CONSTRUCTION(n_args, n_kw, all_args); + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 075d0ded6c475..a1d0ad0f4d93c 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -115,6 +115,8 @@ STATIC void machine_hw_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_p } mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + // Parse args enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { diff --git a/ports/esp8266/machine_hspi.c b/ports/esp8266/machine_hspi.c index 55bbcf9f58b46..ff3ba17255358 100644 --- a/ports/esp8266/machine_hspi.c +++ b/ports/esp8266/machine_hspi.c @@ -151,6 +151,8 @@ STATIC void machine_hspi_init(mp_obj_base_t *self_in, size_t n_args, const mp_ob } mp_obj_t machine_hspi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + MP_MACHINE_SPI_CHECK_FOR_LEGACY_SOFTSPI_CONSTRUCTION(n_args, n_kw, args); + // args[0] holds the id of the peripheral if (args[0] != MP_OBJ_NEW_SMALL_INT(1)) { // FlashROM is on SPI0, so far we don't support its usage diff --git a/ports/nrf/modules/machine/i2c.c b/ports/nrf/modules/machine/i2c.c index 704d8615a810d..ab4d516621bc7 100644 --- a/ports/nrf/modules/machine/i2c.c +++ b/ports/nrf/modules/machine/i2c.c @@ -94,6 +94,8 @@ STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp /* MicroPython bindings for machine API */ 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) { + MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + enum { ARG_id, ARG_scl, ARG_sda }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, diff --git a/ports/stm32/machine_i2c.c b/ports/stm32/machine_i2c.c index 31defcb17a117..e0c408c6d07c2 100644 --- a/ports/stm32/machine_i2c.c +++ b/ports/stm32/machine_i2c.c @@ -193,6 +193,8 @@ STATIC void machine_hard_i2c_init(machine_hard_i2c_obj_t *self, uint32_t freq, u #endif 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) { + MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + // parse args enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout, ARG_timingr }; static const mp_arg_t allowed_args[] = { diff --git a/ports/stm32/machine_spi.c b/ports/stm32/machine_spi.c index edbd500b3c073..37c026cefc33a 100644 --- a/ports/stm32/machine_spi.c +++ b/ports/stm32/machine_spi.c @@ -46,6 +46,8 @@ STATIC void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp } 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) { + MP_MACHINE_SPI_CHECK_FOR_LEGACY_SOFTSPI_CONSTRUCTION(n_args, n_kw, all_args); + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, diff --git a/ports/zephyr/machine_i2c.c b/ports/zephyr/machine_i2c.c index 2d1c7d5c54e8b..ec4b8620a5014 100644 --- a/ports/zephyr/machine_i2c.c +++ b/ports/zephyr/machine_i2c.c @@ -51,6 +51,8 @@ STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp } 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) { + MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); + enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, From 98182a97c5a9229938406beb722966eacceeb823 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Sep 2020 16:50:23 +1000 Subject: [PATCH 327/352] docs: Update I2C and SPI docs to add reference to SoftI2C and SoftSPI. Signed-off-by: Damien George --- docs/esp32/quickref.rst | 62 ++++++++++++++++++++++-------------- docs/esp8266/quickref.rst | 9 +++--- docs/library/machine.I2C.rst | 38 ++++++++++++++++------ docs/library/machine.SPI.rst | 20 ++++++++++-- 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index d5c222f3a1eef..79e61a10b6dfd 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -249,16 +249,15 @@ ESP32 specific ADC class method reference: Software SPI bus ---------------- -There are two SPI drivers. One is implemented in software (bit-banging) -and works on all pins, and is accessed via the :ref:`machine.SPI ` -class:: +Software SPI (using bit-banging) works on all pins, and is accessed via the +:ref:`machine.SoftSPI ` class:: - from machine import Pin, SPI + from machine import Pin, SoftSPI - # construct an SPI bus on the given pins + # construct a SoftSPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) + spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) spi.init(baudrate=200000) # set the baudrate @@ -298,39 +297,54 @@ mosi 13 23 miso 12 19 ===== =========== ============ -Hardware SPI has the same methods as Software SPI above:: +Hardware SPI is accessed via the :ref:`machine.SPI ` class and +has the same methods as software SPI above:: from machine import Pin, SPI hspi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) vspi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) +Software I2C bus +---------------- -I2C bus -------- - -The I2C driver has both software and hardware implementations, and the two -hardware peripherals have identifiers 0 and 1. Any available output-capable -pins can be used for SCL and SDA. The driver is accessed via the -:ref:`machine.I2C ` class:: - - from machine import Pin, I2C +Software I2C (using bit-banging) works on all output-capable pins, and is +accessed via the :ref:`machine.SoftI2C ` class:: - # construct a software I2C bus - i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000) + from machine import Pin, SoftI2C - # construct a hardware I2C bus - i2c = I2C(0) - i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000) + i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100000) - i2c.scan() # scan for slave devices + i2c.scan() # scan for devices - i2c.readfrom(0x3a, 4) # read 4 bytes from slave device with address 0x3a - i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a + i2c.readfrom(0x3a, 4) # read 4 bytes from device with address 0x3a + i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a buf = bytearray(10) # create a buffer with 10 bytes i2c.writeto(0x3a, buf) # write the given buffer to the slave +Hardware I2C bus +---------------- + +There are two hardware I2C peripherals with identifiers 0 and 1. Any available +output-capable pins can be used for SCL and SDA but the defaults are given +below. + +===== =========== ============ +\ I2C(0) I2C(1) +===== =========== ============ +scl 18 25 +sda 19 26 +===== =========== ============ + +The driver is accessed via the :ref:`machine.I2C ` class and +has the same methods as software I2C above:: + + from machine import Pin, I2C + + i2c = I2C(0) + i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000) + Real time clock (RTC) --------------------- diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index c884d93fc9940..1a22bd50a5ac2 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -214,15 +214,15 @@ Software SPI bus ---------------- There are two SPI drivers. One is implemented in software (bit-banging) -and works on all pins, and is accessed via the :ref:`machine.SPI ` +and works on all pins, and is accessed via the :ref:`machine.SoftSPI ` class:: - from machine import Pin, SPI + from machine import Pin, SoftSPI # construct an SPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SPI(-1, baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) + spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) spi.init(baudrate=200000) # set the baudrate @@ -258,7 +258,8 @@ I2C bus ------- The I2C driver is implemented in software and works on all pins, -and is accessed via the :ref:`machine.I2C ` class:: +and is accessed via the :ref:`machine.I2C ` class (which is an +alias of :ref:`machine.SoftI2C `):: from machine import Pin, I2C diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index 743554bc76650..f9b951564611b 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -12,6 +12,14 @@ when created, or initialised later on. Printing the I2C object gives you information about its configuration. +Both hardware and software I2C implementations exist via the +:ref:`machine.I2C ` and `machine.SoftI2C` classes. Hardware I2C uses +underlying hardware support of the system to perform the reads/writes and is +usually efficient and fast but may have restrictions on which pins can be used. +Software I2C is implemented by bit-banging and can be used on any pin but is not +as efficient. These classes have the same methods available and differ primarily +in the way they are constructed. + Example usage:: from machine import I2C @@ -33,21 +41,33 @@ Example usage:: Constructors ------------ -.. class:: I2C(id=-1, *, scl, sda, freq=400000) +.. class:: I2C(id, *, scl, sda, freq=400000) Construct and return a new I2C object using the following parameters: - - *id* identifies a particular I2C peripheral. The default - value of -1 selects a software implementation of I2C which can - work (in most cases) with arbitrary pins for SCL and SDA. - If *id* is -1 then *scl* and *sda* must be specified. Other - allowed values for *id* depend on the particular port/board, - and specifying *scl* and *sda* may or may not be required or - allowed in this case. + - *id* identifies a particular I2C peripheral. Allowed values for + depend on the particular port/board + - *scl* should be a pin object specifying the pin to use for SCL. + - *sda* should be a pin object specifying the pin to use for SDA. + - *freq* should be an integer which sets the maximum frequency + for SCL. + + Note that some ports/boards will have default values of *scl* and *sda* + that can be changed in this constructor. Others will have fixed values + of *scl* and *sda* that cannot be changed. + +.. _machine.SoftI2C: +.. class:: SoftI2C(scl, sda, *, freq=400000, timeout=255) + + Construct a new software I2C object. The parameters are: + - *scl* should be a pin object specifying the pin to use for SCL. - *sda* should be a pin object specifying the pin to use for SDA. - *freq* should be an integer which sets the maximum frequency for SCL. + - *timeout* is the maximum time in microseconds to wait for clock + stretching (SCL held low by another device on the bus), after + which an ``OSError(ETIMEDOUT)`` exception is raised. General Methods --------------- @@ -79,7 +99,7 @@ The following methods implement the primitive I2C master bus operations and can be combined to make any I2C transaction. They are provided if you need more control over the bus, otherwise the standard methods (see below) can be used. -These methods are available on software I2C only. +These methods are only available on the `machine.SoftI2C` class. .. method:: I2C.start() diff --git a/docs/library/machine.SPI.rst b/docs/library/machine.SPI.rst index ea460a7b81282..7565241eb1cd2 100644 --- a/docs/library/machine.SPI.rst +++ b/docs/library/machine.SPI.rst @@ -11,21 +11,35 @@ SS (Slave Select), to select a particular device on a bus with which communication takes place. Management of an SS signal should happen in user code (via machine.Pin class). +Both hardware and software SPI implementations exist via the +:ref:`machine.SPI ` and `machine.SoftSPI` classes. Hardware SPI uses underlying +hardware support of the system to perform the reads/writes and is usually +efficient and fast but may have restrictions on which pins can be used. +Software SPI is implemented by bit-banging and can be used on any pin but +is not as efficient. These classes have the same methods available and +differ primarily in the way they are constructed. + Constructors ------------ .. class:: SPI(id, ...) - Construct an SPI object on the given bus, ``id``. Values of ``id`` depend + Construct an SPI object on the given bus, *id*. Values of *id* depend on a particular port and its hardware. Values 0, 1, etc. are commonly used - to select hardware SPI block #0, #1, etc. Value -1 can be used for - bitbanging (software) implementation of SPI (if supported by a port). + to select hardware SPI block #0, #1, etc. With no additional parameters, the SPI object is created but not initialised (it has the settings from the last initialisation of the bus, if any). If extra arguments are given, the bus is initialised. See ``init`` for parameters of initialisation. +.. _machine.SoftSPI: +.. class:: SoftSPI(baudrate=500000, *, polarity=0, phase=0, bits=8, firstbit=MSB, sck=None, mosi=None, miso=None) + + Construct a new software SPI object. Additional parameters must be + given, usually at least *sck*, *mosi* and *miso*, and these are used + to initialise the bus. See `SPI.init` for a description of the parameters. + Methods ------- From 905a18aafefbe04aa2beceb84885c29aa156b975 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Sep 2020 14:37:01 +1000 Subject: [PATCH 328/352] unix,windows: Implement mp_hal_time_ns using gettimeofday. This provides microsecond accuracy. Signed-off-by: Damien George --- ports/unix/unix_mphal.c | 5 +++-- ports/windows/windows_mphal.c | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index f43067f646ba9..3fe2fa9fec21d 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -216,6 +216,7 @@ mp_uint_t mp_hal_ticks_us(void) { } uint64_t mp_hal_time_ns(void) { - time_t now = time(NULL); - return (uint64_t)now * 1000000000ULL; + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; } diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index 8ed087358f567..b442b59147e4e 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -255,3 +255,9 @@ mp_uint_t mp_hal_ticks_cpu(void) { return value.LowPart; #endif } + +uint64_t mp_hal_time_ns(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; +} From d4b61b00172ccc231307e3ef33f66f28cb6b051f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Sep 2020 12:37:02 +1000 Subject: [PATCH 329/352] extmod/utime_mphal: Add generic utime.time_ns() function. It requires mp_hal_time_ns() to be provided by a port. This function allows very accurate absolute timestamps. Enabled on unix, windows, stm32, esp8266 and esp32. Signed-off-by: Damien George --- docs/library/utime.rst | 10 ++++++++-- extmod/utime_mphal.c | 6 ++++++ extmod/utime_mphal.h | 1 + ports/esp32/modutime.c | 1 + ports/esp8266/modutime.c | 1 + ports/stm32/modutime.c | 1 + ports/unix/modtime.c | 1 + tests/extmod/utime_time_ns.py | 24 ++++++++++++++++++++++++ tests/extmod/utime_time_ns.py.exp | 2 ++ 9 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/extmod/utime_time_ns.py create mode 100644 tests/extmod/utime_time_ns.py.exp diff --git a/docs/library/utime.rst b/docs/library/utime.rst index 8c222ede5f80b..86fd27b3a5ac8 100644 --- a/docs/library/utime.rst +++ b/docs/library/utime.rst @@ -216,8 +216,9 @@ Functions function returns number of seconds since a port-specific reference point in time (for embedded boards without a battery-backed RTC, usually since power up or reset). If you want to develop portable MicroPython application, you should not rely on this function - to provide higher than second precision. If you need higher precision, use - `ticks_ms()` and `ticks_us()` functions, if you need calendar time, + to provide higher than second precision. If you need higher precision, absolute + timestamps, use `time_ns()`. If relative times are acceptable then use the + `ticks_ms()` and `ticks_us()` functions. If you need calendar time, `gmtime()` or `localtime()` without an argument is a better choice. .. admonition:: Difference to CPython @@ -233,3 +234,8 @@ Functions hardware also lacks battery-powered RTC, so returns number of seconds since last power-up or from other relative, hardware-specific point (e.g. reset). + +.. function:: time_ns() + + Similar to `time()` but returns nanoseconds since the Epoch, as an integer (usually + a big integer, so will allocate on the heap). diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c index 6aff2cac725c2..d053cf128b871 100644 --- a/extmod/utime_mphal.c +++ b/extmod/utime_mphal.c @@ -99,4 +99,10 @@ STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { } 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/extmod/utime_mphal.h b/extmod/utime_mphal.h index 88a9ed4d3756d..57fc348832812 100644 --- a/extmod/utime_mphal.h +++ b/extmod/utime_mphal.h @@ -37,5 +37,6 @@ 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 diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c index ac3edb4e9b76f..cf7178e0b1c2d 100644 --- a/ports/esp32/modutime.c +++ b/ports/esp32/modutime.c @@ -97,6 +97,7 @@ STATIC const mp_rom_map_elem_t time_module_globals_table[] = { { 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_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, }; STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); diff --git a/ports/esp8266/modutime.c b/ports/esp8266/modutime.c index 86534722c792f..bcfbf7baf2c66 100644 --- a/ports/esp8266/modutime.c +++ b/ports/esp8266/modutime.c @@ -120,6 +120,7 @@ STATIC const mp_rom_map_elem_t time_module_globals_table[] = { { 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_time), MP_ROM_PTR(&time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, }; STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); diff --git a/ports/stm32/modutime.c b/ports/stm32/modutime.c index 2a37a130df2bb..4bce45eb33315 100644 --- a/ports/stm32/modutime.c +++ b/ports/stm32/modutime.c @@ -144,6 +144,7 @@ STATIC const mp_rom_map_elem_t time_module_globals_table[] = { { 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_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, }; STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index 5343e479c7d92..e08409e20e3e9 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -219,6 +219,7 @@ STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { { 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_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, { MP_ROM_QSTR(MP_QSTR_gmtime), MP_ROM_PTR(&mod_time_gmtime_obj) }, { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&mod_time_localtime_obj) }, { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&mod_time_mktime_obj) }, diff --git a/tests/extmod/utime_time_ns.py b/tests/extmod/utime_time_ns.py new file mode 100644 index 0000000000000..8f3890f1cb85e --- /dev/null +++ b/tests/extmod/utime_time_ns.py @@ -0,0 +1,24 @@ +# test utime.time_ns() + +try: + import utime + + utime.sleep_us + utime.time_ns +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +t0 = utime.time_ns() +utime.sleep_us(1000) +t1 = utime.time_ns() + +# Check that time_ns increases. +print(t0 < t1) + +# Check that time_ns counts correctly, but be very lenient with the upper bound (50ms). +if 950000 < t1 - t0 < 50000000: + print(True) +else: + print(t0, t1, t1 - t0) diff --git a/tests/extmod/utime_time_ns.py.exp b/tests/extmod/utime_time_ns.py.exp new file mode 100644 index 0000000000000..dbde422651c9a --- /dev/null +++ b/tests/extmod/utime_time_ns.py.exp @@ -0,0 +1,2 @@ +True +True From 843dcd4f8515c62b32b4b25dc3b01fba107622e2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Sep 2020 23:34:42 +1000 Subject: [PATCH 330/352] py/parse: Expose rule-name printing as MICROPY_DEBUG_PARSE_RULE_NAME. So it can be enabled without modifying the source. Signed-off-by: Damien George --- py/mpconfig.h | 5 +++++ py/parse.c | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 12517316c6c80..cc83f3850d695 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -454,6 +454,11 @@ #define MICROPY_DEBUG_MP_OBJ_SENTINELS (0) #endif +// Whether to print parse rule names (rather than integers) in mp_parse_node_print +#ifndef MICROPY_DEBUG_PARSE_RULE_NAME +#define MICROPY_DEBUG_PARSE_RULE_NAME (0) +#endif + // Whether to enable a simple VM stack overflow check #ifndef MICROPY_DEBUG_VM_STACK_OVERFLOW #define MICROPY_DEBUG_VM_STACK_OVERFLOW (0) diff --git a/py/parse.c b/py/parse.c index 38cd215a9ff71..da2f5e796d9be 100644 --- a/py/parse.c +++ b/py/parse.c @@ -55,9 +55,6 @@ #define RULE_ARG_RULE (0x2000) #define RULE_ARG_OPT_RULE (0x3000) -// (un)comment to use rule names; for debugging -// #define USE_RULE_NAME (1) - // *FORMAT-OFF* enum { @@ -192,7 +189,7 @@ static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 = #undef DEF_RULE_NC 0; -#if USE_RULE_NAME +#if MICROPY_DEBUG_PARSE_RULE_NAME // Define an array of rule names corresponding to each rule STATIC const char *const rule_name_table[] = { #define DEF_RULE(rule, comp, kind, ...) #rule, @@ -410,7 +407,7 @@ void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t ind #endif } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); - #if USE_RULE_NAME + #if MICROPY_DEBUG_PARSE_RULE_NAME mp_printf(print, "%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); #else mp_printf(print, "rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); From 817b80a102a413b6458ea391bb2c463aff99672e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Sep 2020 23:35:55 +1000 Subject: [PATCH 331/352] unix/variants: Enable MICROPY_DEBUG_PARSE_RULE_NAME on coverage build. Signed-off-by: Damien George --- .../unix/variants/coverage/mpconfigvariant.h | 1 + tests/cmdline/cmd_parsetree.py.exp | 22 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 3de5294322502..942117608fab1 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -30,6 +30,7 @@ #define MICROPY_VFS (1) #define MICROPY_PY_UOS_VFS (1) +#define MICROPY_DEBUG_PARSE_RULE_NAME (1) #define MICROPY_OPT_MATH_FACTORIAL (1) #define MICROPY_FLOAT_HIGH_QUALITY_HASH (1) #define MICROPY_ENABLE_SCHEDULER (1) diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 42a8228fb77e1..e64f4f782951a 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,31 +1,31 @@ ---------------- -[ 4] rule(1) (n=9) +[ 4] \(rule\|file_input_2\)(1) (n=9) tok(4) -[ 4] rule(22) (n=4) +[ 4] \(rule\|for_stmt\)(22) (n=4) id(i) -[ 4] rule(45) (n=1) +[ 4] \(rule\|atom_paren\)(45) (n=1) NULL -[ 5] rule(8) (n=0) +[ 5] \(rule\|pass_stmt\)(8) (n=0) NULL -[ 6] rule(5) (n=2) +[ 6] \(rule\|expr_stmt\)(5) (n=2) id(a) tok(14) -[ 7] rule(5) (n=2) +[ 7] \(rule\|expr_stmt\)(5) (n=2) id(b) str(str) -[ 8] rule(5) (n=2) +[ 8] \(rule\|expr_stmt\)(5) (n=2) id(c) [ 8] literal \.\+ -[ 9] rule(5) (n=2) +[ 9] \(rule\|expr_stmt\)(5) (n=2) id(d) bytes(bytes) -[ 10] rule(5) (n=2) +[ 10] \(rule\|expr_stmt\)(5) (n=2) id(e) [ 10] literal \.\+ -[ 11] rule(5) (n=2) +[ 11] \(rule\|expr_stmt\)(5) (n=2) id(f) [ 11] literal \.\+ -[ 12] rule(5) (n=2) +[ 12] \(rule\|expr_stmt\)(5) (n=2) id(g) int(123) ---------------- From 0fff2e03fe07471997a6df6f92c6960cfd225dc0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Oct 2020 15:46:13 +1000 Subject: [PATCH 332/352] stm32/Makefile: Allow boards to extend SRC_C, SRC_O and OBJ variables. Signed-off-by: Damien George --- ports/stm32/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 7b68299e04f70..cf3a589ca9087 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -259,7 +259,7 @@ DRIVERS_SRC_C = $(addprefix drivers/,\ dht/dht.c \ ) -SRC_C = \ +SRC_C += \ main.c \ stm32_it.c \ usbd_conf.c \ @@ -330,7 +330,7 @@ SRC_C = \ adc.c \ $(wildcard $(BOARD_DIR)/*.c) -SRC_O = \ +SRC_O += \ $(STARTUP_FILE) \ $(SYSTEM_FILE) @@ -505,7 +505,6 @@ endif endif -OBJ = OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) OBJ += $(LIBM_O) From 1dc64359dab733b3c77f07d9d79589010f6fd8e0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Sep 2020 15:54:58 +1000 Subject: [PATCH 333/352] esp32: Use path relative to root for netutils/timeutils headers. Signed-off-by: Damien George --- ports/esp32/Makefile | 3 --- ports/esp32/fatfs_port.c | 2 +- ports/esp32/machine_rtc.c | 2 +- ports/esp32/modesp32.c | 2 +- ports/esp32/modnetwork.c | 2 +- ports/esp32/network_ppp.c | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index dcf4110cfc913..be282577229c4 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -136,9 +136,6 @@ include $(SDKCONFIG) INC += -I. INC += -I$(TOP) -INC += -I$(TOP)/lib/mp-readline -INC += -I$(TOP)/lib/netutils -INC += -I$(TOP)/lib/timeutils INC += -I$(BUILD) INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include diff --git a/ports/esp32/fatfs_port.c b/ports/esp32/fatfs_port.c index cfc52853d08a9..7fce654c0997c 100644 --- a/ports/esp32/fatfs_port.c +++ b/ports/esp32/fatfs_port.c @@ -28,7 +28,7 @@ #include #include "lib/oofatfs/ff.h" -#include "timeutils.h" +#include "lib/timeutils/timeutils.h" DWORD get_fattime(void) { struct timeval tv; diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index 6902ce85fd2c7..1b6a71b5b4181 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -36,7 +36,7 @@ #include "py/obj.h" #include "py/runtime.h" #include "py/mphal.h" -#include "timeutils.h" +#include "lib/timeutils/timeutils.h" #include "modmachine.h" #include "machine_rtc.h" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 8e248370ffb6d..28d1762d240de 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -41,7 +41,7 @@ #include "py/obj.h" #include "py/runtime.h" #include "py/mphal.h" -#include "timeutils.h" +#include "lib/timeutils/timeutils.h" #include "modmachine.h" #include "machine_rtc.h" #include "modesp32.h" diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 029a8d143127c..44263350409fc 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -40,7 +40,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "py/mperrno.h" -#include "netutils.h" +#include "lib/netutils/netutils.h" #include "esp_eth.h" #include "esp_wifi.h" #include "esp_log.h" diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index df57b817251d8..db2292ca03f7e 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "py/objtype.h" #include "py/stream.h" -#include "netutils.h" +#include "lib/netutils/netutils.h" #include "modmachine.h" #include "netif/ppp/ppp.h" From 7497d891a7cfade68d8c7d26b64080c56e0579e5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 2 Oct 2020 21:23:09 +0200 Subject: [PATCH 334/352] stm32/sdio: Don't change any DMA2 settings on H7 MCUs. DMA2 clock and registers should be left in their current state in the H7 build. --- ports/stm32/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c index 79cc9c9a323a9..87f550e3a6f5d 100644 --- a/ports/stm32/sdio.c +++ b/ports/stm32/sdio.c @@ -76,7 +76,9 @@ void sdio_init(uint32_t irq_pri) { #endif mp_hal_delay_us(10); + #if defined(STM32F7) __HAL_RCC_DMA2_CLK_ENABLE(); // enable DMA2 peripheral + #endif NVIC_SetPriority(SDMMC1_IRQn, irq_pri); @@ -216,7 +218,9 @@ int sdio_transfer(uint32_t cmd, uint32_t arg, uint32_t *resp) { } #endif + #if defined(STM32F7) DMA2_Stream3->CR = 0; // ensure DMA is reset + #endif SDMMC1->ICR = SDMMC_STATIC_FLAGS; // clear interrupts SDMMC1->ARG = arg; SDMMC1->CMD = cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN; @@ -296,7 +300,9 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le SDMMC1->DTIMER = 0x2000000; // about 700ms running at 48MHz SDMMC1->DLEN = (len + block_size - 1) & ~(block_size - 1); + #if defined(STM32F7) DMA2_Stream3->CR = 0; + #endif if (dma) { // prepare DMA so it's ready when the DPSM starts its transfer From 9855b9cd82f9d2e9108fd54ab7d4602134ebae1d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 2 Oct 2020 21:44:15 +0200 Subject: [PATCH 335/352] stm32/sdcard: Fix H7 build when using SDMMC2. Changes are: - Fix missing IRQ handler when SDMMC2 is used instead of SDMMC1 with H7 MCUs. - Removed outdated H7 series compatibility macros. - Defined common IRQ handler macro for F4 series. --- ports/stm32/sdcard.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 9d77722302c99..7d79e9f47b1a8 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -48,12 +48,14 @@ #if defined(MICROPY_HW_SDMMC2_CK) #define SDIO SDMMC2 +#define SDMMC_IRQHandler SDMMC2_IRQHandler #define SDMMC_CLK_ENABLE() __HAL_RCC_SDMMC2_CLK_ENABLE() #define SDMMC_CLK_DISABLE() __HAL_RCC_SDMMC2_CLK_DISABLE() #define SDMMC_IRQn SDMMC2_IRQn #define SDMMC_DMA dma_SDMMC_2 #else #define SDIO SDMMC1 +#define SDMMC_IRQHandler SDMMC1_IRQHandler #define SDMMC_CLK_ENABLE() __HAL_RCC_SDMMC1_CLK_ENABLE() #define SDMMC_CLK_DISABLE() __HAL_RCC_SDMMC1_CLK_DISABLE() #define SDMMC_IRQn SDMMC1_IRQn @@ -86,8 +88,6 @@ #define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDMMC_HARDWARE_FLOW_CONTROL_ENABLE #if defined(STM32H7) -#define GPIO_AF12_SDIO GPIO_AF12_SDIO1 -#define SDIO_IRQHandler SDMMC1_IRQHandler #define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV #define SDIO_USE_GPDMA 0 #else @@ -102,6 +102,7 @@ #define SDMMC_CLK_ENABLE() __SDIO_CLK_ENABLE() #define SDMMC_CLK_DISABLE() __SDIO_CLK_DISABLE() #define SDMMC_IRQn SDIO_IRQn +#define SDMMC_IRQHandler SDIO_IRQHandler #define SDMMC_DMA dma_SDIO_0 #define SDIO_USE_GPDMA 1 #define STATIC_AF_SDMMC_CK STATIC_AF_SDIO_CK @@ -398,21 +399,11 @@ STATIC void sdmmc_irq_handler(void) { } } -#if !defined(MICROPY_HW_SDMMC2_CK) -void SDIO_IRQHandler(void) { - IRQ_ENTER(SDIO_IRQn); +void SDMMC_IRQHandler(void) { + IRQ_ENTER(SDMMC_IRQn); sdmmc_irq_handler(); - IRQ_EXIT(SDIO_IRQn); + IRQ_EXIT(SDMMC_IRQn); } -#endif - -#if defined(STM32F7) -void SDMMC2_IRQHandler(void) { - IRQ_ENTER(SDMMC2_IRQn); - sdmmc_irq_handler(); - IRQ_EXIT(SDMMC2_IRQn); -} -#endif STATIC void sdcard_reset_periph(void) { // Fully reset the SDMMC peripheral before calling HAL SD DMA functions. From 7c76a2dfcffa853e73464434861af185b3e5a9f4 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 1 Oct 2020 14:47:00 +1000 Subject: [PATCH 336/352] stm32/rfcore: Add Python API for basic rfcore operations. The new functions provide FUS/WS status, version and SYS HCI commands: - stm.rfcore_status() - stm.rfcore_fw_version(fw_id) - stm.rfcore_sys_hci(ogf, ocf, cmd) --- ports/stm32/modstm.c | 7 +++ ports/stm32/rfcore.c | 103 +++++++++++++++++++++++++++++++++++-------- ports/stm32/rfcore.h | 4 ++ 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/ports/stm32/modstm.c b/ports/stm32/modstm.c index 418b8bde2a14e..3f4f33979afd5 100644 --- a/ports/stm32/modstm.c +++ b/ports/stm32/modstm.c @@ -30,6 +30,7 @@ #include "py/obj.h" #include "py/objint.h" #include "extmod/machine_mem.h" +#include "rfcore.h" #include "portmodules.h" #if MICROPY_PY_STM @@ -44,6 +45,12 @@ STATIC const mp_rom_map_elem_t stm_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, #include "genhdr/modstm_const.h" + + #if defined(STM32WB) + { MP_ROM_QSTR(MP_QSTR_rfcore_status), MP_ROM_PTR(&rfcore_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_rfcore_fw_version), MP_ROM_PTR(&rfcore_fw_version_obj) }, + { MP_ROM_QSTR(MP_QSTR_rfcore_sys_hci), MP_ROM_PTR(&rfcore_sys_hci_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(stm_module_globals, stm_module_globals_table); diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index dc23bbe0b2a69..436042cc2ecc6 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/mphal.h" +#include "py/runtime.h" #include "rtc.h" #include "rfcore.h" @@ -66,9 +67,16 @@ #define HCI_EVENT_COMMAND_COMPLETE (0x0E) // -#define SYS_ACK_TIMEOUT_MS (250) +// There can be quite long delays during firmware update. +#define SYS_ACK_TIMEOUT_MS (1000) + #define BLE_ACK_TIMEOUT_MS (250) +// AN5185 +#define MAGIC_FUS_ACTIVE 0xA94656B9 +// AN5289 +#define MAGIC_IPCC_MEM_INCORRECT 0x3DE96F61 + typedef struct _tl_list_node_t { volatile struct _tl_list_node_t *next; volatile struct _tl_list_node_t *prev; @@ -267,10 +275,10 @@ void ipcc_init(uint32_t irq_pri) { /******************************************************************************/ // Transport layer HCI interface -STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { +STATIC size_t tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { const char *info; - size_t len = 0; bool applied_set_event_event_mask2_fix = false; + size_t len; switch (buf[0]) { case HCI_KIND_BT_ACL: { info = "HCI_ACL"; @@ -334,6 +342,7 @@ STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { } default: info = "HCI_UNKNOWN"; + len = 0; break; } @@ -354,6 +363,8 @@ STATIC void tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { #else (void)info; #endif + + return len; } STATIC void tl_process_msg(volatile tl_list_node_t *head, unsigned int ch, parse_hci_info_t *parse) { @@ -397,7 +408,7 @@ STATIC void tl_check_msg_ble(volatile tl_list_node_t *head, parse_hci_info_t *pa } } -STATIC void tl_hci_cmd(uint8_t *cmd, unsigned int ch, uint8_t hdr, uint16_t opcode, size_t len, const uint8_t *buf) { +STATIC void tl_hci_cmd(uint8_t *cmd, unsigned int ch, uint8_t hdr, uint16_t opcode, const uint8_t *buf, size_t len) { tl_list_node_t *n = (tl_list_node_t *)cmd; n->next = n; n->prev = n; @@ -407,11 +418,19 @@ STATIC void tl_hci_cmd(uint8_t *cmd, unsigned int ch, uint8_t hdr, uint16_t opco cmd[11] = len; memcpy(&cmd[12], buf, len); + #if HCI_TRACE + printf("[% 8d] >HCI(", mp_hal_ticks_ms()); + for (int i = 0; i < len + 4; ++i) { + printf(":%02x", cmd[i + 8]); + } + printf(")\n"); + #endif + // Indicate that this channel is ready. LL_C1_IPCC_SetFlag_CHx(IPCC, ch); } -STATIC int tl_sys_wait_ack(const uint8_t *buf) { +STATIC ssize_t tl_sys_wait_ack(const uint8_t *buf) { uint32_t t0 = mp_hal_ticks_ms(); // C2 will clear this bit to acknowledge the request. @@ -422,14 +441,14 @@ STATIC int tl_sys_wait_ack(const uint8_t *buf) { } } - // C1-to-C2 bit cleared, so process (but ignore) the response. - tl_parse_hci_msg(buf, NULL); - return 0; + // C1-to-C2 bit cleared, so process the response (just get the length, do + // not parse any further). + return (ssize_t)tl_parse_hci_msg(buf, NULL); } -STATIC void tl_sys_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd(ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, len, buf); - tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf); +STATIC ssize_t tl_sys_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len) { + tl_hci_cmd(ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, buf, len); + return tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf); } STATIC int tl_ble_wait_resp(void) { @@ -447,10 +466,9 @@ STATIC int tl_ble_wait_resp(void) { } // Synchronously send a BLE command. -STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, size_t len, const uint8_t *buf) { - tl_hci_cmd(ipcc_membuf_ble_cmd_buf, IPCC_CH_BLE, HCI_KIND_BT_CMD, opcode, len, buf); +STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len) { + tl_hci_cmd(ipcc_membuf_ble_cmd_buf, IPCC_CH_BLE, HCI_KIND_BT_CMD, opcode, buf, len); tl_ble_wait_resp(); - } /******************************************************************************/ @@ -522,8 +540,8 @@ void rfcore_ble_init(void) { tl_check_msg_ble(&ipcc_mem_ble_evt_queue, NULL); // Configure and reset the BLE controller - tl_sys_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_BLE_INIT), sizeof(ble_init_params), (const uint8_t *)&ble_init_params); - tl_ble_hci_cmd_resp(HCI_OPCODE(0x03, 0x0003), 0, NULL); + tl_sys_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_BLE_INIT), (const uint8_t *)&ble_init_params, sizeof(ble_init_params)); + tl_ble_hci_cmd_resp(HCI_OPCODE(0x03, 0x0003), NULL, 0); } void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { @@ -573,14 +591,14 @@ void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env) SWAP_UINT8(buf[2], buf[7]); SWAP_UINT8(buf[3], buf[6]); SWAP_UINT8(buf[4], buf[5]); - tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_WRITE_CONFIG), 8, buf); // set BDADDR + tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_WRITE_CONFIG), buf, 8); // set BDADDR } } // "level" is 0x00-0x1f, ranging from -40 dBm to +6 dBm (not linear). void rfcore_ble_set_txpower(uint8_t level) { uint8_t buf[2] = { 0x00, level }; - tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_SET_TX_POWER), 2, buf); + tl_ble_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_SET_TX_POWER), buf, 2); } // IPCC IRQ Handlers @@ -605,4 +623,53 @@ void IPCC_C1_RX_IRQHandler(void) { IRQ_EXIT(IPCC_C1_RX_IRQn); } +/******************************************************************************/ +// MicroPython bindings + +STATIC mp_obj_t rfcore_status(void) { + return mp_obj_new_int_from_uint(ipcc_mem_dev_info_tab.fus.table_state); +} +MP_DEFINE_CONST_FUN_OBJ_0(rfcore_status_obj, rfcore_status); + +STATIC mp_obj_t get_version_tuple(uint32_t data) { + mp_obj_t items[] = { + MP_OBJ_NEW_SMALL_INT(data >> 24), MP_OBJ_NEW_SMALL_INT(data >> 16 & 0xFF), MP_OBJ_NEW_SMALL_INT(data >> 8 & 0xFF), MP_OBJ_NEW_SMALL_INT(data >> 4 & 0xF), MP_OBJ_NEW_SMALL_INT(data & 0xF) + }; + return mp_obj_new_tuple(5, items); +} + +STATIC mp_obj_t rfcore_fw_version(mp_obj_t fw_id_in) { + if (ipcc_mem_dev_info_tab.fus.table_state == MAGIC_IPCC_MEM_INCORRECT) { + mp_raise_OSError(MP_EINVAL); + } + mp_int_t fw_id = mp_obj_get_int(fw_id_in); + bool fus_active = ipcc_mem_dev_info_tab.fus.table_state == MAGIC_FUS_ACTIVE; + uint32_t v; + if (fw_id == 0) { + // FUS + v = fus_active ? ipcc_mem_dev_info_tab.fus.fus_version : ipcc_mem_dev_info_tab.ws.fus_version; + } else { + // WS + v = fus_active ? ipcc_mem_dev_info_tab.fus.ws_version : ipcc_mem_dev_info_tab.ws.fw_version; + } + return get_version_tuple(v); +} +MP_DEFINE_CONST_FUN_OBJ_1(rfcore_fw_version_obj, rfcore_fw_version); + +STATIC mp_obj_t rfcore_sys_hci(mp_obj_t ogf_in, mp_obj_t ocf_in, mp_obj_t cmd_in) { + if (ipcc_mem_dev_info_tab.fus.table_state == MAGIC_IPCC_MEM_INCORRECT) { + mp_raise_OSError(MP_EINVAL); + } + mp_int_t ogf = mp_obj_get_int(ogf_in); + mp_int_t ocf = mp_obj_get_int(ocf_in); + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(cmd_in, &bufinfo, MP_BUFFER_READ); + ssize_t len = tl_sys_hci_cmd_resp(HCI_OPCODE(ogf, ocf), bufinfo.buf, bufinfo.len); + if (len < 0) { + mp_raise_OSError(-len); + } + return mp_obj_new_bytes(ipcc_membuf_sys_cmd_buf, len); +} +MP_DEFINE_CONST_FUN_OBJ_3(rfcore_sys_hci_obj, rfcore_sys_hci); + #endif // defined(STM32WB) diff --git a/ports/stm32/rfcore.h b/ports/stm32/rfcore.h index fbe111e1ebdcd..39f6220a32d26 100644 --- a/ports/stm32/rfcore.h +++ b/ports/stm32/rfcore.h @@ -35,4 +35,8 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src); void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env); void rfcore_ble_set_txpower(uint8_t level); +MP_DECLARE_CONST_FUN_OBJ_0(rfcore_status_obj); +MP_DECLARE_CONST_FUN_OBJ_1(rfcore_fw_version_obj); +MP_DECLARE_CONST_FUN_OBJ_3(rfcore_sys_hci_obj); + #endif // MICROPY_INCLUDED_STM32_RFCORE_H From 222ec1a4a86cdcfa9546c30243e4e244bec9697f Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 29 Sep 2020 18:37:45 +1000 Subject: [PATCH 337/352] stm32/boards/NUCLEO_WB55: Add standalone WB55 FUS/WS firmware updater. This commit adds a script that can be run on-device to install FUS and WS binaries from the filesystem. Instructions for use are provided in the rfcore_firmware.py file. The commit also removes unneeded functionality from the existing rfcore.py debug script (and renames it rfcore_debug.py). --- .../{rfcore.py => rfcore_debug.py} | 119 +--- .../boards/NUCLEO_WB55/rfcore_firmware.py | 547 ++++++++++++++++++ .../boards/NUCLEO_WB55/rfcore_makefirmware.py | 79 +++ 3 files changed, 630 insertions(+), 115 deletions(-) rename ports/stm32/boards/NUCLEO_WB55/{rfcore.py => rfcore_debug.py} (74%) create mode 100644 ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py create mode 100755 ports/stm32/boards/NUCLEO_WB55/rfcore_makefirmware.py diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_debug.py similarity index 74% rename from ports/stm32/boards/NUCLEO_WB55/rfcore.py rename to ports/stm32/boards/NUCLEO_WB55/rfcore_debug.py index cfe315605e541..4dbead0acc63d 100644 --- a/ports/stm32/boards/NUCLEO_WB55/rfcore.py +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_debug.py @@ -27,85 +27,16 @@ # mechanism in the WB55, and works with the memory layout configured in # ports/stm32/rfcore.c -- i.e. it expects that rfcore_init() has been run. -# At this stage this is useful for debugging, but can be extended to support -# FUS/WS firmware updates. # e.g. # ../../tools/pyboard.py --device /dev/ttyACM0 boards/NUCLEO_WB55/rfcore.py # to print out SRAM2A, register state and FUS/WS info. +# +# The `stm` module provides some helper functions to access rfcore functionality. +# See rfcore_firmware.py for more information. from machine import mem8, mem16, mem32 -import time, struct import stm - -def addressof(buf): - assert type(buf) is bytearray - return mem32[id(buf) + 12] - - -class Flash: - FLASH_KEY1 = 0x45670123 - FLASH_KEY2 = 0xCDEF89AB - - def wait_not_busy(self): - while mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: - machine.idle() - - def unlock(self): - mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY1 - mem32[stm.FLASH + stm.FLASH_KEYR] = Flash.FLASH_KEY2 - - def lock(self): - mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK - - def erase_page(self, page): - print("erase", page) - assert 0 <= page <= 255 # 1MiB range (4k page) - self.wait_not_busy() - cr = page << 3 | 1 << 1 # PNB # PER - mem32[stm.FLASH + stm.FLASH_CR] = cr - mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT - self.wait_not_busy() - mem32[stm.FLASH + stm.FLASH_CR] = 0 - - def write(self, addr, buf): - assert len(buf) % 4 == 0 - self.wait_not_busy() - cr = 1 << 0 # PG - mem32[stm.FLASH + stm.FLASH_CR] = cr - buf_addr = addressof(buf) - off = 0 - while off < len(buf): - mem32[addr + off] = mem32[buf_addr + off] - off += 4 - if off % 8 == 0: - self.wait_not_busy() - if off % 8: - mem32[addr + off] = 0 - self.wait_not_busy() - mem32[stm.FLASH + stm.FLASH_CR] = 0 - - -def copy_file_to_flash(filename, addr): - flash = Flash() - flash.unlock() - try: - with open(filename, "rb") as f: - buf = bytearray(4096) - while 1: - sz = f.readinto(buf) - if sz == 0: - break - print("write", hex(addr), sz) - flash.erase_page((addr - 0x08000000) // 4096) - print("done e") - flash.write(addr, buf) - print("done") - addr += 4096 - finally: - flash.lock() - - SRAM2A_BASE = const(0x2003_0000) # for vendor OGF @@ -205,49 +136,6 @@ def ipcc_init(): print("BLE: 0x%08x 0x%08x 0x%08x" % (BLE_CMD_BUF, BLE_CS_BUF, BLE_EVT_QUEUE)) -def tl_list_init(addr): - mem32[addr] = addr # next - mem32[addr + 4] = addr # prev - - -def tl_list_append(head, n): - sram2a_dump(1024) - print("Appending 0x%08x to 0x%08x" % (head, n)) - # item->next = head - mem32[n] = head - # item->prev = head->prev - mem32[n + 4] = mem32[head + 4] - # head->prev->next = item - mem32[mem32[head + 4]] = n - # head->prev = item - mem32[head + 4] = n - - -def tl_list_unlink(n): - # next = item->next - next = mem32[n] - # prev = item->prev - prev = mem32[n + 4] - # prev->next = item->next - mem32[prev] = next - # item->next->prev = prev - mem32[next + 4] = prev - - return next - - -def tl_list_dump(head): - print( - "list(%08x, %08x, %08x):" % (head, mem32[head] & 0xFFFFFFFF, mem32[head + 4] & 0xFFFFFFFF), - end="", - ) - cur = mem32[head] - while cur != head: - print(" %08x" % (cur & 0xFFFFFFFF), end="") - cur = mem32[cur] - print() - - def fus_active(): return get_ipcc_table_word(TABLE_DEVICE_INFO, 0) == MAGIC_FUS_ACTIVE @@ -346,3 +234,4 @@ def ipcc_state(): ipcc_init() info() dev_info() +ipcc_state() diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py new file mode 100644 index 0000000000000..53c96a552a34c --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py @@ -0,0 +1,547 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2020 Damien P. George +# Copyright (c) 2020 Jim Mussared +# +# 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. + +# This script provides helpers for working with the FUS/WS firmware on the WB55. +# It can be frozen into the MicroPython firmware (via manifest.py) +# +# The current FUS and WS firmware version and state can be queried via the +# `stm` module, e.g. +# stm.rfcore_status() (returns the first word of the device info table) +# stm.rfcore_fw_version(id) (returns a 5-tuple indicating fw version; id is: 0=FUS, 1=WS) +# stm.rfcore_sys_hci(ogf, ocf, cmd_buf) (synchronously execute HCI command on SYS channel) +# +# To perform a firmware update: +# +# 1. Generate "obfuscated" binary images using rfcore_makefirmware.py +# ./boards/NUCLEO_WB55/rfcore_makefirmware.py ~/src/github.com/STMicroelectronics/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/ /tmp +# This will generate /tmp/{fus_102,fus_110,ws_ble_hci}.bin +# +# 2. Copy required files to the device filesystem. +# In general, it's always safe to copy all three files and the updater will +# figure out what needs to be done. This is the recommended option. +# However, if you already have the latest FUS (1.1.0) installed, then just the +# WS firmware is required. +# If a FUS binary is present, then the existing WS will be removed so it's a good +# idea to always include the WS binary if updating FUS. +# Note that a WS binary will not be installed unless FUS 1.1.0 is installed. +# +# 3. Ensure boot.py calls `rfcore_firmware.resume()`. +# The WB55 will reset several times during the firmware update process, so this +# script manages the update state using RTC backup registers. +# `rfcore_firmware.resume()` will continue the update operation on startup to +# resume any in-progress update operation, and either trigger another reset, or +# return 0 to indicate that the operation completed successfully, or a reason +# code (see REASON_* below) to indicate failure. +# +# 4. Call rfcore_firmware.check_for_updates() to start the update process. +# The device will then immediately reboot and when the firmware update completes, +# the status will be returned from rfcore_firmware.resume(). See the REASON_ codes below. +# You can use the built-in stm.rfcore_fw_version() to query the installed version +# from your application code. + +import struct, os +import machine, stm +from micropython import const + +_OGF_VENDOR = const(0x3F) + +_OCF_FUS_GET_STATE = const(0x52) +_OCF_FUS_FW_UPGRADE = const(0x54) +_OCF_FUS_FW_DELETE = const(0x55) +_OCF_FUS_START_WS = const(0x5A) +_OCF_BLE_INIT = const(0x66) + +_HCI_KIND_VENDOR_RESPONSE = const(0x11) + + +# The firmware updater will search all of flash for the image to install, so +# it's important that the file doesn't exist anywhere on the filesystem and +# that the updater only finds the version that we copy into the reserved area. +# Otherwise it will find matching headers/footers in the flash filesystem and +# get confused leading to either "FUS_STATE_IMG_NOT_AUTHENTIC" or (worse) +# corrupting the FUS. +# See footnote [1] referenced by Table 9 in AN5185 - Rev 4 -- the address +# passed to FUS_FW_UPGRADE is ignored (implying that it must be searching the +# flash). This requires that the firmware files have been pre-processed by +# rfcore_makefirmware.py and this key must match the one there. +_OBFUSCATION_KEY = const(0x0573B55AA) + +# On boards using the internal flash filesystem, this must match the +# `_flash_fs_end` symbol defined by the linker script (boards/stm32wb55xg.ld). +# We erase everything from here until the start of the secure area (defined by +# SFSA) just to ensure that no other fragments of firmware files are left +# behind. On boards with external flash, this just needs to ensure that it +# includes any regions that may contain partial firmware data. +# This is non-const so it can be override. +STAGING_AREA_START = 0x80C0000 + +# First word of device info table indicating FUS state (returned by `stm.rfcore_status()`). +_MAGIC_FUS_ACTIVE = const(0xA94656B9) # AN5185 +_MAGIC_IPCC_MEM_INCORRECT = const(0x3DE96F61) # # AN5185 + +# Argument to `stm.rfcore_fw_version()`. +_FW_VERSION_FUS = const(0) +_FW_VERSION_WS = const(1) + +# No firmware update in progress. Boot normally. +_STATE_IDLE = const(0) + +# A previous firmware update failed. Will return reason code from resume(). +_STATE_FAILED = const(1) + +# Trying to get into the FUS. Keep issuing GET_STATE until the FUS is active. +_STATE_WAITING_FOR_FUS = const(2) + +# Trying to get into the WS. Keep issuing START_WS until the WS is active (or fails). +_STATE_WAITING_FOR_WS = const(3) + +# FW_DELETE has been issued. Waiting for the WS version to report zero. +_STATE_DELETING_WS = const(4) + +# Flash copy has started for FUS/WS. If a reboot occurs, then fail. +_STATE_COPYING_FUS = const(5) +_STATE_COPYING_WS = const(6) + +# Flash write fully completed, ready for install. +_STATE_COPIED_FUS = const(7) +_STATE_COPIED_WS = const(8) + +# Check for next update to perform. +# Either we've just gotten into the FUS, or the first update in a sequence +# has completed. (e.g. FUS done, now do WS). +_STATE_CHECK_UPDATES = const(9) + +# Installation has started, keep polling GET_STATE. +_STATE_INSTALLING_WS = const(10) +_STATE_INSTALLING_FUS = const(11) + +# Update completed successfully. +REASON_OK = const(0) +# The device reset during flash copy. Possibly WS still installed. +REASON_FLASH_COPY_FAILED = const(1) +# Unable to start the WS after firmware update. +REASON_NO_WS = const(2) +# Copying FUS image to staging area caused FUS to fail. +REASON_FLASH_FUS_BAD_STATE = const(3) +# Copying WS image to staging area caused FUS to fail. +REASON_FLASH_WS_BAD_STATE = const(4) +# Cannot get into the FUS. Perhaps rfcore misconfigured. +REASON_FUS_NOT_RESPONDING = const(5) +# After a FUS install, unable to get back to the FUS. +REASON_FUS_NOT_RESPONDING_AFTER_FUS = const(6) +# After a WS install, unable to get back to the FUS. +REASON_FUS_NOT_RESPONDING_AFTER_WS = const(7) +# Unable to query rfcore version/active. +REASON_RFCORE_NOT_CONFIGURED = const(8) +# The WS deletion didn't have any effect. +REASON_WS_STILL_PRESENT = const(9) +# FUS refused to delete the WS. +REASON_WS_DELETION_FAILED = const(10) +# FUS returned a specific code for a FUS update. +# See AN5185 Rev 4, Table 12. Reason between 0x00-0x11 will be added. +REASON_FUS_VENDOR = const(0x10) +# FUS returned a specific code for a WS update. Values as for the FUS update. +REASON_WS_VENDOR = const(0x30) + +# FUS 1.0.2 must be installed before FUS 1.1.0 can be installed. +# A factory Nucleo board has FUS (0, 5, 3, 0, 0) and WS (0, 5, 1, 0, 0). +_FUS_VERSION_102 = (1, 0, 2, 0, 0) +_FUS_VERSION_110 = (1, 1, 0, 0, 0) +_PATH_FUS_102 = "fus_102.bin" +_PATH_FUS_110 = "fus_110.bin" +_PATH_WS_BLE_HCI = "ws_ble_hci.bin" + +# This address is correct for versions up to v1.8 (assuming existing firmware deleted). +# Note any address from the end of the filesystem to the SFSA would be fine, but if +# the FUS is fixed in the future to use the specified address then these are the "correct" +# ones. +_ADDR_FUS = 0x080EC000 +_ADDR_WS_BLE_HCI = 0x080DC000 + + +def log(msg, *args, **kwargs): + print("[rfcore update]", msg.format(*args, **kwargs)) + + +class _Flash: + _FLASH_KEY1 = 0x45670123 + _FLASH_KEY2 = 0xCDEF89AB + + def wait_not_busy(self): + while machine.mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: + machine.idle() + + def unlock(self): + machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY1 + machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY2 + + def lock(self): + machine.mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + + def erase_page(self, page): + assert 0 <= page <= 255 # 1MiB range (4k page) + self.wait_not_busy() + cr = page << 3 | 1 << 1 # PNB # PER + machine.mem32[stm.FLASH + stm.FLASH_CR] = cr + machine.mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT + self.wait_not_busy() + machine.mem32[stm.FLASH + stm.FLASH_CR] = 0 + + def write(self, addr, buf, sz, key=0): + assert sz % 4 == 0 + self.wait_not_busy() + cr = 1 << 0 # PG + machine.mem32[stm.FLASH + stm.FLASH_CR] = cr + off = 0 + while off < sz: + v = (buf[off]) | (buf[off + 1] << 8) | (buf[off + 2] << 16) | (buf[off + 3] << 24) + machine.mem32[addr + off] = v ^ key + off += 4 + if off % 8 == 0: + self.wait_not_busy() + if off % 8: + machine.mem32[addr + off] = 0 + self.wait_not_busy() + machine.mem32[stm.FLASH + stm.FLASH_CR] = 0 + + +def _copy_file_to_flash(filename, addr): + flash = _Flash() + flash.unlock() + try: + # Erase the entire staging area in flash. + erase_addr = STAGING_AREA_START + sfr_sfsa = machine.mem32[stm.FLASH + stm.FLASH_SFR] & 0xFF + erase_limit = 0x08000000 + sfr_sfsa * 4096 + while erase_addr < erase_limit: + flash.erase_page((erase_addr - 0x08000000) // 4096) + erase_addr += 4096 + + # Write the contents of the firmware (note flash.write will apply the + # XOR de-obfuscation). + with open(filename, "rb") as f: + buf = bytearray(4096) + + while 1: + sz = f.readinto(buf) + if sz == 0: + break + flash.write(addr, buf, sz, _OBFUSCATION_KEY) + addr += 4096 + + finally: + flash.lock() + + +def _parse_vendor_response(data): + assert len(data) >= 7 + assert data[0] == _HCI_KIND_VENDOR_RESPONSE + assert data[1] == 0x0E + # assert data[3] == 0xff # "Num HCI" -- docs say 0xff, but we see 0x01 + op = (data[5] << 8) | data[4] + return (op >> 10, op & 0x3FF, data[6], data[7] if len(data) > 7 else 0) + + +def _run_sys_hci_cmd(ogf, ocf, buf=b""): + try: + ogf_out, ocf_out, status, result = _parse_vendor_response( + stm.rfcore_sys_hci(ogf, ocf, buf) + ) + except OSError: + # Timeout or FUS not active. + return (0xFF, 0xFF) + assert ogf_out == ogf + assert ocf_out == ocf + return (status, result) + + +def fus_get_state(): + return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_GET_STATE) + + +def fus_is_idle(): + return fus_get_state() == (0, 0) + + +def fus_start_ws(): + return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_START_WS) + + +def _fus_fwdelete(): + return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_DELETE) + + +def _fus_run_fwupgrade(addr): + # Note: Address is ignored by the FUS (see comments above). + return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_UPGRADE, struct.pack(" FUS 1.1.0 -> WS (depending on what's available). + elif _STATE_id == _STATE_CHECK_UPDATES: + log("Checking for updates") + fus_version = stm.rfcore_fw_version(_FW_VERSION_FUS) + log("FUS version {}", fus_version) + if fus_version < _FUS_VERSION_102: + log("Factory FUS detected") + if _stat_and_start_copy( + _PATH_FUS_102, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS + ): + continue + elif fus_version >= _FUS_VERSION_102 and fus_version < _FUS_VERSION_110: + log("FUS 1.0.2 detected") + if _stat_and_start_copy( + _PATH_FUS_110, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS + ): + continue + else: + log("FUS is up-to-date") + + if fus_version >= _FUS_VERSION_110: + if _stat_and_start_copy( + _PATH_WS_BLE_HCI, _ADDR_WS_BLE_HCI, _STATE_COPYING_WS, _STATE_COPIED_WS + ): + continue + else: + log("No WS updates available") + else: + # Don't attempt to install WS if we're running an old FUS. + log("Need latest FUS to install WS") + + # Attempt to go back to WS. + # Either this will fail (because WS was removed due to FUS install), or + # this whole thing was a no-op and we should be fine to restart WS. + _write_state(_STATE_WAITING_FOR_WS) + + # This shouldn't happen - the flash write should always complete and + # move straight onto the COPIED state. Failure here indicates that + # the rfcore is misconfigured or the WS firmware was not deleted first. + elif _STATE_id == _STATE_COPYING_FUS or _STATE_id == _STATE_COPYING_WS: + log("Flash copy failed mid-write") + _write_failure_state(REASON_FLASH_COPY_FAILED) + + # Flash write completed, we should immediately see GET_STATE return 0,0 + # so we can start the FUS install. + elif _STATE_id == _STATE_COPIED_FUS: + if fus_is_idle(): + log("FUS copy complete, installing") + _write_state(_STATE_INSTALLING_FUS) + _fus_run_fwupgrade(_ADDR_FUS) + else: + log("FUS copy bad state") + _write_failure_state(REASON_FLASH_FUS_BAD_STATE) + + # Keep polling the state until we see a 0,0 (success) or non-transient + # error. In general we should expect to see (16,0) several times, + # followed by a (255,0), followed by (0, 0). + elif _STATE_id == _STATE_INSTALLING_FUS: + log("Installing FUS...") + status, result = fus_get_state() + log("FUS state: {} {}", status, result) + if 0x20 <= status <= 0x2F and result == 0: + # FUS_STATE_FUS_UPGRD_ONGOING + log("FUS still in progress...") + elif 0x10 <= status <= 0x1F and result == 0x11: + # FUS_STATE_FW_UPGRD_ONGOING and FUS_FW_ROLLBACK_ERROR + # Confusingly this is a "FW_UPGRD" (0x10) not "FUS_UPRD" (0x20). + log("Attempted to install same FUS version... re-querying FUS state to resume.") + elif status == 0: + log("FUS update successful") + _write_state(_STATE_CHECK_UPDATES) + # Need to force a reset after FUS install otherwise a subsequent flash copy will fail. + machine.reset() + elif result == 0: + # See below (for equivalent path for WS install -- we + # sometimes see (255,0) right at the end). + log("Re-querying FUS state...") + elif result == 0xFF: + _write_failure_state(REASON_FUS_NOT_RESPONDING_AFTER_FUS) + else: + _write_failure_state(REASON_FUS_VENDOR + result) + + # Keep polling the state until we see 0,0 or failure (1,0). Any other + # result means retry (but the docs say that 0 and 1 are the only + # status values). + elif _STATE_id == _STATE_DELETING_WS: + log("Deleting WS...") + status, result = fus_get_state() + log("FUS state: {} {}", status, result) + if status == 0: + if sum(stm.rfcore_fw_version(_FW_VERSION_WS)) == 0: + log("WS deletion complete") + _write_state(_STATE_CHECK_UPDATES) + else: + log("WS deletion no effect") + _write_failure_state(REASON_WS_STILL_PRESENT) + elif status == 1: + log("WS deletion failed") + _write_failure_state(REASON_WS_DELETION_FAILED) + + # As for _STATE_COPIED_FUS above. We should immediately see 0,0. + elif _STATE_id == _STATE_COPIED_WS: + if fus_is_idle(): + log("WS copy complete, installing") + _write_state(_STATE_INSTALLING_WS) + _fus_run_fwupgrade(_ADDR_WS_BLE_HCI) + else: + log("WS copy bad state") + _write_failure_state(REASON_FLASH_WS_BAD_STATE) + + # As for _STATE_INSTALLING_FUS above. + elif _STATE_id == _STATE_INSTALLING_WS: + log("Installing WS...") + status, result = fus_get_state() + log("FUS state: {} {}", status, result) + if 0x10 <= status <= 0x1F and result == 0: + # FUS_STATE_FW_UPGRD_ONGOING + log("WS still in progress...") + elif 0x10 <= status <= 0x1F and result == 0x11: + # FUS_FW_ROLLBACK_ERROR + log("Attempted to install same WS version... re-querying FUS state to resume.") + elif status == 0: + log("WS update successful") + _write_state(_STATE_WAITING_FOR_WS) + elif result == 0: + # We get a error response with no payload sometimes at the end + # of the update (this is not in AN5185). Re-try the GET_STATE. + # The same thing happens transitioning from WS to FUS mode. + # The actual HCI response has no payload, the result=0 comes from + # _parse_vendor_response above when len=7. + log("Re-querying FUS state...") + elif result == 0xFF: + # This is specifically a failure sending the HCI command. + _write_failure_state(REASON_FUS_NOT_RESPONDING_AFTER_WS) + else: + _write_failure_state(REASON_WS_VENDOR + result) + + +# Start a firmware update. +# This will immediately trigger a reset and start the update process on boot. +def check_for_updates(): + log("Starting firmware update") + _write_state(_STATE_WAITING_FOR_FUS) + machine.reset() diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore_makefirmware.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_makefirmware.py new file mode 100755 index 0000000000000..23f3d20f0c9ce --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_makefirmware.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2020 Jim Mussared +# +# 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. + +# This script obfuscates the ST wireless binaries so they can be safely copied +# to the flash filesystem and not be accidentally discovered by the FUS during +# an update. See more information (and the corresponding de-obfuscation) in +# rfcore_firmware.py as well as instructions on how to use. + +import os +import struct +import sys + +# Must match rfcore_firmware.py. +_OBFUSCATION_KEY = 0x0573B55AA + +_FIRMWARE_FILES = { + "stm32wb5x_FUS_fw_1_0_2.bin": "fus_102.bin", + "stm32wb5x_FUS_fw.bin": "fus_110.bin", + "stm32wb5x_BLE_HCILayer_fw.bin": "ws_ble_hci.bin", +} + + +def main(src_path, dest_path): + for src_file, dest_file in _FIRMWARE_FILES.items(): + src_file = os.path.join(src_path, src_file) + dest_file = os.path.join(dest_path, dest_file) + if not os.path.exists(src_file): + print("Unable to find: {}".format(src_file)) + continue + sz = 0 + with open(src_file, "rb") as src: + with open(dest_file, "wb") as dest: + while True: + b = src.read(4) + if not b: + break + (v,) = struct.unpack(" Date: Fri, 9 Oct 2020 16:51:47 +1100 Subject: [PATCH 338/352] stm32/rfcore: Update to support WS=1.9.0.0.4. This WS update to 1.9.0.0.4 broke the workaround used in rfcore for OCF_CB_SET_EVENT_MASK2, so fix it to support WS 1.8 and 1.9. --- ports/stm32/rfcore.c | 45 +++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 436042cc2ecc6..3850a17dae642 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -66,6 +66,7 @@ #define HCI_KIND_VENDOR_EVENT (0x12) #define HCI_EVENT_COMMAND_COMPLETE (0x0E) // +#define HCI_EVENT_COMMAND_STATUS (0x0F) // // There can be quite long delays during firmware update. #define SYS_ACK_TIMEOUT_MS (1000) @@ -275,9 +276,19 @@ void ipcc_init(uint32_t irq_pri) { /******************************************************************************/ // Transport layer HCI interface +// The WS firmware doesn't support OCF_CB_SET_EVENT_MASK2, and fails with: +// v1.8.0.0.4 (and below): HCI_EVENT_COMMAND_COMPLETE with a non-zero status +// v1.9.0.0.4 (and above): HCI_EVENT_COMMAND_STATUS with a non-zero status +// In either case we detect the failure response and inject this response +// instead (which is HCI_EVENT_COMMAND_COMPLETE for OCF_CB_SET_EVENT_MASK2 +// with status=0). +STATIC const uint8_t set_event_event_mask2_fix_payload[] = { 0x04, 0x0e, 0x04, 0x01, 0x63, 0x0c, 0x00 }; + STATIC size_t tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { const char *info; - bool applied_set_event_event_mask2_fix = false; + #if HCI_TRACE + int applied_set_event_event_mask2_fix = 0; + #endif size_t len; switch (buf[0]) { case HCI_KIND_BT_ACL: { @@ -300,10 +311,12 @@ STATIC size_t tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { uint8_t status = buf[6]; if (opcode == HCI_OPCODE(OGF_CTLR_BASEBAND, OCF_CB_SET_EVENT_MASK2) && status != 0) { - // The WB doesn't support this command (despite being in CS 4.1), so pretend like - // it succeeded by replacing the final byte (status) with a zero. - applied_set_event_event_mask2_fix = true; - len -= 1; + // For WS firmware v1.8.0.0.4 and below. Reply with the "everything OK" payload. + parse->cb_fun(parse->cb_env, set_event_event_mask2_fix_payload, sizeof(set_event_event_mask2_fix_payload)); + #if HCI_TRACE + applied_set_event_event_mask2_fix = 18; + #endif + break; // Don't send the original payload. } if (opcode == HCI_OPCODE(OGF_CTLR_BASEBAND, OCF_CB_RESET) && status == 0) { @@ -313,15 +326,21 @@ STATIC size_t tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { } } - parse->cb_fun(parse->cb_env, buf, len); + if (buf[1] == HCI_EVENT_COMMAND_STATUS && len == 7) { + uint16_t opcode = (buf[6] << 8) | buf[5]; + uint8_t status = buf[3]; - if (applied_set_event_event_mask2_fix) { - // Inject the zero status. - uint8_t data = 0; - parse->cb_fun(parse->cb_env, &data, 1); - // Restore the length for the HCI tracing below. - len += 1; + if (opcode == HCI_OPCODE(OGF_CTLR_BASEBAND, OCF_CB_SET_EVENT_MASK2) && status != 0) { + // For WS firmware v1.9.0.0.4 and higher. Reply with the "everything OK" payload. + parse->cb_fun(parse->cb_env, set_event_event_mask2_fix_payload, sizeof(set_event_event_mask2_fix_payload)); + #if HCI_TRACE + applied_set_event_event_mask2_fix = 19; + #endif + break; // Don't send the original payload. + } } + + parse->cb_fun(parse->cb_env, buf, len); } break; } @@ -356,7 +375,7 @@ STATIC size_t tl_parse_hci_msg(const uint8_t *buf, parse_hci_info_t *parse) { printf(" (reset)"); } if (applied_set_event_event_mask2_fix) { - printf(" (mask2 fix)"); + printf(" (mask2 fix %d)", applied_set_event_event_mask2_fix); } printf("\n"); From 880875bea1d825b8fa0d1c4a779ff767377f7655 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 9 Oct 2020 17:10:29 +1100 Subject: [PATCH 339/352] py/objdict: Add mp_const_empty_dict_obj, use it for mp_const_empty_map. --- py/map.c | 11 ----------- py/obj.h | 9 ++++++--- py/objdict.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/py/map.c b/py/map.c index 676c364da76a1..54f4b0204b87f 100644 --- a/py/map.c +++ b/py/map.c @@ -40,17 +40,6 @@ #define DEBUG_printf(...) (void)0 #endif -// Fixed empty map. Useful when need to call kw-receiving functions -// without any keywords from C, etc. -const mp_map_t mp_const_empty_map = { - .all_keys_are_qstrs = 0, - .is_fixed = 1, - .is_ordered = 1, - .used = 0, - .alloc = 0, - .table = NULL, -}; - // This table of sizes is used to control the growth of hash tables. // The first set of sizes are chosen so the allocation fits exactly in a // 4-word GC block, and it's not so important for these small values to be diff --git a/py/obj.h b/py/obj.h index 31542a7f9bd7c..6a040b77739c2 100644 --- a/py/obj.h +++ b/py/obj.h @@ -422,8 +422,6 @@ typedef enum _mp_map_lookup_kind_t { MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND = 3, // only valid for mp_set_lookup } mp_map_lookup_kind_t; -extern const mp_map_t mp_const_empty_map; - static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { assert(pos < map->alloc); return (map)->table[pos].key != MP_OBJ_NULL && (map)->table[pos].key != MP_OBJ_SENTINEL; @@ -685,17 +683,22 @@ extern const struct _mp_obj_bool_t mp_const_false_obj; extern const struct _mp_obj_bool_t mp_const_true_obj; #endif -// Constant objects, globally accessible: b'', (), Ellipsis, NotImplemented, GeneratorExit() +// Constant objects, globally accessible: b'', (), {}, Ellipsis, NotImplemented, GeneratorExit() // The below macros are for convenience only. #define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj)) #define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj)) #define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj)) extern const struct _mp_obj_str_t mp_const_empty_bytes_obj; extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj; +extern const struct _mp_obj_dict_t mp_const_empty_dict_obj; extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj; extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj; extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; +// Fixed empty map. Useful when calling keyword-receiving functions +// without any keywords from C, etc. +#define mp_const_empty_map (mp_const_empty_dict_obj.map) + // General API for objects // These macros are derived from more primitive ones and are used to diff --git a/py/objdict.c b/py/objdict.c index 4fa59f4634cb0..4e51f259e7ff4 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -33,6 +33,18 @@ #include "py/objtype.h" #include "py/objstr.h" +const mp_obj_dict_t mp_const_empty_dict_obj = { + .base = { .type = &mp_type_dict }, + .map = { + .all_keys_are_qstrs = 0, + .is_fixed = 1, + .is_ordered = 1, + .used = 0, + .alloc = 0, + .table = NULL, + } +}; + STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); // This is a helper function to iterate through a dictionary. The state of From b137d064e9e0bfebd2a59a9b312935031252e742 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 4 Aug 2020 14:28:06 +1000 Subject: [PATCH 340/352] py/objtype: Handle __dict__ attribute when type has no locals. --- py/objtype.c | 9 ++++++--- tests/basics/class_dict.py | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index 40900dc050358..7f75232941046 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1014,13 +1014,16 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (attr == MP_QSTR___dict__) { // Returns a read-only dict of the class attributes. // If the internal locals is not fixed, a copy will be created. - mp_obj_dict_t *dict = self->locals_dict; + const mp_obj_dict_t *dict = self->locals_dict; + if (!dict) { + dict = &mp_const_empty_dict_obj; + } if (dict->map.is_fixed) { dest[0] = MP_OBJ_FROM_PTR(dict); } else { dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(dict)); - dict = MP_OBJ_TO_PTR(dest[0]); - dict->map.is_fixed = 1; + mp_obj_dict_t *dict_copy = MP_OBJ_TO_PTR(dest[0]); + dict_copy->map.is_fixed = 1; } return; } diff --git a/tests/basics/class_dict.py b/tests/basics/class_dict.py index f80ded678b0f8..508ae5e2e543e 100644 --- a/tests/basics/class_dict.py +++ b/tests/basics/class_dict.py @@ -17,3 +17,8 @@ class Foo: d = Foo.__dict__ print(d["a"], d["b"]) + + +# dict of a class that has no locals_dict (return empty dict). +d = type(type('')).__dict__ +print(d is not None) From 520bb88d70893287b2c2728259cd020ddc710eaf Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 15 Oct 2020 20:09:04 +1100 Subject: [PATCH 341/352] stm32/boards/NUCLEO_WB55/rfcore_firmware.py: Fix flash unlock. The flash can sometimes be in an already-unlocked state, and attempting to unlock it again will cause an immediate reset. So make _Flash.unlock() check FLASH_CR_LOCK to get the current state. Also fix some magic numbers for FLASH_CR_LOCK AND FLASH_CR_STRT. The machine.reset() could be removed because it no longer crashes now that the flash unlock is fixed. Signed-off-by: Jim Mussared --- .../boards/NUCLEO_WB55/rfcore_firmware.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py index 53c96a552a34c..de98d97430445 100644 --- a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py @@ -189,23 +189,31 @@ class _Flash: _FLASH_KEY1 = 0x45670123 _FLASH_KEY2 = 0xCDEF89AB + _FLASH_CR_STRT_MASK = 1 << 16 + _FLASH_CR_LOCK_MASK = 1 << 31 + _FLASH_SR_BSY_MASK = 1 << 16 + def wait_not_busy(self): - while machine.mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: + while machine.mem32[stm.FLASH + stm.FLASH_SR] & _Flash._FLASH_SR_BSY_MASK: machine.idle() def unlock(self): - machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY1 - machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY2 + if machine.mem32[stm.FLASH + stm.FLASH_CR] & _Flash._FLASH_CR_LOCK_MASK: + # Only unlock if already locked (i.e. FLASH_CR_LOCK is set). + machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY1 + machine.mem32[stm.FLASH + stm.FLASH_KEYR] = _Flash._FLASH_KEY2 + else: + log("Flash was already unlocked.") def lock(self): - machine.mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + machine.mem32[stm.FLASH + stm.FLASH_CR] = _Flash._FLASH_CR_LOCK_MASK def erase_page(self, page): assert 0 <= page <= 255 # 1MiB range (4k page) self.wait_not_busy() cr = page << 3 | 1 << 1 # PNB # PER machine.mem32[stm.FLASH + stm.FLASH_CR] = cr - machine.mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT + machine.mem32[stm.FLASH + stm.FLASH_CR] = cr | _Flash._FLASH_CR_STRT_MASK self.wait_not_busy() machine.mem32[stm.FLASH + stm.FLASH_CR] = 0 @@ -472,8 +480,6 @@ def resume(): elif status == 0: log("FUS update successful") _write_state(_STATE_CHECK_UPDATES) - # Need to force a reset after FUS install otherwise a subsequent flash copy will fail. - machine.reset() elif result == 0: # See below (for equivalent path for WS install -- we # sometimes see (255,0) right at the end). From dfb63b56134dc054b2e0023d9a404dea749d1fee Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 15 Oct 2020 20:11:43 +1100 Subject: [PATCH 342/352] stm32/boards/NUCLEO_WB55/rfcore_firmware.py: Fix bad variable name. Signed-off-by: Jim Mussared --- .../boards/NUCLEO_WB55/rfcore_firmware.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py index de98d97430445..3358b246d064c 100644 --- a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py @@ -370,18 +370,18 @@ def resume(): return _write_failure_state(REASON_RFCORE_NOT_CONFIGURED) while True: - _STATE_id = _read_state() + state = _read_state() - if _STATE_id == _STATE_IDLE: + if state == _STATE_IDLE: log("Firmware update complete") return 0 - elif _STATE_id == _STATE_FAILED: + elif state == _STATE_FAILED: log("Firmware update failed") return _read_failure_reason() # Keep calling GET_STATE until error or FUS. - elif _STATE_id == _STATE_WAITING_FOR_FUS: + elif state == _STATE_WAITING_FOR_FUS: log("Querying FUS state") status, result = fus_get_state() log("FUS state: {} {}", status, result) @@ -395,7 +395,7 @@ def resume(): _write_state(_STATE_CHECK_UPDATES) # Keep trying to start the WS until !fus_active() (or error). - elif _STATE_id == _STATE_WAITING_FOR_WS: + elif state == _STATE_WAITING_FOR_WS: if stm.rfcore_status() != _MAGIC_FUS_ACTIVE: log("WS active") _write_state(_STATE_IDLE) @@ -410,7 +410,7 @@ def resume(): _write_failure_state(REASON_NO_WS) # Sequence the FUS 1.0.2 -> FUS 1.1.0 -> WS (depending on what's available). - elif _STATE_id == _STATE_CHECK_UPDATES: + elif state == _STATE_CHECK_UPDATES: log("Checking for updates") fus_version = stm.rfcore_fw_version(_FW_VERSION_FUS) log("FUS version {}", fus_version) @@ -448,13 +448,13 @@ def resume(): # This shouldn't happen - the flash write should always complete and # move straight onto the COPIED state. Failure here indicates that # the rfcore is misconfigured or the WS firmware was not deleted first. - elif _STATE_id == _STATE_COPYING_FUS or _STATE_id == _STATE_COPYING_WS: + elif state == _STATE_COPYING_FUS or state == _STATE_COPYING_WS: log("Flash copy failed mid-write") _write_failure_state(REASON_FLASH_COPY_FAILED) # Flash write completed, we should immediately see GET_STATE return 0,0 # so we can start the FUS install. - elif _STATE_id == _STATE_COPIED_FUS: + elif state == _STATE_COPIED_FUS: if fus_is_idle(): log("FUS copy complete, installing") _write_state(_STATE_INSTALLING_FUS) @@ -466,7 +466,7 @@ def resume(): # Keep polling the state until we see a 0,0 (success) or non-transient # error. In general we should expect to see (16,0) several times, # followed by a (255,0), followed by (0, 0). - elif _STATE_id == _STATE_INSTALLING_FUS: + elif state == _STATE_INSTALLING_FUS: log("Installing FUS...") status, result = fus_get_state() log("FUS state: {} {}", status, result) @@ -492,7 +492,7 @@ def resume(): # Keep polling the state until we see 0,0 or failure (1,0). Any other # result means retry (but the docs say that 0 and 1 are the only # status values). - elif _STATE_id == _STATE_DELETING_WS: + elif state == _STATE_DELETING_WS: log("Deleting WS...") status, result = fus_get_state() log("FUS state: {} {}", status, result) @@ -508,7 +508,7 @@ def resume(): _write_failure_state(REASON_WS_DELETION_FAILED) # As for _STATE_COPIED_FUS above. We should immediately see 0,0. - elif _STATE_id == _STATE_COPIED_WS: + elif state == _STATE_COPIED_WS: if fus_is_idle(): log("WS copy complete, installing") _write_state(_STATE_INSTALLING_WS) @@ -518,7 +518,7 @@ def resume(): _write_failure_state(REASON_FLASH_WS_BAD_STATE) # As for _STATE_INSTALLING_FUS above. - elif _STATE_id == _STATE_INSTALLING_WS: + elif state == _STATE_INSTALLING_WS: log("Installing WS...") status, result = fus_get_state() log("FUS state: {} {}", status, result) From 893f75546c4e65ca5b72bc7ef9b91003372c4705 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 15 Oct 2020 20:35:11 +1100 Subject: [PATCH 343/352] stm32/boards/NUCLEO_WB55/rfcore_firmware.py: Increase GET_STATE timeout. When installing WS firmware, the very first GET_STATE can take several seconds to respond (especially with the larger binaries like BLE_stack_full). Allows stm.rfcore_sys_hci to take an optional timeout, defaulting to SYS_ACK_TIMEOUT_MS (which is 250ms). Signed-off-by: Jim Mussared --- .../boards/NUCLEO_WB55/rfcore_firmware.py | 18 +++++++---- ports/stm32/rfcore.c | 32 +++++++++++-------- ports/stm32/rfcore.h | 2 +- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py index 3358b246d064c..b5f1d0072e349 100644 --- a/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py +++ b/ports/stm32/boards/NUCLEO_WB55/rfcore_firmware.py @@ -180,6 +180,12 @@ _ADDR_FUS = 0x080EC000 _ADDR_WS_BLE_HCI = 0x080DC000 +# When installing the FUS/WS it can take a long time to return to the first +# GET_STATE HCI command. +# e.g. Installing stm32wb5x_BLE_Stack_full_fw.bin takes 3600ms to respond. +_INSTALLING_FUS_GET_STATE_TIMEOUT = const(1000) +_INSTALLING_WS_GET_STATE_TIMEOUT = const(6000) + def log(msg, *args, **kwargs): print("[rfcore update]", msg.format(*args, **kwargs)) @@ -272,10 +278,10 @@ def _parse_vendor_response(data): return (op >> 10, op & 0x3FF, data[6], data[7] if len(data) > 7 else 0) -def _run_sys_hci_cmd(ogf, ocf, buf=b""): +def _run_sys_hci_cmd(ogf, ocf, buf=b"", timeout=0): try: ogf_out, ocf_out, status, result = _parse_vendor_response( - stm.rfcore_sys_hci(ogf, ocf, buf) + stm.rfcore_sys_hci(ogf, ocf, buf, timeout) ) except OSError: # Timeout or FUS not active. @@ -285,8 +291,8 @@ def _run_sys_hci_cmd(ogf, ocf, buf=b""): return (status, result) -def fus_get_state(): - return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_GET_STATE) +def fus_get_state(timeout=0): + return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_GET_STATE, timeout=timeout) def fus_is_idle(): @@ -468,7 +474,7 @@ def resume(): # followed by a (255,0), followed by (0, 0). elif state == _STATE_INSTALLING_FUS: log("Installing FUS...") - status, result = fus_get_state() + status, result = fus_get_state(_INSTALLING_FUS_GET_STATE_TIMEOUT) log("FUS state: {} {}", status, result) if 0x20 <= status <= 0x2F and result == 0: # FUS_STATE_FUS_UPGRD_ONGOING @@ -520,7 +526,7 @@ def resume(): # As for _STATE_INSTALLING_FUS above. elif state == _STATE_INSTALLING_WS: log("Installing WS...") - status, result = fus_get_state() + status, result = fus_get_state(_INSTALLING_WS_GET_STATE_TIMEOUT) log("FUS state: {} {}", status, result) if 0x10 <= status <= 0x1F and result == 0: # FUS_STATE_FW_UPGRD_ONGOING diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 3850a17dae642..1fc0c9531d03b 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -68,9 +68,7 @@ #define HCI_EVENT_COMMAND_COMPLETE (0x0E) // #define HCI_EVENT_COMMAND_STATUS (0x0F) // -// There can be quite long delays during firmware update. -#define SYS_ACK_TIMEOUT_MS (1000) - +#define SYS_ACK_TIMEOUT_MS (250) #define BLE_ACK_TIMEOUT_MS (250) // AN5185 @@ -449,12 +447,14 @@ STATIC void tl_hci_cmd(uint8_t *cmd, unsigned int ch, uint8_t hdr, uint16_t opco LL_C1_IPCC_SetFlag_CHx(IPCC, ch); } -STATIC ssize_t tl_sys_wait_ack(const uint8_t *buf) { +STATIC ssize_t tl_sys_wait_ack(const uint8_t *buf, mp_int_t timeout_ms) { uint32_t t0 = mp_hal_ticks_ms(); + timeout_ms = MAX(SYS_ACK_TIMEOUT_MS, timeout_ms); + // C2 will clear this bit to acknowledge the request. while (LL_C1_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_SYS)) { - if (mp_hal_ticks_ms() - t0 > SYS_ACK_TIMEOUT_MS) { + if (mp_hal_ticks_ms() - t0 > timeout_ms) { printf("tl_sys_wait_ack: timeout\n"); return -MP_ETIMEDOUT; } @@ -465,9 +465,9 @@ STATIC ssize_t tl_sys_wait_ack(const uint8_t *buf) { return (ssize_t)tl_parse_hci_msg(buf, NULL); } -STATIC ssize_t tl_sys_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len) { +STATIC ssize_t tl_sys_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len, mp_int_t timeout_ms) { tl_hci_cmd(ipcc_membuf_sys_cmd_buf, IPCC_CH_SYS, 0x10, opcode, buf, len); - return tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf); + return tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf, timeout_ms); } STATIC int tl_ble_wait_resp(void) { @@ -559,7 +559,7 @@ void rfcore_ble_init(void) { tl_check_msg_ble(&ipcc_mem_ble_evt_queue, NULL); // Configure and reset the BLE controller - tl_sys_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_BLE_INIT), (const uint8_t *)&ble_init_params, sizeof(ble_init_params)); + tl_sys_hci_cmd_resp(HCI_OPCODE(OGF_VENDOR, OCF_BLE_INIT), (const uint8_t *)&ble_init_params, sizeof(ble_init_params), 0); tl_ble_hci_cmd_resp(HCI_OPCODE(0x03, 0x0003), NULL, 0); } @@ -675,20 +675,24 @@ STATIC mp_obj_t rfcore_fw_version(mp_obj_t fw_id_in) { } MP_DEFINE_CONST_FUN_OBJ_1(rfcore_fw_version_obj, rfcore_fw_version); -STATIC mp_obj_t rfcore_sys_hci(mp_obj_t ogf_in, mp_obj_t ocf_in, mp_obj_t cmd_in) { +STATIC mp_obj_t rfcore_sys_hci(size_t n_args, const mp_obj_t *args) { if (ipcc_mem_dev_info_tab.fus.table_state == MAGIC_IPCC_MEM_INCORRECT) { mp_raise_OSError(MP_EINVAL); } - mp_int_t ogf = mp_obj_get_int(ogf_in); - mp_int_t ocf = mp_obj_get_int(ocf_in); + mp_int_t ogf = mp_obj_get_int(args[0]); + mp_int_t ocf = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo = {0}; - mp_get_buffer_raise(cmd_in, &bufinfo, MP_BUFFER_READ); - ssize_t len = tl_sys_hci_cmd_resp(HCI_OPCODE(ogf, ocf), bufinfo.buf, bufinfo.len); + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + mp_int_t timeout_ms = 0; + if (n_args >= 4) { + timeout_ms = mp_obj_get_int(args[3]); + } + ssize_t len = tl_sys_hci_cmd_resp(HCI_OPCODE(ogf, ocf), bufinfo.buf, bufinfo.len, timeout_ms); if (len < 0) { mp_raise_OSError(-len); } return mp_obj_new_bytes(ipcc_membuf_sys_cmd_buf, len); } -MP_DEFINE_CONST_FUN_OBJ_3(rfcore_sys_hci_obj, rfcore_sys_hci); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_sys_hci_obj, 3, 4, rfcore_sys_hci); #endif // defined(STM32WB) diff --git a/ports/stm32/rfcore.h b/ports/stm32/rfcore.h index 39f6220a32d26..6a3c85f67dfe8 100644 --- a/ports/stm32/rfcore.h +++ b/ports/stm32/rfcore.h @@ -37,6 +37,6 @@ void rfcore_ble_set_txpower(uint8_t level); MP_DECLARE_CONST_FUN_OBJ_0(rfcore_status_obj); MP_DECLARE_CONST_FUN_OBJ_1(rfcore_fw_version_obj); -MP_DECLARE_CONST_FUN_OBJ_3(rfcore_sys_hci_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_sys_hci_obj); #endif // MICROPY_INCLUDED_STM32_RFCORE_H From 18518e26a7a92345fdcf8ad79e4c8b3a753f2d06 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Oct 2020 11:06:49 +1100 Subject: [PATCH 344/352] ports: Use correct in/out endpoint size in TUD_CDC_DESCRIPTOR. The last argument of TUD_CDC_DESCRIPTOR() is the endpoint size (or wMaxPacketSize), not the CDC RX buffer size (which can be larger than the endpoint size). Signed-off-by: Damien George --- ports/mimxrt/tusb_port.c | 3 ++- ports/nrf/drivers/usb/usb_descriptors.c | 3 ++- ports/samd/tusb_port.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index 70f8ef527cd0d..f6b09a362d18a 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -39,6 +39,7 @@ #define USBD_CDC_EP_OUT (0x02) #define USBD_CDC_EP_IN (0x82) #define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (512) #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) @@ -70,7 +71,7 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, CFG_TUD_CDC_RX_BUFSIZE), + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), }; static const char *const usbd_desc_str[] = { diff --git a/ports/nrf/drivers/usb/usb_descriptors.c b/ports/nrf/drivers/usb/usb_descriptors.c index 3704e5d0dd180..f6724c1bc078c 100644 --- a/ports/nrf/drivers/usb/usb_descriptors.c +++ b/ports/nrf/drivers/usb/usb_descriptors.c @@ -39,6 +39,7 @@ #define USBD_CDC_EP_OUT (0x02) #define USBD_CDC_EP_IN (0x82) #define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (64) #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) @@ -70,7 +71,7 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, CFG_TUD_CDC_RX_BUFSIZE), + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), }; static const char *const usbd_desc_str[] = { diff --git a/ports/samd/tusb_port.c b/ports/samd/tusb_port.c index 019e3a891c262..f3d417f1a182e 100644 --- a/ports/samd/tusb_port.c +++ b/ports/samd/tusb_port.c @@ -40,6 +40,7 @@ #define USBD_CDC_EP_OUT (0x02) #define USBD_CDC_EP_IN (0x82) #define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (64) #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) @@ -71,7 +72,7 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, CFG_TUD_CDC_RX_BUFSIZE), + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), }; static const char *const usbd_desc_str[] = { From 56e0932485af51ec175c8f43432eee67d657b334 Mon Sep 17 00:00:00 2001 From: awachtler Date: Tue, 6 Oct 2020 22:19:05 +0200 Subject: [PATCH 345/352] tools/upip.py: Support explicit port number in host. Adding a port number other then 443 to a PyPI URL may be needed if a local server like devpi is used. --- tools/upip.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/upip.py b/tools/upip.py index 0036eac25db7b..aa8aecedfc23f 100644 --- a/tools/upip.py +++ b/tools/upip.py @@ -129,7 +129,11 @@ def url_open(url): proto, _, host, urlpath = url.split("/", 3) try: - ai = usocket.getaddrinfo(host, 443, 0, usocket.SOCK_STREAM) + port = 443 + if ":" in host: + host, port = host.split(":") + port = int(port) + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) except OSError as e: fatal("Unable to resolve %s (no Internet?)" % host, e) # print("Address infos:", ai) @@ -147,7 +151,7 @@ def url_open(url): warn_ussl = False # MicroPython rawsocket module supports file interface directly - s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host)) + s.write("GET /%s HTTP/1.0\r\nHost: %s:%s\r\n\r\n" % (urlpath, host, port)) l = s.readline() protover, status, msg = l.split(None, 2) if status != b"200": From 3bc0ecbcd974716920b860904a47ee7f69afb717 Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Fri, 9 Oct 2020 20:40:17 +1100 Subject: [PATCH 346/352] docs/library/btree.rst: Correct method typo: __detitem__ to __delitem__. --- docs/library/btree.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/btree.rst b/docs/library/btree.rst index de52ef7e7e44e..ba910852102d1 100644 --- a/docs/library/btree.rst +++ b/docs/library/btree.rst @@ -118,7 +118,7 @@ Methods .. method:: btree.__getitem__(key) btree.get(key, default=None, /) btree.__setitem__(key, val) - btree.__detitem__(key) + btree.__delitem__(key) btree.__contains__(key) Standard dictionary methods. From 23f9439f441173dae961de4d2fe73986c166ff8f Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Sat, 10 Oct 2020 08:14:38 +1100 Subject: [PATCH 347/352] docs/library/machine.rst: Correct minor typo: timout to timeout. --- docs/library/machine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/machine.rst b/docs/library/machine.rst index b580353d6b564..18dc6f2afaa59 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -79,7 +79,7 @@ Power related functions If *time_ms* is specified then this will be the maximum time in milliseconds that the sleep will last for. Otherwise the sleep can last indefinitely. - With or without a timout, execution may resume at any time if there are events + With or without a timeout, execution may resume at any time if there are events that require processing. Such events, or wake sources, should be configured before sleeping, like `Pin` change or `RTC` timeout. From cf6845b1cf4680bb2eade175aaab00428bedf8ba Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Sat, 10 Oct 2020 08:18:24 +1100 Subject: [PATCH 348/352] docs/library/machine.Signal.rst: Correct typo: usecases to use cases. --- docs/library/machine.Signal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/machine.Signal.rst b/docs/library/machine.Signal.rst index 651c8c8497fd8..1e1fcb5483a86 100644 --- a/docs/library/machine.Signal.rst +++ b/docs/library/machine.Signal.rst @@ -55,7 +55,7 @@ Following is the guide when Signal vs Pin should be used: * Use Pin: If you implement a higher-level protocol or bus to communicate with more complex devices. -The split between Pin and Signal come from the usecases above and the +The split between Pin and Signal come from the use cases above and the architecture of MicroPython: Pin offers the lowest overhead, which may be important when bit-banging protocols. But Signal adds additional flexibility on top of Pin, at the cost of minor overhead (much smaller From 4842060366a88d8f50155538ce5fc3a12c8c709a Mon Sep 17 00:00:00 2001 From: Howard Lovatt Date: Sat, 10 Oct 2020 08:31:00 +1100 Subject: [PATCH 349/352] docs/library/machine.Timer.rst: Add mention of constructor arguments. --- docs/library/machine.Timer.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/library/machine.Timer.rst b/docs/library/machine.Timer.rst index 2d1287f325780..9991d3aebc2fc 100644 --- a/docs/library/machine.Timer.rst +++ b/docs/library/machine.Timer.rst @@ -31,6 +31,8 @@ Constructors Construct a new timer object of the given id. Id of -1 constructs a virtual timer (if supported by a board). + + See ``init`` for parameters of initialisation. Methods ------- From 32c99174e143b45d056c83a33f8de7502a82370c Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 10 Sep 2020 17:46:25 +1000 Subject: [PATCH 350/352] unix/mpconfigport.h: Enable MICROPY_PY_DELATTR_SETATTR. This is a generally useful feature and because it's part of the object model it cannot be added at runtime by some loadable Python code, so enable it on the standard unix build. --- ports/unix/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index c74d2fd84aa5f..17f4895573ed5 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -87,6 +87,7 @@ #define MICROPY_VFS_POSIX_FILE (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) +#define MICROPY_PY_DELATTR_SETATTR (1) #define MICROPY_PY_BUILTINS_STR_UNICODE (1) #define MICROPY_PY_BUILTINS_STR_CENTER (1) #define MICROPY_PY_BUILTINS_STR_PARTITION (1) From 97108fce5730f2342903e55d533ef2c30ebdfc13 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 21 Oct 2020 08:54:46 +1100 Subject: [PATCH 351/352] esp32/mpconfigport.h: Enable MICROPY_PY_DELATTR_SETATTR. To align with unix and stm32 ports. --- ports/esp32/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 5bf0676b238e3..b63d1f89558c0 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -63,6 +63,7 @@ // control over Python builtins #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) +#define MICROPY_PY_DELATTR_SETATTR (1) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) #define MICROPY_PY_BUILTINS_STR_UNICODE (1) #define MICROPY_PY_BUILTINS_STR_CENTER (1) From 396e5d4db8796be5d4df1224525540792baec41d Mon Sep 17 00:00:00 2001 From: arlucio Date: Wed, 21 Oct 2020 12:47:15 -0700 Subject: [PATCH 352/352] Add mbedtls support * Updated to the last version of Micropython * Reverted commit c846c69 that produced performance drop (also was generating bugs on make with mbedtls) * Added mbedtls related files as on other ports Additional info on PR --- ports/gprs_a9/Makefile | 19 ++++- ports/gprs_a9/main.c | 3 + ports/gprs_a9/mbedtls/mbedtls_config.h | 100 +++++++++++++++++++++++++ ports/gprs_a9/mbedtls/mbedtls_port.c | 96 ++++++++++++++++++++++++ ports/gprs_a9/mpconfigport.h | 11 ++- 5 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 ports/gprs_a9/mbedtls/mbedtls_config.h create mode 100644 ports/gprs_a9/mbedtls/mbedtls_port.c diff --git a/ports/gprs_a9/Makefile b/ports/gprs_a9/Makefile index 72110df05be70..06f8e49b87eea 100644 --- a/ports/gprs_a9/Makefile +++ b/ports/gprs_a9/Makefile @@ -28,15 +28,20 @@ QSTR_DEFS = qstrdefsport.h ############################################# # MicroPython feature configurations + MICROPY_PY_USSL = 1 -MICROPY_SSL_AXTLS = 1 -AXTLS_DEFS_EXTRA = -Dabort=abort_ -include time.h -DRT_MAX_PLAIN_LENGTH=1024 -DRT_EXTRA=4096 +MICROPY_SSL_MBEDTLS = 1 +# MICROPY_SSL_AXTLS = 1 +# AXTLS_DEFS_EXTRA = -Dabort=abort_ -include time.h -DRT_MAX_PLAIN_LENGTH=1024 -DRT_EXTRA=4096 FROZEN_MANIFEST ?= boards/manifest.py # include py core make definitions include $(TOP)/py/py.mk +# GIT_SUBMODULES = lib/mbedtls +GIT_SUBMODULES = lib/mbedtls + ############################################# CROSS_COMPILE = $(CSDTK_PATH)/bin/mips-elf- @@ -164,6 +169,16 @@ DRIVERS_SRC_C = $(addprefix drivers/,\ SRC_S = \ gchelper.s + +ifeq ($(MICROPY_SSL_MBEDTLS),1) +CFLAGS_MOD += -DMBEDTLS_CONFIG_FILE='"mbedtls/mbedtls_config.h"' +SRC_MOD += mbedtls/mbedtls_port.c +# replace mbedtls' error.c by ours +SRC_MOD := $(filter-out %/mbedtls/library/error.c, $(SRC_MOD)) +LIB_SRC_C += lib/mbedtls_errors/mp_mbedtls_errors.c +# LIB_SRC_C += lib/GPRS_C_SDK/include/api_inc/api +endif + OBJ_MP = OBJ_MP += $(PY_O) OBJ_MP += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/ports/gprs_a9/main.c b/ports/gprs_a9/main.c index bec8b8b79eb55..3d9f86ef9dc8b 100644 --- a/ports/gprs_a9/main.c +++ b/ports/gprs_a9/main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "py/compile.h" #include "py/runtime.h" @@ -59,6 +60,8 @@ #include "modgps.h" #include "modmachine.h" +#define SIZE_MAX std::numeric_limits::max() + #define AppMain_TASK_STACK_SIZE (2048 * 2) #define AppMain_TASK_PRIORITY 0 #define MICROPYTHON_TASK_STACK_SIZE (2048 * 4) diff --git a/ports/gprs_a9/mbedtls/mbedtls_config.h b/ports/gprs_a9/mbedtls/mbedtls_config.h new file mode 100644 index 0000000000000..eeed1a566a2c2 --- /dev/null +++ b/ports/gprs_a9/mbedtls/mbedtls_config.h @@ -0,0 +1,100 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 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_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H + +#define SIZE_MAX UINT32_MAX +// Set mbedtls configuration +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS +#define MBEDTLS_DEPRECATED_REMOVED +#define MBEDTLS_ENTROPY_HARDWARE_ALT +#define MBEDTLS_AES_ROM_TABLES +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SSL_PROTO_TLS1 +#define MBEDTLS_SSL_PROTO_TLS1_1 +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_SERVER_NAME_INDICATION + +// Use a smaller output buffer to reduce size of SSL context +#define MBEDTLS_SSL_MAX_CONTENT_LEN (16384) +#define MBEDTLS_SSL_IN_CONTENT_LEN (MBEDTLS_SSL_MAX_CONTENT_LEN) +#define MBEDTLS_SSL_OUT_CONTENT_LEN (4096) + +// Enable mbedtls modules +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +//#define MBEDTLS_ECP_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_ERROR_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS5_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_RSA_C +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_USE_C + +// Memory allocation hooks +#include +#include +void *m_calloc_mbedtls(size_t nmemb, size_t size); +void m_free_mbedtls(void *ptr); +#define MBEDTLS_PLATFORM_STD_CALLOC m_calloc_mbedtls +#define MBEDTLS_PLATFORM_STD_FREE m_free_mbedtls +#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf + +#include "mbedtls/check_config.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ \ No newline at end of file diff --git a/ports/gprs_a9/mbedtls/mbedtls_port.c b/ports/gprs_a9/mbedtls/mbedtls_port.c new file mode 100644 index 0000000000000..bc628b6fe75e8 --- /dev/null +++ b/ports/gprs_a9/mbedtls/mbedtls_port.c @@ -0,0 +1,96 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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" +#include "py/gc.h" +#include "rng.h" +#include "mbedtls_config.h" + +#define DEBUG (0) + +#if DEBUG +static size_t count_links(uint32_t *nb) { + void **p = MP_STATE_PORT(mbedtls_memory); + size_t n = 0; + *nb = 0; + while (p != NULL) { + ++n; + *nb += gc_nbytes(p); + p = (void**)p[1]; + } + return n; +} +#endif + +void *m_calloc_mbedtls(size_t nmemb, size_t size) { + void **ptr = m_malloc0(nmemb * size + 2 * sizeof(uintptr_t)); + #if DEBUG + uint32_t nb; + size_t n = count_links(&nb); + printf("mbed_alloc(%u, %u) -> (%u;%u) %p\n", nmemb, size, n, (uint)nb, ptr); + #endif + if (MP_STATE_PORT(mbedtls_memory) != NULL) { + MP_STATE_PORT(mbedtls_memory)[0] = ptr; + } + ptr[0] = NULL; + ptr[1] = MP_STATE_PORT(mbedtls_memory); + MP_STATE_PORT(mbedtls_memory) = ptr; + return &ptr[2]; +} + +void m_free_mbedtls(void *ptr_in) { + void **ptr = &((void**)ptr_in)[-2]; + #if DEBUG + uint32_t nb; + size_t n = count_links(&nb); + printf("mbed_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", ptr, ptr[0], ptr[1], gc_nbytes(ptr), n, (uint)nb); + #endif + if (ptr[1] != NULL) { + ((void**)ptr[1])[0] = ptr[0]; + } + if (ptr[0] != NULL) { + ((void**)ptr[0])[1] = ptr[1]; + } else { + MP_STATE_PORT(mbedtls_memory) = ptr[1]; + } + m_free(ptr); +} + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { + uint32_t val; + int n = 0; + *olen = len; + while (len--) { + if (!n) { + val = rng_get(); + n = 4; + } + *output++ = val; + val >>= 8; + --n; + } + return 0; +} \ No newline at end of file diff --git a/ports/gprs_a9/mpconfigport.h b/ports/gprs_a9/mpconfigport.h index d3d0cbb24b324..c0b2e7f59d2bb 100644 --- a/ports/gprs_a9/mpconfigport.h +++ b/ports/gprs_a9/mpconfigport.h @@ -8,8 +8,6 @@ #include #include -#include "api_sys.h" - // options to control how MicroPython is built @@ -65,8 +63,6 @@ #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_COMP_CONST (1) -#define MICROPY_BEGIN_ATOMIC_SECTION() SYS_EnterCriticalSection() -#define MICROPY_END_ATOMIC_SECTION(state) SYS_ExitCriticalSection(state) // MCU definition #define MP_ENDIANNESS_LITTLE (1) @@ -247,8 +243,15 @@ typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM +#if MICROPY_SSL_MBEDTLS +#define MICROPY_PORT_ROOT_POINTER_MBEDTLS void **mbedtls_memory; +#else +#define MICROPY_PORT_ROOT_POINTER_MBEDTLS +#endif + #define MICROPY_PORT_ROOT_POINTERS \ const char *readline_hist[8]; \ + MICROPY_PORT_ROOT_POINTER_MBEDTLS \ byte *uart_rxbuf[2]; #include "csdk_config.h"