diff --git a/docs/library/stm.rst b/docs/library/stm.rst index a181d6044cc16..3103aa297b0e6 100644 --- a/docs/library/stm.rst +++ b/docs/library/stm.rst @@ -102,3 +102,14 @@ the second CPU, the RF core. Execute a HCI command on the SYS channel. The execution is synchronous. Returns a bytes object with the result of the SYS command. + +.. function:: rfcore_ble_hci(command[, response_buf]) + + Execute a HCI command on the BLE channel. The execution is synchronous. + + Takes a *command* byte/bytearray with pre-formatted HCI packet. + + Optionally takes a pre-allocated bytearray buffer for the response packet. + + Returns response length if *response_buf* is provided, else a bytes object with the + response HCI packet. diff --git a/examples/natmod/stm32wb55_transparent_vcp/LICENCE_ST.md b/examples/natmod/stm32wb55_transparent_vcp/LICENCE_ST.md new file mode 100644 index 0000000000000..9c720b23e5705 --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/LICENCE_ST.md @@ -0,0 +1,83 @@ +> Originally from https://github.com/STMicroelectronics/STM32CubeWB/blob/83aacadecbd5136ad3194f39e002ff50a5439ad9/Projects/P-NUCLEO-WB55.USBDongle/Applications/LICENSE.md +> Applies only to stm32wb55_local_commands.c + +SLA0044 Rev5/February 2018 + +## Software license agreement + +### **ULTIMATE LIBERTY SOFTWARE LICENSE AGREEMENT** + +BY INSTALLING, COPYING, DOWNLOADING, ACCESSING OR OTHERWISE USING THIS SOFTWARE +OR ANY PART THEREOF (AND THE RELATED DOCUMENTATION) FROM STMICROELECTRONICS +INTERNATIONAL N.V, SWISS BRANCH AND/OR ITS AFFILIATED COMPANIES +(STMICROELECTRONICS), THE RECIPIENT, ON BEHALF OF HIMSELF OR HERSELF, OR ON +BEHALF OF ANY ENTITY BY WHICH SUCH RECIPIENT IS EMPLOYED AND/OR ENGAGED AGREES +TO BE BOUND BY THIS SOFTWARE LICENSE AGREEMENT. + +Under STMicroelectronics’ intellectual property rights, the redistribution, +reproduction and use in source and binary forms of the software or any part +thereof, with or without modification, are permitted provided that the following +conditions are met: + +1. Redistribution of source code (modified or not) must retain any copyright + notice, this list of conditions and the disclaimer set forth below as items 10 + and 11. + +2. Redistributions in binary form, except as embedded into microcontroller or + microprocessor device manufactured by or for STMicroelectronics or a software + update for such device, must reproduce any copyright notice provided with the + binary code, this list of conditions, and the disclaimer set forth below as + items 10 and 11, in documentation and/or other materials provided with the + distribution. + +3. Neither the name of STMicroelectronics nor the names of other contributors to + this software may be used to endorse or promote products derived from this + software or part thereof without specific written permission. + +4. This software or any part thereof, including modifications and/or derivative + works of this software, must be used and execute solely and exclusively on or in + combination with a microcontroller or microprocessor device manufactured by or + for STMicroelectronics. + +5. No use, reproduction or redistribution of this software partially or totally + may be done in any manner that would subject this software to any Open Source + Terms. “Open Source Terms” shall mean any open source license which requires as + part of distribution of software that the source code of such software is + distributed therewith or otherwise made available, or open source license that + substantially complies with the Open Source definition specified at + www.opensource.org and any other comparable open source license such as for + example GNU General Public License (GPL), Eclipse Public License (EPL), Apache + Software License, BSD license or MIT license. + +6. STMicroelectronics has no obligation to provide any maintenance, support or + updates for the software. + +7. The software is and will remain the exclusive property of STMicroelectronics + and its licensors. The recipient will not take any action that jeopardizes + STMicroelectronics and its licensors' proprietary rights or acquire any rights + in the software, except the limited rights specified hereunder. + +8. The recipient shall comply with all applicable laws and regulations affecting + the use of the software or any part thereof including any applicable export + control law or regulation. + +9. Redistribution and use of this software or any part thereof other than as + permitted under this license is void and will automatically terminate your + rights under this license. + +10. THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS, WHICH ARE + DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT SHALL + STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +11. EXCEPT AS EXPRESSLY PERMITTED HEREUNDER, NO LICENSE OR OTHER RIGHTS, WHETHER + EXPRESS OR IMPLIED, ARE GRANTED UNDER ANY PATENT OR OTHER INTELLECTUAL PROPERTY + RIGHTS OF STMICROELECTRONICS OR ANY THIRD PARTY. diff --git a/examples/natmod/stm32wb55_transparent_vcp/Makefile b/examples/natmod/stm32wb55_transparent_vcp/Makefile new file mode 100644 index 0000000000000..2fce3c4de474b --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/Makefile @@ -0,0 +1,34 @@ +# This module is intended for use on stm32wb55 microcontrollers +# and shows how a native module can be used to extend hardware +# functionality. + +# Note: `pip install pyelftools` is required to build native modules. + +# Location of top-level MicroPython directory +MPY_DIR = ../../.. + +# Name of module +MOD = rfcore_transparent + +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +ARCH = armv7m + +# Source files (.c or .py) +SRC = \ + stm32wb55_transparent.c \ + stm32wb55_local_commands.c \ + rfcore_transparent.py + +MCU_SERIES = wb +CMSIS_MCU = STM32WB55xx + +CFLAGS += -Os +CFLAGS += -D$(CMSIS_MCU) -DUSE_FULL_LL_DRIVER $(CFLAGS_MCU_$(MCU_SERIES)) + +CFLAGS += -I$(MPY_DIR)/ports/stm32 +CFLAGS += -I$(MPY_DIR)/ports/stm32/boards/NUCLEO_WB55 +CFLAGS += -I$(MPY_DIR)/lib/cmsis/inc +CFLAGS += -I$(MPY_DIR)/lib/stm32lib/STM32WBxx_HAL_Driver/Inc +CFLAGS += -I$(MPY_DIR)/lib/stm32lib/CMSIS/STM32WBxx/Include + +include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/stm32wb55_transparent_vcp/Readme.md b/examples/natmod/stm32wb55_transparent_vcp/Readme.md new file mode 100644 index 0000000000000..bfd5c404baf2e --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/Readme.md @@ -0,0 +1,50 @@ +# STM32WB55 BLE HCI Transparent Mode + +This module allows the usage of a stm32wb55 board (eg. nucleo or dongle) as a USB/UART Bluetooth HCI Dongle. + +This allows it to be used to provide bluetooth functionality with the unix micropython port. + +It also has full support for use with STM32CubeMonitor-Rf app. + +The native module can be compiled and deployed with: + +``` +cd examples/natmod/stm32wb55_transparent_vcp +make +mpremote cp rfcore_transparent.mpy : +``` + +Minimal usage: + +``` +import rfcore_transparent +rfcore_transparent.start() +``` + +By default stdio (repl) will be used / taken over by this +transparent mode. + +Example `main.py` + +``` +import os +from pyb import Pin, LED + +sw = Pin("SW3", Pin.IN, Pin.PULL_UP) + +def activity(status): + if status: + LED(3).on() + else: + LED(3).off() + +if sw.value(): + LED(2).on() + import rfcore_transparent + + # Disconnect USB VCP from repl to use here + usb = os.dupterm(None, 1) # startup default is usb (repl) on slot 1 + + rfcore_transparent.start(usb, activity) + +``` diff --git a/examples/natmod/stm32wb55_transparent_vcp/rfcore_transparent.py b/examples/natmod/stm32wb55_transparent_vcp/rfcore_transparent.py new file mode 100644 index 0000000000000..5e6231010aa62 --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/rfcore_transparent.py @@ -0,0 +1,25 @@ +# This will be built into the native mpy module. +# Functions here will be available in the rfcore_transparent module along with +# the native C functions. + + +def start(stream=None, callback=None): + import sys + import micropython + from bluetooth import BLE + + # Ensure rfcore has been started at least once, then turn off bluetooth. + BLE().active(1) + BLE().active(0) + + in_stream = out_stream = stream + + if not in_stream: + # Disable the ctrl-c interrupt when using repl stream. + micropython.kbd_intr(-1) + + in_stream = sys.stdin + out_stream = sys.stdout + + # Start transparant mode C function (never exits). + _rfcore_transparent_start(in_stream, out_stream, callback) diff --git a/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.c b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.c new file mode 100644 index 0000000000000..80ccd42982ab3 --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.c @@ -0,0 +1,475 @@ +// STM32WB55 BLE_TransparentModeVCP Local Commands +// Based on functionality from: +// https://github.com/STMicroelectronics/STM32CubeWB/tree/83aacadecbd5136ad3194f39e002ff50a5439ad9/Projects/P-NUCLEO-WB55.USBDongle/Applications/BLE/BLE_TransparentModeVCP +// +// > Copyright (c) 2019-2021 STMicroelectronics. +// > All rights reserved. +// > +// > This software is licensed under terms that can be found in the LICENSE file +// > in the root directory of this software component. +// > If no LICENSE file comes with this software, it is provided AS-IS. +// +// Note: The LICENCE file referred to is copied here as LICENCE_ST.md + +#include +#include "stm32wbxx_ll_utils.h" +#include "stm32wbxx_ll_system.h" + +#include "stm32wb55_local_commands.h" + +#define FW_Version 1 + +#ifndef STATIC +#define STATIC static +#endif + +#define PACKED_STRUCT struct __attribute__((packed)) + +/** + * 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 sector) + * [8:15] = Reserved ( Shall be set to 0 - may be used as flash extension ) + * [16:23] = SRAM2b ( Number of 1k sector) + * [24:31] = SRAM2a ( Number of 1k sector) + */ +typedef PACKED_STRUCT { + uint32_t Version; +} +MB_SafeBootInfoTable_t; + +typedef PACKED_STRUCT { + uint32_t Version; + uint32_t MemorySize; + uint32_t FusInfo; +} +MB_FusInfoTable_t; + +typedef PACKED_STRUCT { + uint32_t Version; + uint32_t MemorySize; + uint32_t InfoStack; + uint32_t Reserved; +} +MB_WirelessFwInfoTable_t; + +typedef struct { + MB_SafeBootInfoTable_t SafeBootInfoTable; + MB_FusInfoTable_t FusInfoTable; + MB_WirelessFwInfoTable_t WirelessFwInfoTable; +} MB_DeviceInfoTable_t; + +typedef struct { + uint8_t *pcmd_buffer; + uint8_t *pcs_buffer; + uint8_t *pevt_queue; + uint8_t *phci_acl_data_buffer; +} MB_BleTable_t; + +typedef struct { + uint8_t *notack_buffer; + uint8_t *clicmdrsp_buffer; + uint8_t *otcmdrsp_buffer; + uint8_t *clinot_buffer; +} MB_ThreadTable_t; + +typedef struct { + uint8_t *clicmdrsp_buffer; + uint8_t *m0cmd_buffer; +} MB_LldTestsTable_t; + +typedef struct { + uint8_t *cmdrsp_buffer; + uint8_t *m0cmd_buffer; +} MB_BleLldTable_t; + +typedef struct { + uint8_t *notifM0toM4_buffer; + uint8_t *appliCmdM4toM0_buffer; + uint8_t *requestM0toM4_buffer; +} MB_ZigbeeTable_t; +/** + * msg + * [0:7] = cmd/evt + * [8:31] = Reserved + */ +typedef struct { + uint8_t *pcmd_buffer; + uint8_t *sys_queue; +} MB_SysTable_t; + +typedef struct { + uint8_t *spare_ble_buffer; + uint8_t *spare_sys_buffer; + uint8_t *blepool; + uint32_t blepoolsize; + uint8_t *pevt_free_buffer_queue; + uint8_t *traces_evt_pool; + uint32_t tracespoolsize; +} MB_MemManagerTable_t; + +typedef struct { + uint8_t *traces_queue; +} MB_TracesTable_t; + +typedef struct { + uint8_t *p_cmdrsp_buffer; + uint8_t *p_notack_buffer; + uint8_t *evt_queue; +} MB_Mac_802_15_4_t; + +typedef struct { + MB_DeviceInfoTable_t *p_device_info_table; + MB_BleTable_t *p_ble_table; + MB_ThreadTable_t *p_thread_table; + MB_SysTable_t *p_sys_table; + MB_MemManagerTable_t *p_mem_manager_table; + MB_TracesTable_t *p_traces_table; + MB_Mac_802_15_4_t *p_mac_802_15_4_table; + MB_ZigbeeTable_t *p_zigbee_table; + MB_LldTestsTable_t *p_lld_tests_table; + MB_BleLldTable_t *p_ble_lld_table; +} MB_RefTable_t; + +typedef enum { + LHCI_8bits = 1, + LHCI_16bits = 2, + LHCI_32bits = 4, +} LHCI_Busw_t; + +#define LHCI_OGF (0x3F) +#define LHCI_OCF_BASE (0x160) + +#define LHCI_OCF_C1_WRITE_REG (LHCI_OCF_BASE + 0) +#define LHCI_OPCODE_C1_WRITE_REG ((LHCI_OGF << 10) + LHCI_OCF_C1_WRITE_REG) +typedef PACKED_STRUCT { + LHCI_Busw_t busw; + uint32_t mask; + uint32_t add; + uint32_t val; +} +LHCI_C1_Write_Register_cmd_t; + +#define LHCI_OCF_C1_READ_REG (LHCI_OCF_BASE + 1) +#define LHCI_OPCODE_C1_READ_REG ((LHCI_OGF << 10) + LHCI_OCF_C1_READ_REG) +typedef PACKED_STRUCT { + LHCI_Busw_t busw; + uint32_t add; +} +LHCI_C1_Read_Register_cmd_t; + +typedef PACKED_STRUCT { + uint8_t status; + uint32_t val; +} +LHCI_C1_Read_Register_ccrp_t; + +#define LHCI_OCF_C1_DEVICE_INF (LHCI_OCF_BASE + 2) +#define LHCI_OPCODE_C1_DEVICE_INF ((LHCI_OGF << 10) + LHCI_OCF_C1_DEVICE_INF) +typedef PACKED_STRUCT { + uint8_t status; + uint16_t rev_id; /* from DBGMCU_ICODE */ + uint16_t dev_code_id; /* from DBGMCU_ICODE */ + uint8_t package_type; /* from package data register */ + uint8_t device_type_id; /* from FLASH UID64 */ + uint32_t st_company_id; /* from FLASH UID64 */ + uint32_t uid64; /* from FLASH UID64 */ + uint32_t uid96_0; /* from Unique device ID register */ + uint32_t uid96_1; /* from Unique device ID register */ + uint32_t uid96_2; /* from Unique device ID register */ + MB_SafeBootInfoTable_t SafeBootInf; + MB_FusInfoTable_t FusInf; + MB_WirelessFwInfoTable_t WirelessFwInf; + uint32_t AppFwInf; +} +LHCI_C1_Device_Information_ccrp_t; + +#define TL_BLECMD_PKT_TYPE (0x01) +#define TL_ACL_DATA_PKT_TYPE (0x02) +#define TL_BLEEVT_PKT_TYPE (0x04) +#define TL_OTCMD_PKT_TYPE (0x08) +#define TL_OTRSP_PKT_TYPE (0x09) +#define TL_CLICMD_PKT_TYPE (0x0A) +#define TL_OTNOT_PKT_TYPE (0x0C) +#define TL_OTACK_PKT_TYPE (0x0D) +#define TL_CLINOT_PKT_TYPE (0x0E) +#define TL_CLIACK_PKT_TYPE (0x0F) +#define TL_SYSCMD_PKT_TYPE (0x10) +#define TL_SYSRSP_PKT_TYPE (0x11) +#define TL_SYSEVT_PKT_TYPE (0x12) +#define TL_CLIRESP_PKT_TYPE (0x15) +#define TL_M0CMD_PKT_TYPE (0x16) +#define TL_LOCCMD_PKT_TYPE (0x20) +#define TL_LOCRSP_PKT_TYPE (0x21) +#define TL_TRACES_APP_PKT_TYPE (0x40) +#define TL_TRACES_WL_PKT_TYPE (0x41) + +#define TL_CMD_HDR_SIZE (4) +#define TL_EVT_HDR_SIZE (3) +#define TL_EVT_CS_PAYLOAD_SIZE (4) + +#define TL_BLEEVT_CC_OPCODE (0x0E) +#define TL_BLEEVT_CS_OPCODE (0x0F) +#define TL_BLEEVT_VS_OPCODE (0xFF) + + +typedef PACKED_STRUCT { + uint16_t cmdcode; + uint8_t plen; + uint8_t payload[255]; +} +TL_Cmd_t; + +typedef PACKED_STRUCT { + uint8_t type; + TL_Cmd_t cmd; +} +TL_CmdPacket_t; + +typedef PACKED_STRUCT { + uint8_t numcmd; + uint16_t cmdcode; + uint8_t payload[1]; +} +TL_CcEvt_t; + +typedef PACKED_STRUCT { + uint8_t evtcode; + uint8_t plen; + uint8_t payload[1]; +} +TL_Evt_t; + +typedef PACKED_STRUCT { + uint8_t type; + TL_Evt_t evt; +} +TL_EvtSerial_t; + +// CPU1 (local) interaction functions + +STATIC void LHCI_C1_Write_Register(TL_CmdPacket_t *pcmd) { + LHCI_C1_Write_Register_cmd_t *p_param; + uint32_t primask_bit; + + primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ + + p_param = (LHCI_C1_Write_Register_cmd_t *)pcmd->cmd.payload; + + switch (p_param->busw) { + case LHCI_8bits: + __disable_irq(); + + *(uint8_t *)(p_param->add) = ((*(uint8_t *)(p_param->add)) & (~(p_param->mask))) | (p_param->val & p_param->mask); + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ + break; + + case LHCI_16bits: + __disable_irq(); + + *(uint16_t *)(p_param->add) = ((*(uint16_t *)(p_param->add)) & (~(p_param->mask))) | (p_param->val & p_param->mask); + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ + break; + + default: /**< case SHCI_32BITS */ + __disable_irq(); + + *(uint32_t *)(p_param->add) = ((*(uint32_t *)(p_param->add)) & (~(p_param->mask))) | (p_param->val & p_param->mask); + + __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ + break; + } + + ((TL_EvtSerial_t *)pcmd)->type = TL_LOCRSP_PKT_TYPE; + ((TL_EvtSerial_t *)pcmd)->evt.evtcode = TL_BLEEVT_CC_OPCODE; + ((TL_EvtSerial_t *)pcmd)->evt.plen = TL_EVT_CS_PAYLOAD_SIZE; + ; + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->cmdcode = LHCI_OPCODE_C1_WRITE_REG; + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload[0] = 0x00; + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->numcmd = 1; + + return; +} + +STATIC void LHCI_C1_Read_Register(TL_CmdPacket_t *pcmd) { + LHCI_C1_Read_Register_cmd_t *p_param; + uint32_t rsp_val; + uint8_t busw; + + p_param = (LHCI_C1_Read_Register_cmd_t *)pcmd->cmd.payload; + busw = p_param->busw; + + switch (busw) { + case LHCI_8bits: + rsp_val = *(uint8_t *)(p_param->add); + break; + + case LHCI_16bits: + rsp_val = *(uint16_t *)(p_param->add); + break; + + default: /**< case SHCI_32BITS */ + rsp_val = *(uint32_t *)(p_param->add); + break; + } + + ((TL_EvtSerial_t *)pcmd)->type = TL_LOCRSP_PKT_TYPE; + ((TL_EvtSerial_t *)pcmd)->evt.evtcode = TL_BLEEVT_CC_OPCODE; + ((TL_EvtSerial_t *)pcmd)->evt.plen = TL_EVT_HDR_SIZE + sizeof(LHCI_C1_Read_Register_ccrp_t); + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->cmdcode = LHCI_OPCODE_C1_READ_REG; + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->numcmd = 1; + ((LHCI_C1_Read_Register_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->status = + 0x00; + + memcpy( + (void *)&(((LHCI_C1_Read_Register_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->val), + &rsp_val, + 4); + + return; +} + +STATIC void LHCI_C1_Read_Device_Information(TL_CmdPacket_t *pcmd) { + uint32_t ipccdba; + MB_RefTable_t *p_ref_table; + + ipccdba = READ_BIT(FLASH->IPCCBR, FLASH_IPCCBR_IPCCDBA); + p_ref_table = (MB_RefTable_t *)((ipccdba << 2) + SRAM2A_BASE); + + ((TL_EvtSerial_t *)pcmd)->type = TL_LOCRSP_PKT_TYPE; + ((TL_EvtSerial_t *)pcmd)->evt.evtcode = TL_BLEEVT_CC_OPCODE; + ((TL_EvtSerial_t *)pcmd)->evt.plen = TL_EVT_HDR_SIZE + sizeof(LHCI_C1_Device_Information_ccrp_t); + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->cmdcode = LHCI_OPCODE_C1_DEVICE_INF; + ((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->numcmd = 1; + + /** + * status + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->status = + 0x00; + + /** + * revision id + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->rev_id = + (uint16_t)LL_DBGMCU_GetRevisionID(); + + /** + * device code id + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->dev_code_id = + (uint16_t)LL_DBGMCU_GetDeviceID(); + + /** + * package type + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->package_type = + (uint8_t)LL_GetPackageType(); + + /** + * device type id + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->device_type_id = + (uint8_t)LL_FLASH_GetDeviceID(); + + /** + * st company id + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->st_company_id = + LL_FLASH_GetSTCompanyID(); + + /** + * UID64 + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->uid64 = + LL_FLASH_GetUDN(); + + /** + * UID96_0 + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->uid96_0 = + LL_GetUID_Word0(); + + /** + * UID96_1 + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->uid96_1 = + LL_GetUID_Word1(); + + /** + * UID96_2 + */ + ((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->uid96_2 = + LL_GetUID_Word2(); + + /** + * SafeBootInf + */ + memcpy( + &(((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->SafeBootInf), + &(p_ref_table->p_device_info_table->SafeBootInfoTable), + sizeof(MB_SafeBootInfoTable_t)); + + /** + * FusInf + */ + memcpy( + &(((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->FusInf), + &(p_ref_table->p_device_info_table->FusInfoTable), + sizeof(MB_FusInfoTable_t)); + + /** + * WirelessFwInf + */ + memcpy( + &(((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->WirelessFwInf), + &(p_ref_table->p_device_info_table->WirelessFwInfoTable), + sizeof(MB_WirelessFwInfoTable_t)); + + /** + * AppFwInf + */ + (((LHCI_C1_Device_Information_ccrp_t *)(((TL_CcEvt_t *)(((TL_EvtSerial_t *)pcmd)->evt.payload))->payload))->AppFwInf) = + FW_Version; + + return; +} + +size_t local_hci_cmd(size_t len, const uint8_t *buffer) { + + TL_CmdPacket_t *SysLocalCmd = (TL_CmdPacket_t *)buffer; + TL_EvtSerial_t *SysLocalRsp = (TL_EvtSerial_t *)buffer; + + switch (SysLocalCmd->cmd.cmdcode) { + case LHCI_OPCODE_C1_WRITE_REG: + LHCI_C1_Write_Register(SysLocalCmd); + break; + + case LHCI_OPCODE_C1_READ_REG: + LHCI_C1_Read_Register(SysLocalCmd); + break; + + case LHCI_OPCODE_C1_DEVICE_INF: + LHCI_C1_Read_Device_Information(SysLocalCmd); + break; + + default: + ((TL_CcEvt_t *)(SysLocalRsp->evt.payload))->cmdcode = SysLocalCmd->cmd.cmdcode; + ((TL_CcEvt_t *)(SysLocalRsp->evt.payload))->payload[0] = 0x01; + ((TL_CcEvt_t *)(SysLocalRsp->evt.payload))->numcmd = 1; + SysLocalRsp->type = TL_LOCRSP_PKT_TYPE; + SysLocalRsp->evt.evtcode = TL_BLEEVT_CC_OPCODE; + SysLocalRsp->evt.plen = TL_EVT_CS_PAYLOAD_SIZE; + + break; + } + return SysLocalRsp->evt.plen + TL_EVT_HDR_SIZE; +} diff --git a/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.h b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.h new file mode 100644 index 0000000000000..f89f5361f701e --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_local_commands.h @@ -0,0 +1,3 @@ +#include +#include +size_t local_hci_cmd(size_t len, const uint8_t *buffer); diff --git a/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_transparent.c b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_transparent.c new file mode 100644 index 0000000000000..b7947e7cff616 --- /dev/null +++ b/examples/natmod/stm32wb55_transparent_vcp/stm32wb55_transparent.c @@ -0,0 +1,177 @@ +#include "py/dynruntime.h" +#include "py/binary.h" +#include "py/objarray.h" + +#include "stm32wb55_local_commands.h" + +// Don't enable this if stdio is used for transp comms. +#define DEBUG_printf(...) // mp_printf(&mp_plat_print, "rfcore_transp: " __VA_ARGS__) + +#define STATE_IDLE 0 +#define STATE_NEED_LEN 1 +#define STATE_IN_PAYLOAD 2 + +#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_KIND_LOCAL_CMD (0x20) // Used by STM32CubeMonitor to query the device +#define HCI_KIND_LOCAL_RSP (0x21) + + +// Callback python function, can be used to provide feedback on each comm, eg. blink LED. +void mpy_run_callback(mp_obj_t callback, bool on) { + if (callback != mp_const_none) { + mp_call_function_n_kw(callback, 1, 0, (mp_obj_t[]) {(on) ? mp_const_true : mp_const_false}); + } +} + + +mp_obj_t mp_stream_write(mp_obj_t stream_out, const void *buf, size_t len, byte flags) { + int errcode; + const mp_stream_p_t *stream_out_p = ((mp_obj_base_t *)stream_out)->type->protocol; + mp_uint_t out_sz = stream_out_p->write(stream_out, buf, len, &errcode); + if (out_sz == MP_STREAM_ERROR) { + return mp_const_false; + } + return mp_const_none; +} + +mp_uint_t mp_stream_read(mp_obj_t stream_in, void *buf, size_t len) { + int errcode; + const mp_stream_p_t *stream_in_p = ((mp_obj_base_t *)stream_in)->type->protocol; + mp_uint_t out_sz = stream_in_p->read(stream_in, buf, len, &errcode); + if (out_sz == MP_STREAM_ERROR) { + return -1; + } + return out_sz; +} + +mp_uint_t mp_stream_poll(mp_obj_t stream_in, uintptr_t poll_flags) { + int errcode = 0; + mp_uint_t ret = 0; + const mp_stream_p_t *stream_in_p = ((mp_obj_base_t *)stream_in)->type->protocol; + ret = stream_in_p->ioctl(stream_in, MP_STREAM_POLL, poll_flags, &errcode); + return ret; +} + + +STATIC mp_obj_t rfcore_transparent(mp_obj_t stream_in, mp_obj_t stream_out, mp_obj_t callback) { + // Make sure we have suitable stream objects. + mp_get_stream_raise(stream_in, MP_STREAM_OP_READ | MP_STREAM_OP_IOCTL); + mp_get_stream_raise(stream_out, MP_STREAM_OP_WRITE); + + mp_obj_t stm_module = mp_import_name(MP_QSTR_stm, mp_const_none, MP_OBJ_NEW_SMALL_INT(0)); + mp_obj_t rfcore_ble_hci_obj = mp_import_from(stm_module, MP_QSTR_rfcore_ble_hci); + + mp_obj_t time_module = mp_import_name(MP_QSTR_time, mp_const_none, MP_OBJ_NEW_SMALL_INT(0)); + mp_obj_t sleep_ms_obj = mp_import_from(time_module, MP_QSTR_sleep_ms); + + uint8_t buf[1024]; + size_t rx = 0; + size_t len = 0; + int state = 0; + int cmd_type = 0; + + while (true) { + // Sleep briefly to allow micropython background processing. + mp_call_function_n_kw(sleep_ms_obj, 1, 0, (mp_obj_t[]) {MP_OBJ_NEW_SMALL_INT(1)}); + + if (state == STATE_IN_PAYLOAD && len == 0) { + size_t rsp_len = 0; + + if (cmd_type == HCI_KIND_LOCAL_CMD) { + // Process the command directly (cpu1). + DEBUG_printf("local_hci_cmd\n"); + rsp_len = local_hci_cmd(rx, buf); + DEBUG_printf("rsp: len 0x%x\n", rsp_len); + + } else { + // Forward command to rfcore (cpu2). + DEBUG_printf("rfcore_ble_hci_cmd\n"); + mp_obj_array_t cmd = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, rx, buf}; + mp_obj_array_t rsp = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, sizeof(buf), buf}; + + mp_obj_t args[] = {MP_OBJ_FROM_PTR(&cmd), MP_OBJ_FROM_PTR(&rsp)}; + mp_obj_t rsp_len_o = mp_call_function_n_kw(rfcore_ble_hci_obj, 2, 0, args); + rsp_len = mp_obj_get_int(rsp_len_o); + } + + if (rsp_len > 0) { + DEBUG_printf("rsp: len 0x%x\n", rsp_len); + mp_stream_write(stream_out, (const char *)buf, rsp_len, MP_STREAM_RW_WRITE); + mpy_run_callback(callback, false); + } else { + DEBUG_printf("rsp: None\n"); + } + + rx = 0; + len = 0; + state = STATE_IDLE; + + } else if (mp_stream_poll(stream_in, MP_STREAM_POLL_RD) & MP_STREAM_POLL_RD) { + uint8_t c; + int32_t ret = mp_stream_read(stream_in, &c, 1); + if (ret < 1) { + continue; + } + mpy_run_callback(callback, true); + + if (state == STATE_IDLE) { + if (c == HCI_KIND_BT_CMD || c == HCI_KIND_BT_ACL || c == HCI_KIND_BT_EVENT || c == HCI_KIND_VENDOR_RESPONSE || c == HCI_KIND_VENDOR_EVENT || HCI_KIND_LOCAL_CMD) { + cmd_type = c; + state = STATE_NEED_LEN; + buf[rx++] = c; + len = 0; + DEBUG_printf("cmd_type 0x%x\n", c); + } else { + DEBUG_printf("cmd_type unknown 0x%x\n", c); + } + } else if (state == STATE_NEED_LEN) { + buf[rx++] = c; + if (cmd_type == HCI_KIND_BT_ACL && rx == 4) { + len = c; + } + if (cmd_type == HCI_KIND_BT_ACL && rx == 5) { + len += ((size_t)c) << 8; + DEBUG_printf("len 0x%x\n", c); + state = STATE_IN_PAYLOAD; + } + if (cmd_type == HCI_KIND_BT_EVENT && rx == 3) { + len = c; + DEBUG_printf("len 0x%x\n", c); + state = STATE_IN_PAYLOAD; + } + if (cmd_type == HCI_KIND_BT_CMD && rx == 4) { + len = c; + DEBUG_printf("len 0x%x\n", c); + state = STATE_IN_PAYLOAD; + } + if (cmd_type == HCI_KIND_LOCAL_CMD && rx == 4) { + len = c; + DEBUG_printf("len 0x%x\n", c); + state = STATE_IN_PAYLOAD; + } + } else if (state == STATE_IN_PAYLOAD) { + buf[rx++] = c; + --len; + } + } + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(rfcore_transparent_obj, rfcore_transparent); + + +mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) { + // This must be first, it sets up the globals dict and other things + MP_DYNRUNTIME_INIT_ENTRY + + // Make the function available in the module's namespace + mp_store_global(MP_QSTR__rfcore_transparent_start, MP_OBJ_FROM_PTR(&rfcore_transparent_obj)); + + // This must be last, it restores the globals dict + MP_DYNRUNTIME_INIT_EXIT +} diff --git a/ports/stm32/modstm.c b/ports/stm32/modstm.c index 94d56d4d082b5..d7318d4338db0 100644 --- a/ports/stm32/modstm.c +++ b/ports/stm32/modstm.c @@ -50,6 +50,7 @@ STATIC const mp_rom_map_elem_t stm_module_globals_table[] = { { 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) }, + { MP_ROM_QSTR(MP_QSTR_rfcore_ble_hci), MP_ROM_PTR(&rfcore_ble_hci_obj) }, #endif }; diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 2a75f7c74a19d..c5ac408bb1eb5 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -503,7 +503,7 @@ STATIC ssize_t tl_sys_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t l return tl_sys_wait_ack(ipcc_membuf_sys_cmd_buf, timeout_ms); } -STATIC int tl_ble_wait_resp(void) { +STATIC size_t tl_ble_wait_resp(parse_hci_info_t *parse) { 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) { @@ -513,8 +513,7 @@ STATIC int tl_ble_wait_resp(void) { } // C2 set IPCC flag -- process the data, clear the flag, and re-enable IRQs. - tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); - return 0; + return tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, parse); } // Synchronously send a BLE command. @@ -522,7 +521,7 @@ STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len) // Poll for completion rather than wait for IRQ->scheduler. LL_C1_IPCC_DisableReceiveChannel(IPCC, IPCC_CH_BLE); tl_hci_cmd(ipcc_membuf_ble_cmd_buf, IPCC_CH_BLE, HCI_KIND_BT_CMD, opcode, buf, len); - tl_ble_wait_resp(); + tl_ble_wait_resp(NULL); } /******************************************************************************/ @@ -793,4 +792,50 @@ STATIC mp_obj_t rfcore_sys_hci(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_sys_hci_obj, 3, 4, rfcore_sys_hci); +STATIC void rfcore_ble_hci_response_to_buffer(void *env, const uint8_t *buf, size_t len) { + DEBUG_printf("rfcore_ble_hci_response_to_buffer len 0x%x\n", len); + mp_obj_t *rsp = (mp_obj_t *)env; + if (*rsp == mp_const_none) { + *rsp = mp_obj_new_bytes(buf, len); + } else { + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(*rsp, &bufinfo, MP_BUFFER_WRITE); + if (bufinfo.len < len) { + mp_raise_OSError(-len); + } + memcpy(bufinfo.buf, buf, len); + bufinfo.len = len; + } +} + +STATIC mp_obj_t rfcore_ble_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_obj_t cmd = args[0]; + mp_obj_t rsp = mp_const_none; + bool return_len = false; + if (n_args == 2) { + rsp = args[1]; + // response buffer passed in, so return rsp length. + return_len = true; + } + + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(cmd, &bufinfo, MP_BUFFER_READ); + + // Poll for completion rather than wait for IRQ->scheduler. Is re-enabled in tl_check_msg. + LL_C1_IPCC_DisableReceiveChannel(IPCC, IPCC_CH_BLE); + + rfcore_ble_hci_cmd(bufinfo.len, bufinfo.buf); + + parse_hci_info_t parse = { rfcore_ble_hci_response_to_buffer, &rsp, false }; + size_t ret_len = tl_ble_wait_resp(&parse); + if (return_len) { + return MP_OBJ_NEW_SMALL_INT(ret_len); + } + return rsp; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_ble_hci_obj, 1, 2, rfcore_ble_hci); + #endif // defined(STM32WB) diff --git a/ports/stm32/rfcore.h b/ports/stm32/rfcore.h index 39267b325ec4d..bac14a843ffc4 100644 --- a/ports/stm32/rfcore.h +++ b/ports/stm32/rfcore.h @@ -44,5 +44,6 @@ void rfcore_end_flash_erase(void); MP_DECLARE_CONST_FUN_OBJ_0(rfcore_status_obj); MP_DECLARE_CONST_FUN_OBJ_1(rfcore_fw_version_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_sys_hci_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(rfcore_ble_hci_obj); #endif // MICROPY_INCLUDED_STM32_RFCORE_H diff --git a/py/dynruntime.h b/py/dynruntime.h index 8564715c0b59f..cf475e2b2ddfb 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -87,6 +87,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_type_int (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_int))) #define mp_type_str (*mp_fun_table.type_str) #define mp_type_bytes (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_bytes))) +#define mp_type_bytearray (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_bytearray))) #define mp_type_tuple (*((mp_obj_base_t *)mp_const_empty_tuple)->type) #define mp_type_list (*mp_fun_table.type_list) #define mp_type_EOFError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_EOFError)))