diff --git a/CMakeLists.txt b/CMakeLists.txt index 23bb7e96..acf54d27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,4 +6,5 @@ zephyr_include_directories(include) +add_subdirectory(drivers) add_subdirectory(lib) diff --git a/Kconfig b/Kconfig index 26968cbb..d5344497 100644 --- a/Kconfig +++ b/Kconfig @@ -5,4 +5,5 @@ # as the module Kconfig entry point (see zephyr/module.yml). You can browse # module options by going to Zephyr -> Modules in Kconfig. +rsource "drivers/Kconfig" rsource "lib/Kconfig" diff --git a/app/Kconfig b/app/Kconfig index 87a0acca..a78e3361 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -79,34 +79,6 @@ config SM_UART_TX_BUF_SIZE If the buffers are full, will send synchronously. These buffers are not used when CMUX is in use. -# -# GPIO functionality -# -config SM_START_SLEEP - bool "Enter sleep on startup" - help - If enabled, the SiP is put into deep sleep when powered on. - It can then be started using SM_POWER_PIN. - -config SM_POWER_PIN - int "Power pin" - default -1 - help - Interface GPIO pin used to put the SiP into deep sleep and wake up from it or exit idle. - The pin is configured with a pull up and must be pulled down shortly to perform one power off or wake up operation. - -config SM_INDICATE_PIN - int "Indicate pin" - default -1 - help - Interface GPIO pin used to indicate that data is available when Serial Modem is idle (after #XSLEEP=2 AT command). - -config SM_INDICATE_TIME - int "Indication period" - default 100 - help - GPIO active indication time in milliseconds. This setting specify the period length for the pin to be active - # # Automatic network attach # diff --git a/app/boards/nrf9131ek_nrf9131_ns.conf b/app/boards/nrf9131ek_nrf9131_ns.conf deleted file mode 100644 index 84a5fbd8..00000000 --- a/app/boards/nrf9131ek_nrf9131_ns.conf +++ /dev/null @@ -1,12 +0,0 @@ -# -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for nRF9131EK. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -CONFIG_SM_POWER_PIN=28 -CONFIG_SM_INDICATE_PIN=0 diff --git a/app/boards/nrf9131ek_nrf9131_ns.overlay b/app/boards/nrf9131ek_nrf9131_ns.overlay index 83e7e8a9..2b628f14 100644 --- a/app/boards/nrf9131ek_nrf9131_ns.overlay +++ b/app/boards/nrf9131ek_nrf9131_ns.overlay @@ -6,11 +6,24 @@ / { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; &uart0 { status = "okay"; hw-flow-control; + + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 28 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; + ri-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (28) pin to save power */ + sense-edge-mask = <0x10000000>; }; diff --git a/app/boards/nrf9151dk_nrf9151_ns.conf b/app/boards/nrf9151dk_nrf9151_ns.conf deleted file mode 100644 index dc6225ac..00000000 --- a/app/boards/nrf9151dk_nrf9151_ns.conf +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for nRF9151DK. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -# When working with PC terminal, unmask the following config. -CONFIG_SM_POWER_PIN=8 -CONFIG_SM_INDICATE_PIN=0 - -# When working with external MCU, unmask the following config. -#CONFIG_SM_POWER_PIN=31 -#CONFIG_SM_INDICATE_PIN=30 diff --git a/app/boards/nrf9151dk_nrf9151_ns.overlay b/app/boards/nrf9151dk_nrf9151_ns.overlay index 2465139c..c8325ad6 100644 --- a/app/boards/nrf9151dk_nrf9151_ns.overlay +++ b/app/boards/nrf9151dk_nrf9151_ns.overlay @@ -4,56 +4,35 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; +/* DTR with DK */ &uart0 { status = "okay"; hw-flow-control; -}; -&uart2 { - compatible = "nordic,nrf-uarte"; - current-speed = <115200>; - status = "disabled"; - hw-flow-control; + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 8 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; + ri-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; - pinctrl-0 = <&uart2_default_alt>; - pinctrl-1 = <&uart2_sleep_alt>; - pinctrl-names = "default", "sleep"; +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (8) pin to save power */ + sense-edge-mask = <0x00000100>; }; &i2c2 { status = "disabled"; }; -&pinctrl { - uart2_default_alt: uart2_default_alt { - group1 { - psels = ; - bias-pull-up; - }; - group2 { - psels = , - , - ; - }; - }; - - uart2_sleep_alt: uart2_sleep_alt { - group1 { - psels = , - , - , - ; - low-power-enable; - }; - }; -}; - /* Enable external flash */ &gd25wb256 { status = "okay"; diff --git a/app/boards/nrf9160dk_nrf9160_ns.conf b/app/boards/nrf9160dk_nrf9160_ns.conf deleted file mode 100644 index 3daf1a2d..00000000 --- a/app/boards/nrf9160dk_nrf9160_ns.conf +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for nRF9160DK. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -# When working with PC terminal, unmask the following config. -CONFIG_SM_POWER_PIN=6 -CONFIG_SM_INDICATE_PIN=2 - -# When working with external MCU, unmask the following config. -#CONFIG_SM_POWER_PIN=31 -#CONFIG_SM_INDICATE_PIN=30 diff --git a/app/boards/nrf9160dk_nrf9160_ns.overlay b/app/boards/nrf9160dk_nrf9160_ns.overlay index 3c5257e9..10fe5518 100644 --- a/app/boards/nrf9160dk_nrf9160_ns.overlay +++ b/app/boards/nrf9160dk_nrf9160_ns.overlay @@ -6,50 +6,29 @@ / { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; +/* DTR with DK */ &uart0 { status = "okay"; hw-flow-control; -}; -&uart2 { - compatible = "nordic,nrf-uarte"; - current-speed = <115200>; - status = "disabled"; - hw-flow-control; + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 6 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; /* button0 */ + ri-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; /* led0 */ + status = "okay"; + }; +}; - pinctrl-0 = <&uart2_default_alt>; - pinctrl-1 = <&uart2_sleep_alt>; - pinctrl-names = "default", "sleep"; +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (6) pin to save power */ + sense-edge-mask = <0x00000040>; }; &i2c2 { status = "disabled"; }; - -&pinctrl { - uart2_default_alt: uart2_default_alt { - group1 { - psels = ; - bias-pull-up; - }; - group2 { - psels = , - , - ; - }; - }; - - uart2_sleep_alt: uart2_sleep_alt { - group1 { - psels = , - , - , - ; - low-power-enable; - }; - }; -}; diff --git a/app/boards/nrf9161dk_nrf9161_ns.conf b/app/boards/nrf9161dk_nrf9161_ns.conf deleted file mode 100644 index 132d30ec..00000000 --- a/app/boards/nrf9161dk_nrf9161_ns.conf +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for nRF9161DK. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -# When working with PC terminal, unmask the following config. -CONFIG_SM_POWER_PIN=8 -CONFIG_SM_INDICATE_PIN=0 - -# When working with external MCU, unmask the following config. -#CONFIG_SM_POWER_PIN=31 -#CONFIG_SM_INDICATE_PIN=30 diff --git a/app/boards/nrf9161dk_nrf9161_ns.overlay b/app/boards/nrf9161dk_nrf9161_ns.overlay index a54ee563..c2c436b5 100644 --- a/app/boards/nrf9161dk_nrf9161_ns.overlay +++ b/app/boards/nrf9161dk_nrf9161_ns.overlay @@ -6,54 +6,33 @@ / { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; +/* DTR with DK */ &uart0 { status = "okay"; hw-flow-control; -}; -&uart2 { - compatible = "nordic,nrf-uarte"; - current-speed = <115200>; - status = "disabled"; - hw-flow-control; + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 8 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; + ri-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; - pinctrl-0 = <&uart2_default_alt>; - pinctrl-1 = <&uart2_sleep_alt>; - pinctrl-names = "default", "sleep"; +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (8) pin to save power */ + sense-edge-mask = <0x00000100>; }; &i2c2 { status = "disabled"; }; -&pinctrl { - uart2_default_alt: uart2_default_alt { - group1 { - psels = ; - bias-pull-up; - }; - group2 { - psels = , - , - ; - }; - }; - - uart2_sleep_alt: uart2_sleep_alt { - group1 { - psels = , - , - , - ; - low-power-enable; - }; - }; -}; - /* Enable external flash */ &gd25wb256 { status = "okay"; diff --git a/app/boards/thingy91_nrf9160_ns.conf b/app/boards/thingy91_nrf9160_ns.conf deleted file mode 100644 index ee2bc94e..00000000 --- a/app/boards/thingy91_nrf9160_ns.conf +++ /dev/null @@ -1,12 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for Thingy:91. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -# Configuration related to external sensors. -CONFIG_SM_POWER_PIN=26 diff --git a/app/boards/thingy91_nrf9160_ns.overlay b/app/boards/thingy91_nrf9160_ns.overlay index c44dd1c7..03e2d28a 100644 --- a/app/boards/thingy91_nrf9160_ns.overlay +++ b/app/boards/thingy91_nrf9160_ns.overlay @@ -6,12 +6,26 @@ / { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; +/* DTR with DK */ &uart0 { status = "okay"; + + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 26 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; /* button0 */ + ri-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; /* blue_led */ + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (26) pin to save power */ + sense-edge-mask = <0x04000000>; }; &uart2 { @@ -21,7 +35,3 @@ &i2c2 { status = "okay"; }; - -&button0 { - status = "disabled"; -}; diff --git a/app/boards/thingy91x_nrf9151_ns.conf b/app/boards/thingy91x_nrf9151_ns.conf deleted file mode 100644 index bb5b4449..00000000 --- a/app/boards/thingy91x_nrf9151_ns.conf +++ /dev/null @@ -1,12 +0,0 @@ -# -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Configuration file for Thingy:91 X. -# This file is merged with prj.conf in the application folder, and options -# set here will take precedence if they are present in both files. - -# Configuration related to external sensors. -CONFIG_SM_POWER_PIN=26 diff --git a/app/boards/thingy91x_nrf9151_ns.overlay b/app/boards/thingy91x_nrf9151_ns.overlay index ac1bd7ae..dc7609af 100644 --- a/app/boards/thingy91x_nrf9151_ns.overlay +++ b/app/boards/thingy91x_nrf9151_ns.overlay @@ -6,14 +6,24 @@ / { chosen { - ncs,sm-uart = &uart0; + ncs,sm-uart = &dtr_uart0; }; }; +/* DTR with DK */ &uart0 { status = "okay"; + + dtr_uart0: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 26 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>; /* button0 */ + ri-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; /* blue_led */ + status = "okay"; + }; }; -&button0 { - status = "disabled"; +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (26) pin to save power */ + sense-edge-mask = <0x04000000>; }; diff --git a/app/overlay-disable-dtr.overlay b/app/overlay-disable-dtr.overlay new file mode 100644 index 00000000..fe0608d8 --- /dev/null +++ b/app/overlay-disable-dtr.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + ncs,sm-uart = &uart0; + }; +}; + +&dtr_uart0 { + status = "disabled"; +}; diff --git a/app/overlay-external-mcu.overlay b/app/overlay-external-mcu.overlay index 6924f87f..5038f0e9 100644 --- a/app/overlay-external-mcu.overlay +++ b/app/overlay-external-mcu.overlay @@ -6,15 +6,62 @@ / { chosen { - ncs,sm-uart = &uart2; + ncs,sm-uart = &dtr_uart2; }; }; +&uart0 { + status = "disabled"; +}; + +&dtr_uart0 { + status = "disabled"; +}; + +/* DTR with external MCU */ &uart2 { + compatible = "nordic,nrf-uarte"; + current-speed = <115200>; + hw-flow-control; status = "okay"; + + dtr_uart2: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&gpio0 31 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + ri-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + pinctrl-0 = <&uart2_default_alt>; + pinctrl-1 = <&uart2_sleep_alt>; + pinctrl-names = "default", "sleep"; }; -/* uart0 is not needed if no logging is received there. */ -&uart0 { - status = "disabled"; +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (31) pin to save power */ + sense-edge-mask = <0x80000000>; +}; + +&pinctrl { + uart2_default_alt: uart2_default_alt { + group1 { + psels = , + ; + bias-pull-up; + }; + group2 { + psels = , + ; + }; + }; + + uart2_sleep_alt: uart2_sleep_alt { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; }; diff --git a/app/overlay-zephyr-modem-nrf9160dk-nrf52840.conf b/app/overlay-zephyr-modem-nrf9160dk-nrf52840.conf deleted file mode 100644 index a7571c85..00000000 --- a/app/overlay-zephyr-modem-nrf9160dk-nrf52840.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# nRF52 <=> nRF91 interface pin 4 (see https://docs.nordicsemi.com/bundle/ug_nrf91_dk/page/UG/nrf91_DK/board_controller.html) -CONFIG_SM_POWER_PIN=22 diff --git a/app/overlay-zephyr-modem-nrf9160dk-nrf52840.overlay b/app/overlay-zephyr-modem-nrf9160dk-nrf52840.overlay index e9c1b3f0..c98fe87f 100644 --- a/app/overlay-zephyr-modem-nrf9160dk-nrf52840.overlay +++ b/app/overlay-zephyr-modem-nrf9160dk-nrf52840.overlay @@ -8,11 +8,21 @@ / { chosen { - ncs,sm-uart = &uart1; + ncs,sm-uart = &dtr_uart; }; }; +/* nRF52 <=> nRF91 interface pins. + * (see https://docs.nordicsemi.com/bundle/ug_nrf91_dk/page/UG/nrf91_DK/board_controller.html) + */ &uart1 { - current-speed = <115200>; - hw-flow-control; + current-speed = <115200>; + hw-flow-control; + status = "okay"; + dtr_uart: dtr-uart { + compatible = "nordic,dtr-uart"; + dtr-gpios = <&interface_to_nrf52840 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; + ri-gpios = <&interface_to_nrf52840 5 (GPIO_ACTIVE_LOW)>; + status = "okay"; + }; }; diff --git a/app/overlay-zephyr-modem.conf b/app/overlay-zephyr-modem.conf index 0d8d41d9..22c32264 100644 --- a/app/overlay-zephyr-modem.conf +++ b/app/overlay-zephyr-modem.conf @@ -4,8 +4,5 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -# The nRF91 is power-managed by Zephyr's modem_cellular driver running on the other chip. -CONFIG_SM_START_SLEEP=y - # Compatibility with cellular modem driver. CONFIG_SM_MODEM_CELLULAR=y diff --git a/app/overlay-zephyr-modem.overlay b/app/overlay-zephyr-modem.overlay new file mode 100644 index 00000000..59b9ba78 --- /dev/null +++ b/app/overlay-zephyr-modem.overlay @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + / { + chosen { + ncs,sm-power-key = &power_key; + }; +}; + +&dtr_uart2 { + /* Currently Cellular Modem driver does not drive DTR, so set it always active */ + dtr-gpios = <&gpio0 31 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; +}; +/ { + power_key: sm-power-key { + compatible = "nordic,sm-power-key"; + gpios = <&gpio0 22 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + status = "okay"; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT events to save power */ + sense-edge-mask = <0xFFFFFFFF>; +}; diff --git a/app/prj.conf b/app/prj.conf index 5599f9ed..5090dc9d 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -134,6 +134,7 @@ CONFIG_SM_EXTERNAL_XTAL=n #CONFIG_LOG_PRINTK=n #CONFIG_LOG_MODE_IMMEDIATE=y #CONFIG_DEBUG_OPTIMIZATIONS=y +#CONFIG_DTR_UART_LOG_LEVEL_DBG=y # For using external GNSS antenna #CONFIG_MODEM_ANTENNA=y diff --git a/app/sample.yaml b/app/sample.yaml index 92b99ad7..c3df0125 100644 --- a/app/sample.yaml +++ b/app/sample.yaml @@ -22,39 +22,14 @@ tests: - ci_build - sysbuild - ci_applications_serial_modem - serial_modem.no_power_indicate: - sysbuild: true - build_only: true - extra_configs: - - CONFIG_SM_POWER_PIN=-1 - - CONFIG_SM_INDICATE_PIN=-1 - platform_allow: - - nrf9151dk/nrf9151/ns - integration_platforms: - - nrf9151dk/nrf9151/ns - tags: - - ci_build - - sysbuild - - ci_applications_serial_modem - serial_modem.no_power: - sysbuild: true - build_only: true - extra_configs: - - CONFIG_SM_POWER_PIN=-1 - platform_allow: - - nrf9151dk/nrf9151/ns - integration_platforms: - - nrf9151dk/nrf9151/ns - tags: - - ci_build - - sysbuild - - ci_applications_serial_modem - serial_modem.no_indicate: + serial_modem.extmcu: sysbuild: true build_only: true - extra_configs: - - CONFIG_SM_INDICATE_PIN=-1 + extra_args: + - EXTRA_DTC_OVERLAY_FILE="overlay-external-mcu.overlay" platform_allow: + - nrf9160dk/nrf9160/ns + - nrf9161dk/nrf9161/ns - nrf9151dk/nrf9151/ns integration_platforms: - nrf9151dk/nrf9151/ns @@ -62,18 +37,18 @@ tests: - ci_build - sysbuild - ci_applications_serial_modem - serial_modem.extmcu: + serial_modem.no_dtr: sysbuild: true build_only: true extra_args: - - EXTRA_DTC_OVERLAY_FILE="overlay-external-mcu.overlay" - extra_configs: - - CONFIG_SM_POWER_PIN=31 - - CONFIG_SM_INDICATE_PIN=30 + - EXTRA_DTC_OVERLAY_FILE="overlay-disable-dtr.overlay" platform_allow: - - nrf9160dk/nrf9160/ns - nrf9161dk/nrf9161/ns - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns + - nrf9131ek/nrf9131/ns + - thingy91/nrf9160/ns + - thingy91x/nrf9151/ns integration_platforms: - nrf9151dk/nrf9151/ns tags: @@ -107,9 +82,6 @@ tests: - EXTRA_CONF_FILE="overlay-ppp.conf" - EXTRA_CONF_FILE="overlay-ppp-without-cmux.conf" - EXTRA_DTC_OVERLAY_FILE="overlay-ppp-without-cmux.overlay" - extra_configs: - - CONFIG_SM_POWER_PIN=31 - - CONFIG_SM_INDICATE_PIN=30 platform_allow: - nrf9160dk/nrf9160/ns - nrf9161dk/nrf9161/ns @@ -146,9 +118,6 @@ tests: build_only: true extra_args: - EXTRA_CONF_FILE="overlay-ppp-cmux-linux.conf" - extra_configs: - - CONFIG_SM_POWER_PIN=31 - - CONFIG_SM_INDICATE_PIN=30 platform_allow: - nrf9160dk/nrf9160/ns - nrf9161dk/nrf9161/ns @@ -167,14 +136,11 @@ tests: build_only: true extra_args: - EXTRA_CONF_FILE="overlay-cmux.conf;overlay-ppp.conf;overlay-zephyr-modem.conf" - - EXTRA_DTC_OVERLAY_FILE="overlay-external-mcu.overlay" + - EXTRA_DTC_OVERLAY_FILE="overlay-external-mcu.overlay;overlay-zephyr-modem.overlay" platform_allow: - nrf9160dk/nrf9160/ns - nrf9161dk/nrf9161/ns - nrf9151dk/nrf9151/ns - - nrf9131ek/nrf9131/ns - - thingy91/nrf9160/ns - - thingy91x/nrf9151/ns integration_platforms: - nrf9151dk/nrf9151/ns tags: @@ -185,7 +151,7 @@ tests: sysbuild: true build_only: true extra_args: - - EXTRA_CONF_FILE="overlay-cmux.conf;overlay-ppp.conf;overlay-zephyr-modem.conf;overlay-zephyr-modem-nrf9160dk-nrf52840.conf" + - EXTRA_CONF_FILE="overlay-cmux.conf;overlay-ppp.conf;overlay-zephyr-modem.conf;" - EXTRA_DTC_OVERLAY_FILE="overlay-zephyr-modem-nrf9160dk-nrf52840.overlay" platform_allow: - nrf9160dk/nrf9160/ns @@ -274,20 +240,6 @@ tests: - ci_build - sysbuild - ci_applications_serial_modem - serial_modem.lwm2m_carrier.thingy91: - sysbuild: true - build_only: true - extra_args: - - EXTRA_CONF_FILE=overlay-carrier.conf - - SB_CONFIG_THINGY91_STATIC_PARTITIONS_LWM2M_CARRIER=y - platform_allow: - - thingy91/nrf9160/ns - integration_platforms: - - thingy91/nrf9160/ns - tags: - - ci_build - - sysbuild - - ci_applications_serial_modem serial_modem.tracing: sysbuild: true build_only: true diff --git a/app/src/main.c b/app/src/main.c index 0cd63bd5..4d907544 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -202,7 +202,11 @@ int start_execute(void) LOG_INF("Serial Modem"); - sm_ctrl_pin_init(); + err = sm_ctrl_pin_init(); + if (err) { + LOG_ERR("Failed to init ctrl_pin: %d", err); + return err; + } /* This will send "READY" or "INIT ERROR" to UART so after this nothing * should be done that can fail @@ -256,13 +260,12 @@ int main(void) check_app_fota_status(); -#if defined(CONFIG_SM_START_SLEEP) - +#if DT_HAS_CHOSEN(ncs_sm_power_key) if (!(rr & NRF_POWER_RESETREAS_OFF_MASK)) { /* DETECT signal from GPIO */ - sm_ctrl_pin_enter_sleep_no_uninit(); + sm_ctrl_pin_enter_sleep_no_uninit(true); } -#endif /* CONFIG_SM_START_SLEEP */ +#endif ret = start_execute(); exit: diff --git a/app/src/sm_at_commands.c b/app/src/sm_at_commands.c index 36e422f3..ed646ec3 100644 --- a/app/src/sm_at_commands.c +++ b/app/src/sm_at_commands.c @@ -78,12 +78,10 @@ enum sleep_modes { SLEEP_MODE_IDLE }; -#if POWER_PIN_IS_ENABLED static struct { struct k_work_delayable work; uint32_t mode; } sleep_control; -#endif bool verify_datamode_control(uint16_t time_limit, uint16_t *time_limit_min); @@ -127,8 +125,6 @@ static int handle_at_slmver(enum at_parser_cmd_type cmd_type, struct at_parser * return ret; } -#if POWER_PIN_IS_ENABLED - static void go_sleep_wk(struct k_work *) { if (sleep_control.mode == SLEEP_MODE_IDLE) { @@ -153,6 +149,10 @@ static int handle_at_sleep(enum at_parser_cmd_type cmd_type, struct at_parser *p if (ret) { return -EINVAL; } + ret = sm_ctrl_pin_ready(); + if (ret) { + return ret; + } if (sleep_control.mode == SLEEP_MODE_DEEP || sleep_control.mode == SLEEP_MODE_IDLE) { k_work_reschedule(&sleep_control.work, SM_UART_RESPONSE_DELAY); @@ -167,8 +167,6 @@ static int handle_at_sleep(enum at_parser_cmd_type cmd_type, struct at_parser *p return ret; } -#endif /* POWER_PIN_IS_ENABLED */ - static void final_call(void (*func)(void)) { /* Delegate the final call to a worker so that the "OK" response is properly sent. */ @@ -377,9 +375,7 @@ int sm_at_init(void) { int err; -#if POWER_PIN_IS_ENABLED k_work_init_delayable(&sleep_control.work, go_sleep_wk); -#endif err = sm_at_tcp_proxy_init(); if (err) { diff --git a/app/src/sm_at_host.c b/app/src/sm_at_host.c index e4e8d5d1..b56b382d 100644 --- a/app/src/sm_at_host.c +++ b/app/src/sm_at_host.c @@ -445,8 +445,7 @@ static void format_final_result(char *buf, size_t buf_len, size_t buf_max_len) } } -static int sm_at_send_indicate(const uint8_t *data, size_t len, - bool print_full_debug, bool indicate) +static int sm_at_send_internal(const uint8_t *data, size_t len, bool print_full_debug) { int ret; @@ -455,16 +454,7 @@ static int sm_at_send_indicate(const uint8_t *data, size_t len, return -EINTR; } - if (indicate) { - enum pm_device_state state = PM_DEVICE_STATE_OFF; - - pm_device_state_get(sm_uart_dev, &state); - if (state != PM_DEVICE_STATE_ACTIVE) { - sm_ctrl_pin_indicate(); - } - } - - ret = sm_tx_write(data, len); + ret = sm_tx_write(data, len, true); if (!ret) { LOG_HEXDUMP_DBG(data, print_full_debug ? len : MIN(HEXDUMP_LIMIT, len), "TX"); } @@ -473,7 +463,7 @@ static int sm_at_send_indicate(const uint8_t *data, size_t len, int sm_at_send(const uint8_t *data, size_t len) { - return sm_at_send_indicate(data, len, true, false); + return sm_at_send_internal(data, len, true); } int sm_at_send_str(const char *str) @@ -713,7 +703,7 @@ static void notification_handler(const char *notification) return; } #endif - sm_at_send_indicate(CRLF_STR, strlen(CRLF_STR), true, true); + sm_at_send_internal(CRLF_STR, strlen(CRLF_STR), true); sm_at_send_str(notification); } else { LOG_DBG("Drop notification: %s", notification); @@ -730,43 +720,28 @@ void rsp_send_error(void) sm_at_send_str(ERROR_STR); } -static void rsp_send_internal(bool indicate, const char *fmt, va_list arg_ptr) +void rsp_send(const char *fmt, ...) { static K_MUTEX_DEFINE(mutex_rsp_buf); static char rsp_buf[SM_AT_MAX_RSP_LEN]; + va_list arg_ptr; int rsp_len; k_mutex_lock(&mutex_rsp_buf, K_FOREVER); + va_start(arg_ptr, fmt); rsp_len = vsnprintf(rsp_buf, sizeof(rsp_buf), fmt, arg_ptr); rsp_len = MIN(rsp_len, sizeof(rsp_buf) - 1); - - sm_at_send_indicate(rsp_buf, rsp_len, true, indicate); - - k_mutex_unlock(&mutex_rsp_buf); -} - -void rsp_send(const char *fmt, ...) -{ - va_list arg_ptr; - - va_start(arg_ptr, fmt); - rsp_send_internal(false, fmt, arg_ptr); va_end(arg_ptr); -} -void rsp_send_indicate(const char *fmt, ...) -{ - va_list arg_ptr; + sm_at_send_internal(rsp_buf, rsp_len, true); - va_start(arg_ptr, fmt); - rsp_send_internal(true, fmt, arg_ptr); - va_end(arg_ptr); + k_mutex_unlock(&mutex_rsp_buf); } void data_send(const uint8_t *data, size_t len) { - sm_at_send_indicate(data, len, false, true); + sm_at_send_internal(data, len, false); } int enter_datamode(sm_datamode_handler_t handler) @@ -958,22 +933,20 @@ int sm_at_host_init(void) static int at_host_power_off(bool shutting_down) { - int err = sm_uart_handler_disable(); - - if (!err || shutting_down) { - /* Wait for UART disabling to complete. */ - k_sleep(K_MSEC(100)); + int err; - /* Power off UART module */ - err = pm_device_action_run(sm_uart_dev, PM_DEVICE_ACTION_SUSPEND); - if (err == -EALREADY) { - err = 0; - } + if (shutting_down) { + err = sm_uart_handler_disable(); if (err) { - LOG_WRN("Failed to suspend UART. (%d)", err); + LOG_WRN("Failed to disable UART. (%d)", err); } } + err = pm_device_action_run(sm_uart_dev, PM_DEVICE_ACTION_SUSPEND); + if (err) { + LOG_WRN("Failed to suspend UART. (%d)", err); + } + return err; } @@ -981,8 +954,10 @@ int sm_at_host_power_off(void) { const int err = at_host_power_off(false); - /* Write sync str to buffer so it is sent first when resuming. */ - sm_at_send_str(SM_SYNC_STR); + /* Write sync str to buffer so it is sent first when resuming, do not flush. */ + if (!IS_ENABLED(CONFIG_SM_SKIP_READY_MSG)) { + sm_tx_write(SM_SYNC_STR, strlen(SM_SYNC_STR), false); + } return err; } @@ -996,9 +971,8 @@ int sm_at_host_power_on(void) return err; } - /* Wait for UART enabling to complete. */ - k_sleep(K_MSEC(100)); - sm_uart_handler_enable(); + /* Flush the TX buffer. */ + sm_tx_write(NULL, 0, true); return 0; } diff --git a/app/src/sm_at_host.h b/app/src/sm_at_host.h index dc880ae6..b46df9d3 100644 --- a/app/src/sm_at_host.h +++ b/app/src/sm_at_host.h @@ -100,14 +100,6 @@ void sm_at_host_uninit(void); */ void rsp_send(const char *fmt, ...); -/** - * @brief Send AT command response with indication. - * - * @param fmt Response message format string. - * - */ -void rsp_send_indicate(const char *fmt, ...); - /** * @brief Send AT command response of OK */ diff --git a/app/src/sm_at_socket.c b/app/src/sm_at_socket.c index 01dcccb3..8e3b2cc2 100644 --- a/app/src/sm_at_socket.c +++ b/app/src/sm_at_socket.c @@ -1799,7 +1799,7 @@ static inline void handle_apoll_socket_event(struct sm_socket *s, struct zsock_p fd->events &= ~fd->revents; } if (!in_datamode()) { - rsp_send_indicate("\r\n#XAPOLL: %d,%d\r\n", fd->fd, fd->revents); + rsp_send("\r\n#XAPOLL: %d,%d\r\n", fd->fd, fd->revents); } } } diff --git a/app/src/sm_ctrl_pin.c b/app/src/sm_ctrl_pin.c index e5285be5..36f4e70a 100644 --- a/app/src/sm_ctrl_pin.c +++ b/app/src/sm_ctrl_pin.c @@ -18,22 +18,20 @@ LOG_MODULE_REGISTER(sm_ctrl_pin, CONFIG_SM_LOG_LEVEL); -#define POWER_PIN_DEBOUNCE_MS 50 +#define SM_DTR_GPIOS DT_NODE_HAS_PROP(DT_CHOSEN(ncs_sm_uart), dtr_gpios) +#define SM_HAS_PWR_KEY DT_HAS_CHOSEN(ncs_sm_power_key) -static const struct device *gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0)); +#if SM_DTR_GPIOS +static const struct gpio_dt_spec dtr_gpio = + GPIO_DT_SPEC_GET_OR(DT_CHOSEN(ncs_sm_uart), dtr_gpios, {0}); -#if POWER_PIN_IS_ENABLED -static struct gpio_callback gpio_cb; -#else -BUILD_ASSERT(!IS_ENABLED(CONFIG_SM_START_SLEEP), - "CONFIG_SM_START_SLEEP requires CONFIG_SM_POWER_PIN to be defined."); +static struct gpio_callback dtr_gpio_cb; #endif +#if SM_HAS_PWR_KEY +static const struct gpio_dt_spec mdm_pwr_gpio = + GPIO_DT_SPEC_GET_OR(DT_CHOSEN(ncs_sm_power_key), gpios, {0}); -#if INDICATE_PIN_IS_ENABLED -static struct k_work_delayable indicate_work; -#endif -#if POWER_PIN_IS_ENABLED -static atomic_t callback_wakeup_running; +static struct gpio_callback mdm_pwr_gpio_cb; #endif static int ext_xtal_control(bool xtal_on) @@ -69,171 +67,89 @@ static int ext_xtal_control(bool xtal_on) return err; } -#if POWER_PIN_IS_ENABLED || INDICATE_PIN_IS_ENABLED - -static int configure_gpio(gpio_pin_t pin, gpio_flags_t flags) +#if SM_DTR_GPIOS +static void dtr_enable_fn(struct k_work *) { - const int err = gpio_pin_configure(gpio_dev, pin, flags); - - if (err) { - LOG_ERR("Failed to configure GPIO pin P0.%d. (%d)", pin, err); - return err; - } - - return 0; + LOG_INF("DTR pin callback work function."); + sm_at_host_power_on(); } -#endif -#if POWER_PIN_IS_ENABLED - -static int configure_power_pin_interrupt(gpio_callback_handler_t handler, gpio_flags_t flags) +static void dtr_pin_callback(const struct device *dev, struct gpio_callback *gpio_callback, + uint32_t) { - int err; - const gpio_pin_t pin = CONFIG_SM_POWER_PIN; + static K_WORK_DEFINE(work, dtr_enable_fn); + bool asserted = gpio_pin_get_dt(&dtr_gpio); - /* First disable the previously configured interrupt. Somehow when in idle mode if - * the wake-up interrupt is configured to be on an edge the power consumption - * drastically increases (3x), which is why it is configured to be level-triggered. - * When entering idle for some reason first disabling the previously edge-level - * configured interrupt is also needed to keep the power consumption down. - */ - err = gpio_pin_interrupt_configure(gpio_dev, pin, GPIO_INT_DISABLE); - if (err) { - LOG_ERR("Failed to configure %s (0x%lx) on power pin. (%d)", - "interrupt", GPIO_INT_DISABLE, err); - } + LOG_DBG("DTR pin %s.", asserted ? "asserted" : "de-asserted"); - err = gpio_pin_interrupt_configure(gpio_dev, pin, flags); - if (err) { - LOG_ERR("Failed to configure %s (0x%x) on power pin. (%d)", - "interrupt", flags, err); - return err; + if (asserted) { + gpio_remove_callback(dev, gpio_callback); + k_work_submit(&work); } - - gpio_init_callback(&gpio_cb, handler, BIT(pin)); - - err = gpio_add_callback(gpio_dev, &gpio_cb); - if (err) { - LOG_ERR("Failed to configure %s (0x%x) on power pin. (%d)", "callback", flags, err); - return err; - } - - LOG_DBG("Configured interrupt (0x%x) on power pin (%u) with handler (%p).", - flags, pin, (void *)handler); - return 0; -} - -static void sm_ctrl_pin_enter_sleep_work_fn(struct k_work *) -{ - sm_ctrl_pin_enter_sleep(); } +#endif -static void power_pin_callback_poweroff(const struct device *dev, - struct gpio_callback *gpio_callback, uint32_t) -{ - static K_WORK_DEFINE(work, sm_ctrl_pin_enter_sleep_work_fn); - - LOG_INF("Power off triggered."); - gpio_remove_callback(dev, gpio_callback); - k_work_submit(&work); -} - -#endif /* POWER_PIN_IS_ENABLED */ - -#if INDICATE_PIN_IS_ENABLED - -static void indicate_stop(void) +int sm_ctrl_pin_ready(void) { - if (gpio_pin_set(gpio_dev, CONFIG_SM_INDICATE_PIN, 0) != 0) { - LOG_WRN("GPIO_0 set error"); +#if SM_DTR_GPIOS + if (gpio_is_ready_dt(&dtr_gpio)) { + return 0; } - LOG_DBG("Stop indicating"); +#endif + LOG_ERR("dtr-gpios is not ready"); + return -EFAULT; } -static void indicate_wk(struct k_work *work) +void sm_ctrl_pin_enter_sleep_no_uninit(bool at_host_power_off) { - ARG_UNUSED(work); - - indicate_stop(); -} - -#endif /* INDICATE_PIN_IS_ENABLED */ - -#if POWER_PIN_IS_ENABLED - -static void power_pin_callback_enable_poweroff_fn(struct k_work *) -{ - LOG_INF("Enabling poweroff interrupt."); - configure_power_pin_interrupt(power_pin_callback_poweroff, GPIO_INT_EDGE_RISING); -} +#if SM_DTR_GPIOS || SM_HAS_PWR_KEY + if (at_host_power_off) { + sm_at_host_power_off(); + } -static K_WORK_DELAYABLE_DEFINE(work_poweroff, power_pin_callback_enable_poweroff_fn); + LOG_INF("Entering sleep. No uninit."); + LOG_PANIC(); -static void power_pin_callback_enable_poweroff(const struct device *dev, - struct gpio_callback *gpio_callback, uint32_t) -{ - LOG_INF("Enabling the poweroff interrupt shortly..."); - gpio_remove_callback(dev, gpio_callback); + k_sleep(K_MSEC(100)); - k_work_reschedule(&work_poweroff, K_MSEC(POWER_PIN_DEBOUNCE_MS)); + nrf_regulators_system_off(NRF_REGULATORS_NS); + assert(false); +#endif } -static void power_pin_callback_wakeup_work_fn(struct k_work *) +void sm_ctrl_pin_enter_sleep(void) { - int err; - - LOG_INF("Resuming from idle."); +#if SM_DTR_GPIOS || SM_HAS_PWR_KEY -#if INDICATE_PIN_IS_ENABLED - if (k_work_delayable_is_pending(&indicate_work)) { - k_work_cancel_delayable(&indicate_work); - indicate_stop(); - } -#endif /* INDICATE_PIN_IS_ENABLED */ - - err = ext_xtal_control(true); - if (err < 0) { - LOG_WRN("Failed to enable ext XTAL: %d", err); - } - err = sm_at_host_power_on(); - if (err) { - LOG_ERR("Failed to power on uart: %d. Resetting Serial Modem.", err); - gpio_remove_callback(gpio_dev, &gpio_cb); - sm_reset(); - return; - } - - atomic_set(&callback_wakeup_running, false); -} - -static void power_pin_callback_wakeup(const struct device *dev, - struct gpio_callback *gpio_callback, uint32_t) -{ - static K_WORK_DEFINE(work, power_pin_callback_wakeup_work_fn); + /* Stop threads, uninitialize host and disable DTR UART. */ + sm_at_host_uninit(); - /* Prevent level triggered interrupt running this multiple times. */ - if (!atomic_cas(&callback_wakeup_running, false, true)) { - return; + /* Only power off the modem if it has not been put + * in flight mode to allow reducing NVM wear. + */ + if (!sm_is_modem_functional_mode(LTE_LC_FUNC_MODE_OFFLINE)) { + sm_power_off_modem(); } - LOG_INF("Resuming from idle shortly..."); - gpio_remove_callback(dev, gpio_callback); - - /* Enable the poweroff interrupt only when the pin will be back to a nonactive state. */ - configure_power_pin_interrupt(power_pin_callback_enable_poweroff, GPIO_INT_EDGE_FALLING); - - k_work_submit(&work); + sm_ctrl_pin_enter_sleep_no_uninit(false); +#endif } void sm_ctrl_pin_enter_idle(void) { +#if SM_DTR_GPIOS LOG_INF("Entering idle."); int err; - gpio_remove_callback(gpio_dev, &gpio_cb); + err = sm_ctrl_pin_ready(); + if (err) { + return; + } - err = configure_power_pin_interrupt(power_pin_callback_wakeup, GPIO_INT_LEVEL_LOW); + gpio_init_callback(&dtr_gpio_cb, dtr_pin_callback, BIT(dtr_gpio.pin)); + err = gpio_add_callback_dt(&dtr_gpio, &dtr_gpio_cb); if (err) { + LOG_ERR("gpio_add_callback failed: %d", err); return; } @@ -241,110 +157,82 @@ void sm_ctrl_pin_enter_idle(void) if (err < 0) { LOG_WRN("Failed to disable ext XTAL: %d", err); } +#endif } -void sm_ctrl_pin_enter_sleep(void) -{ - sm_at_host_uninit(); - - /* Only power off the modem if it has not been put - * in flight mode to allow reducing NVM wear. - */ - if (!sm_is_modem_functional_mode(LTE_LC_FUNC_MODE_OFFLINE)) { - sm_power_off_modem(); - } - sm_ctrl_pin_enter_sleep_no_uninit(); -} - -void sm_ctrl_pin_enter_sleep_no_uninit(void) +void sm_ctrl_pin_enter_shutdown(void) { - LOG_INF("Entering sleep."); - LOG_PANIC(); - nrf_gpio_cfg_sense_set(CONFIG_SM_POWER_PIN, NRF_GPIO_PIN_SENSE_LOW); - + LOG_INF("Entering shutdown."); k_sleep(K_MSEC(100)); nrf_regulators_system_off(NRF_REGULATORS_NS); assert(false); } -#endif /* POWER_PIN_IS_ENABLED */ - -int sm_ctrl_pin_indicate(void) +void sm_ctrl_pin_init_gpios(void) { - int err = 0; +#if SM_DTR_GPIOS + nrf_gpio_cfg_sense_set(dtr_gpio.pin, NRF_GPIO_PIN_SENSE_LOW); +#endif -#if INDICATE_PIN_IS_ENABLED - if (k_work_delayable_is_pending(&indicate_work)) { - return 0; +#if SM_HAS_PWR_KEY + /* Configure Modem Power GPIO */ + if (!gpio_is_ready_dt(&mdm_pwr_gpio)) { + LOG_ERR("Modem Power GPIO not ready"); + return; } - LOG_DBG("Start indicating"); - err = gpio_pin_set(gpio_dev, CONFIG_SM_INDICATE_PIN, 1); - if (err) { - LOG_ERR("GPIO_0 set error: %d", err); - } else { - k_work_reschedule(&indicate_work, K_MSEC(CONFIG_SM_INDICATE_TIME)); + int err = gpio_pin_configure_dt(&mdm_pwr_gpio, GPIO_INPUT); + + if (err < 0) { + LOG_ERR("Failed to configure Modem Power GPIO (%d).", err); + return; } + nrf_gpio_cfg_sense_set(mdm_pwr_gpio.pin, NRF_GPIO_PIN_SENSE_LOW); #endif - return err; } -void sm_ctrl_pin_enter_shutdown(void) -{ - LOG_INF("Entering shutdown."); - - /* De-configure GPIOs */ -#if POWER_PIN_IS_ENABLED - gpio_pin_interrupt_configure(gpio_dev, CONFIG_SM_POWER_PIN, GPIO_INT_DISABLE); - gpio_pin_configure(gpio_dev, CONFIG_SM_POWER_PIN, GPIO_DISCONNECTED); -#endif -#if INDICATE_PIN_IS_ENABLED - gpio_pin_configure(gpio_dev, CONFIG_SM_INDICATE_PIN, GPIO_DISCONNECTED); -#endif - - k_sleep(K_MSEC(100)); - nrf_regulators_system_off(NRF_REGULATORS_NS); - assert(false); +#if SM_HAS_PWR_KEY +static void pwr_pin_fn(struct k_work *) +{ + nrf_gpio_cfg_sense_set(mdm_pwr_gpio.pin, NRF_GPIO_PIN_SENSE_LOW); + sm_ctrl_pin_enter_sleep(); } -void sm_ctrl_pin_init_gpios(void) +static void pwr_pin_callback(const struct device *dev, struct gpio_callback *gpio_callback, + uint32_t) { - if (!device_is_ready(gpio_dev)) { - LOG_ERR("GPIO controller not ready"); - return; - } + static K_WORK_DELAYABLE_DEFINE(work, pwr_pin_fn); -#if POWER_PIN_IS_ENABLED - (void)configure_gpio(CONFIG_SM_POWER_PIN, GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW); -#endif - -#if INDICATE_PIN_IS_ENABLED - (void)configure_gpio(CONFIG_SM_INDICATE_PIN, GPIO_OUTPUT_INACTIVE | GPIO_ACTIVE_LOW); -#endif + k_work_reschedule(&work, K_MSEC(10)); } +#endif int sm_ctrl_pin_init(void) { int err; -#if INDICATE_PIN_IS_ENABLED - k_work_init_delayable(&indicate_work, indicate_wk); -#endif - err = ext_xtal_control(true); - if (err < 0) { + if (err) { LOG_ERR("Failed to enable ext XTAL: %d", err); return err; } -#if POWER_PIN_IS_ENABLED - /* Do not directly enable the poweroff interrupt so that only a full toggle triggers - * power off. This is because power on is triggered on low level, so if the pin is held - * down until Serial Modem is fully initialized releasing it would directly trigger - * the power off. - */ - err = configure_power_pin_interrupt(power_pin_callback_enable_poweroff, - GPIO_INT_EDGE_FALLING); +#if SM_HAS_PWR_KEY + if (!gpio_is_ready_dt(&mdm_pwr_gpio)) { + LOG_ERR("Modem Power GPIO not ready"); + return -ENODEV; + } + err = gpio_pin_interrupt_configure_dt(&mdm_pwr_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (err) { + LOG_ERR("Failed to configure Modem Power GPIO interrupt (%d).", err); + return err; + } + gpio_init_callback(&mdm_pwr_gpio_cb, pwr_pin_callback, BIT(mdm_pwr_gpio.pin)); + err = gpio_add_callback_dt(&mdm_pwr_gpio, &mdm_pwr_gpio_cb); + if (err) { + LOG_ERR("Failed to add Modem Power GPIO callback (%d).", err); + return err; + } #endif - return err; + return 0; } diff --git a/app/src/sm_ctrl_pin.h b/app/src/sm_ctrl_pin.h index e5b8e994..939905dd 100644 --- a/app/src/sm_ctrl_pin.h +++ b/app/src/sm_ctrl_pin.h @@ -13,6 +13,13 @@ * @{ */ +/** + * @brief Check if control pin is ready. + * + * @retval 0 if ready, nonzero otherwise. + */ +int sm_ctrl_pin_ready(void); + /** * @brief Enter idle. */ @@ -25,8 +32,10 @@ void sm_ctrl_pin_enter_sleep(void); /** * @brief Enter sleep without uninitializing AT host. + * + * @param at_host_power_off If true, power off AT host before entering sleep. */ -void sm_ctrl_pin_enter_sleep_no_uninit(void); +void sm_ctrl_pin_enter_sleep_no_uninit(bool at_host_power_off); /** * @brief nRF91 Series SiP enters System OFF mode. @@ -34,11 +43,9 @@ void sm_ctrl_pin_enter_sleep_no_uninit(void); void sm_ctrl_pin_enter_shutdown(void); /** - * @brief Temporarily sets the indicate pin high. - * - * @retval 0 on success, nonzero otherwise. + * @brief Initialize Serial Modem control pins. */ -int sm_ctrl_pin_indicate(void); +void sm_ctrl_pin_init_gpios(void); /** * @brief Initialize Serial Modem control pin module. @@ -47,11 +54,6 @@ int sm_ctrl_pin_indicate(void); */ int sm_ctrl_pin_init(void); -/** - * @brief Initialize Serial Modem control pins, that is, power and indicate pins. - */ -void sm_ctrl_pin_init_gpios(void); - /** @} */ #endif /* SM_CTRL_PIN_ */ diff --git a/app/src/sm_defines.h b/app/src/sm_defines.h index 6731784f..80058ce8 100644 --- a/app/src/sm_defines.h +++ b/app/src/sm_defines.h @@ -37,7 +37,4 @@ enum { #define SM_NRF52_BLK_SIZE 4096 /** nRF52 flash block size for write operation */ #define SM_NRF52_BLK_TIME 2000 /** nRF52 flash block write time in millisecond (1.x second) */ -#define POWER_PIN_IS_ENABLED (CONFIG_SM_POWER_PIN != -1) -#define INDICATE_PIN_IS_ENABLED (CONFIG_SM_INDICATE_PIN != -1) - #endif diff --git a/app/src/sm_ppp.c b/app/src/sm_ppp.c index ea6a4b85..6d6abdf2 100644 --- a/app/src/sm_ppp.c +++ b/app/src/sm_ppp.c @@ -37,7 +37,6 @@ bool sm_fwd_cgev_notifs; #if defined(CONFIG_SM_CMUX) BUILD_ASSERT(!DT_NODE_EXISTS(DT_CHOSEN(ncs_sm_ppp_uart)), "When CMUX is enabled PPP is usable only through it so it cannot have its own UART."); -static const struct device *ppp_uart_dev = DEVICE_DT_GET(DT_CHOSEN(ncs_sm_uart)); #else static const struct device *ppp_uart_dev = DEVICE_DT_GET(DT_CHOSEN(ncs_sm_ppp_uart)); #endif @@ -705,7 +704,6 @@ static void ppp_data_passing_thread(void*, void*, void*) { const size_t mtu = net_if_get_mtu(ppp_iface); struct zsock_pollfd fds[PPP_FDS_COUNT]; - enum pm_device_state state = PM_DEVICE_STATE_OFF; for (size_t i = 0; i != ARRAY_SIZE(fds); ++i) { fds[i].fd = ppp_fds[i]; @@ -745,14 +743,6 @@ static void ppp_data_passing_thread(void*, void*, void*) return; } - /* When DL data is received from the network, check if UART is suspended */ - if (src == MODEM_FD_IDX) { - pm_device_state_get(ppp_uart_dev, &state); - if (state != PM_DEVICE_STATE_ACTIVE) { - LOG_DBG("PPP data received but UART not active"); - sm_ctrl_pin_indicate(); - } - } const ssize_t len = zsock_recv(fds[src].fd, ppp_data_buf, mtu, ZSOCK_MSG_DONTWAIT); diff --git a/app/src/sm_uart_handler.c b/app/src/sm_uart_handler.c index 798282a3..06c6a917 100644 --- a/app/src/sm_uart_handler.c +++ b/app/src/sm_uart_handler.c @@ -268,17 +268,11 @@ static int tx_start(void) uint8_t *buf; size_t len; int err; - enum pm_device_state state = PM_DEVICE_STATE_OFF; if (!atomic_test_bit(&uart_state, SM_UART_STATE_TX_ENABLED_BIT)) { return -EAGAIN; } - pm_device_state_get(sm_uart_dev, &state); - if (state != PM_DEVICE_STATE_ACTIVE) { - return 1; - } - len = ring_buf_get_claim(&tx_buf, &buf, ring_buf_capacity_get(&tx_buf)); err = uart_tx(sm_uart_dev, buf, len, SYS_FOREVER_US); if (err) { @@ -383,7 +377,7 @@ static void uart_callback(const struct device *dev, struct uart_event *evt, void /* Write the data to tx_buffer and trigger sending. Repeat until everything is sent. * Returns 0 on success or a negative error code. */ -static int sm_uart_tx_write(const uint8_t *data, size_t len) +static int sm_uart_tx_write(const uint8_t *data, size_t len, bool flush) { size_t ret; size_t sent = 0; @@ -417,9 +411,9 @@ static int sm_uart_tx_write(const uint8_t *data, size_t len) } k_mutex_unlock(&mutex_tx_put); - if (k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { + if (flush && k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { err = tx_start(); - if (err == 1 || err == -EAGAIN) { + if (err == -EAGAIN) { k_sem_give(&tx_done_sem); return 0; } else if (err) { @@ -427,21 +421,19 @@ static int sm_uart_tx_write(const uint8_t *data, size_t len) k_sem_give(&tx_done_sem); return err; } - } else { - /* TX already in progress. */ } return 0; } -int sm_tx_write(const uint8_t *data, size_t len) +int sm_tx_write(const uint8_t *data, size_t len, bool flush) { #if SM_PIPE if (atomic_test_bit(&sm_pipe.state, SM_PIPE_STATE_INIT_BIT) && sm_pipe.tx_cb != NULL) { return sm_pipe.tx_cb(data, len); } #endif - return sm_uart_tx_write(data, len); + return sm_uart_tx_write(data, len, flush); } int sm_uart_handler_enable(void) @@ -579,7 +571,7 @@ static int pipe_transmit(void *data, const uint8_t *buf, size_t size) if (k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { int err = tx_start(); - if (err == 1 || err == -EAGAIN) { + if (err == -EAGAIN) { k_sem_give(&tx_done_sem); return (int)sent; } else if (err) { diff --git a/app/src/sm_uart_handler.h b/app/src/sm_uart_handler.h index b139d36a..c49aca34 100644 --- a/app/src/sm_uart_handler.h +++ b/app/src/sm_uart_handler.h @@ -46,10 +46,11 @@ int sm_uart_handler_disable(void); * * @param data Data to write. * @param len Length of data to write. + * @param flush Whether to flush the data immediately. * * @retval 0 on success. Otherwise, a negative error code. */ -int sm_tx_write(const uint8_t *data, size_t len); +int sm_tx_write(const uint8_t *data, size_t len, bool flush); /** * @brief Initialize UART pipe for Serial Modem. diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt new file mode 100644 index 00000000..53759c47 --- /dev/null +++ b/drivers/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +add_subdirectory_ifdef(CONFIG_DTR_UART dtr_uart) diff --git a/drivers/Kconfig b/drivers/Kconfig new file mode 100644 index 00000000..6c84de8a --- /dev/null +++ b/drivers/Kconfig @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +menu "Drivers" +rsource "dtr_uart/Kconfig" +endmenu diff --git a/drivers/dtr_uart/CMakeLists.txt b/drivers/dtr_uart/CMakeLists.txt new file mode 100644 index 00000000..9e5f3455 --- /dev/null +++ b/drivers/dtr_uart/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_library() +zephyr_library_sources(dtr_uart.c) diff --git a/drivers/dtr_uart/Kconfig b/drivers/dtr_uart/Kconfig new file mode 100644 index 00000000..8ff976d1 --- /dev/null +++ b/drivers/dtr_uart/Kconfig @@ -0,0 +1,31 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +config DTR_UART + bool "DTR UART implementation - DCE side" + default y + depends on DT_HAS_NORDIC_DTR_UART_ENABLED + select GPIO + select UART_ASYNC_API + select RING_BUFFER + select PM_DEVICE + help + Data Terminal Ready (DTR) UART implements UART API and extends UART communication + with DTR and Ring Indicator (RI) signals. + + Data Communication Equipment (DCE) is controlled by DTE (Data Terminal Equipment) + using DTR signal. When DTR is deasserted, DCE will stop the UART communication and + power down the UART peripheral. When DTR is asserted, DCE will power up the UART + peripheral and start the UART communication. + + RI signal is used by DCE to notify DTE that data is available to read. + +if DTR_UART + +module = DTR_UART +module-str = dtr uart +source "subsys/logging/Kconfig.template.log_config" + +endif # DTR_UART diff --git a/drivers/dtr_uart/dtr_uart.c b/drivers/dtr_uart/dtr_uart.c new file mode 100644 index 00000000..92a7e7d2 --- /dev/null +++ b/drivers/dtr_uart/dtr_uart.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include + +/* + * DTR (Data Terminal Ready) Logic: + * + * This driver implements DTR flow control where DTR input levels directly + * correspond to DTR assertion/deassertion events: + * + * DTR Input Level 0 → DTR_DEASSERTED → UART inactive (powered down) + * DTR Input Level 1 → DTR_ASSERTED → UART active (powered up and ready) + * + * Internal state representation (matches input level): + * - dtr_state = 0: DTR deasserted, UART inactive + * - dtr_state = 1: DTR asserted, UART active + */ + +LOG_MODULE_REGISTER(dtr_uart, CONFIG_DTR_UART_LOG_LEVEL); + +#define DT_DRV_COMPAT nordic_dtr_uart + +struct dtr_uart_data { + /* --- Device and configuration --- */ + const struct device *dev; + + /* --- TX (Transmit) state --- */ + const uint8_t *tx_buf; + size_t tx_len; + bool tx_in_progress; + + /* --- RX (Receive) state --- */ + bool app_rx_enabled; /* RX enabled by application */ + bool rx_active; /* RX currently active */ + int32_t rx_timeout; + struct k_sem rx_disable_sync; /* Semaphore for RX disable completion */ + + /* --- DTR (Data Terminal Ready) --- */ + bool dtr_state; /* 0 = deasserted (UART inactive), 1 = asserted (UART active) */ + struct gpio_callback dtr_cb; + struct k_mutex dtr_mutex; + struct k_work_delayable dtr_work; + + /* --- RI (Ring Indicator) --- */ + struct k_work_delayable ri_work; + + /* --- Power Management --- */ + bool pm_suspended; /* 0 = UART && DTR active, 1 = UART && DTR inactive */ + + /* --- User callback --- */ + uart_callback_t user_callback; + void *user_data; +}; +/* Configuration structure. */ +struct dtr_uart_config { + /* Physical UART device */ + const struct device *uart; + struct gpio_dt_spec dtr_gpio; + struct gpio_dt_spec ri_gpio; +}; + +static void user_callback(const struct device *dev, struct uart_event *evt); + +/* --- Power Management --- */ +static void power_on_uart(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + enum pm_device_state state = PM_DEVICE_STATE_OFF; + int err = pm_device_state_get(config->uart, &state); + + if (err) { + LOG_ERR("Failed to get PM device state (%d).", err); + } + if (state != PM_DEVICE_STATE_ACTIVE) { + /* Power on UART module */ + err = pm_device_action_run(config->uart, PM_DEVICE_ACTION_RESUME); + if (err) { + LOG_ERR("Failed to %s UART device (%d).", "resume", err); + } + LOG_DBG("UART powered on"); + } +} + +static void power_off_uart(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + enum pm_device_state state = PM_DEVICE_STATE_OFF; + int err = pm_device_state_get(config->uart, &state); + + if (err) { + LOG_ERR("Failed to get PM device state (%d).", err); + return; + } + if (state != PM_DEVICE_STATE_SUSPENDED) { + /* Power off UART module */ + err = pm_device_action_run(config->uart, PM_DEVICE_ACTION_SUSPEND); + if (err) { + LOG_ERR("Failed to %s UART device (%d).", "suspend", err); + } + LOG_DBG("UART powered off"); + } +} + +/* --- TX/RX helpers --- */ +static void tx_complete(struct dtr_uart_data *data) +{ + data->tx_in_progress = false; + data->tx_buf = NULL; + data->tx_len = 0; +} + +static void activate_tx(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + + if (data->tx_buf) { + int err; + + data->tx_in_progress = true; + err = uart_tx(config->uart, data->tx_buf, data->tx_len, SYS_FOREVER_US); + if (err) { + LOG_ERR("TX: Not started (%d).", err); + + struct uart_event evt = { + .type = UART_TX_ABORTED, + .data.tx.buf = data->tx_buf, + .data.tx.len = 0, + }; + + tx_complete(data); + user_callback(data->dev, &evt); + } + } +} + +static int deactivate_tx(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + int err; + + if (data->tx_buf && !data->tx_in_progress) { + LOG_DBG("TX: Abort - Before started."); + + struct uart_event evt = { + .type = UART_TX_ABORTED, + .data.tx.buf = data->tx_buf, + .data.tx.len = 0, + }; + tx_complete(data); + user_callback(data->dev, &evt); + return 0; + } + + err = uart_tx_abort(config->uart); + if (err == 0) { + LOG_DBG("TX: Abort."); + } else if (err != -EFAULT) { + /* We assume that UART_TX_ABORTED is sent. */ + LOG_ERR("TX: Abort (%d).", err); + } + + return err; +} + +static int deactivate_rx(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + int err; + + data->rx_active = false; + + err = uart_rx_disable(config->uart); + if (err == -EFAULT) { + LOG_DBG("RX: Already disabled."); + err = 0; + } else if (err && err != -EFAULT) { + LOG_ERR("RX: Failed to disable (%d).", err); + } + + return err; +} + +static void activate_rx(struct dtr_uart_data *data) +{ + if (data->rx_active) { + LOG_DBG("RX: Already active"); + return; + } + + if (!data->app_rx_enabled) { + LOG_DBG("RX: Not enabled by application"); + return; + } + + struct uart_event evt = { + .type = UART_RX_BUF_REQUEST, + }; + user_callback(data->dev, &evt); +} + +/* --- RI handling --- */ +static void ri_work_fn(struct k_work *work) +{ + const struct k_work_delayable *delayed_work = + CONTAINER_OF(work, struct k_work_delayable, work); + const struct dtr_uart_data *data = + CONTAINER_OF(delayed_work, struct dtr_uart_data, ri_work); + const struct dtr_uart_config *config = data->dev->config; + + gpio_pin_set_dt(&config->ri_gpio, 0); +} + +static void ri_start(struct dtr_uart_data *data) +{ + const struct dtr_uart_config *config = data->dev->config; + + gpio_pin_set_dt(&config->ri_gpio, 1); + k_work_schedule(&data->ri_work, K_MSEC(100)); +} + +/* --- DTR handling --- */ +static void uart_dtr_input_gpio_callback(const struct device *port, struct gpio_callback *cb, + uint32_t pins) +{ + struct dtr_uart_data *data = CONTAINER_OF(cb, struct dtr_uart_data, dtr_cb); + + k_work_reschedule(&data->dtr_work, K_MSEC(10)); +} + +static void dtr_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct dtr_uart_data *data = CONTAINER_OF(dwork, struct dtr_uart_data, dtr_work); + const struct dtr_uart_config *config = data->dev->config; + + k_mutex_lock(&data->dtr_mutex, K_FOREVER); + + bool asserted = gpio_pin_get_dt(&config->dtr_gpio) && !data->pm_suspended; + + if (data->dtr_state == asserted) { + LOG_INF("DTR is already %s, ignoring event", asserted ? "asserted" : "deasserted"); + goto exit; + } + + LOG_DBG("DTR %s", asserted ? "asserted" : "deasserted"); + data->dtr_state = asserted; + + if (asserted) { + /* Stop RI signal. */ + k_work_cancel_delayable(&data->ri_work); + gpio_pin_set_dt(&config->ri_gpio, 0); + + /* Enable UART and RX/TX. */ + power_on_uart(data); + activate_rx(data); + activate_tx(data); + } else { + /* Disable TX. */ + deactivate_tx(data); + + /* Wait for RX to be fully disabled, before powering UART down. */ + k_sem_reset(&data->rx_disable_sync); + deactivate_rx(data); + k_sem_take(&data->rx_disable_sync, K_MSEC(100)); + power_off_uart(data); + } +exit: + k_mutex_unlock(&data->dtr_mutex); +} + +/* --- UART and user callbacks --- */ +static void user_callback(const struct device *dev, struct uart_event *evt) +{ + const struct dtr_uart_data *data = dev->data; + + if (data->user_callback) { + data->user_callback(dev, evt, data->user_data); + } +} + +static void uart_callback(const struct device *uart, struct uart_event *evt, void *user_data) +{ + struct device *dev = user_data; + struct dtr_uart_data *data = dev->data; + + switch (evt->type) { + case UART_TX_DONE: + LOG_DBG("TX: Done"); + tx_complete(data); + user_callback(dev, evt); + break; + case UART_TX_ABORTED: + LOG_DBG("TX: Aborted"); + tx_complete(data); + user_callback(dev, evt); + break; + case UART_RX_RDY: + LOG_DBG("RX: Ready buf:%p, offset: %d,len: %d", (void *)evt->data.rx.buf, + evt->data.rx.offset, evt->data.rx.len); + user_callback(dev, evt); + break; + + case UART_RX_BUF_REQUEST: + LOG_DBG("RX: Buf request"); + user_callback(dev, evt); + break; + + case UART_RX_BUF_RELEASED: + LOG_DBG("RX: Buf released %p", (void *)evt->data.rx_buf.buf); + user_callback(dev, evt); + break; + + case UART_RX_DISABLED: { + LOG_DBG("RX: Disabled. DTR: %d.", data->dtr_state); + /* When RX disabled because of DTR down, we handle it ourselves. */ + if (data->dtr_state && data->app_rx_enabled) { + data->app_rx_enabled = false; + user_callback(dev, evt); + } + + if (!data->dtr_state) { + /* RX disabled because of DTR down. */ + k_sem_give(&data->rx_disable_sync); + } + break; + } + case UART_RX_STOPPED: + LOG_DBG("RX: Stopped"); + if (data->dtr_state && data->app_rx_enabled) { + user_callback(dev, evt); + } + break; + } +} + +/* --- API Implementation --- */ +static int api_callback_set(const struct device *dev, uart_callback_t callback, void *user_data) +{ + struct dtr_uart_data *data = dev->data; + + data->user_callback = callback; + data->user_data = user_data; + + return 0; +} + +static int api_tx(const struct device *dev, const uint8_t *buf, size_t len, int32_t timeout) +{ + const struct dtr_uart_config *config = dev->config; + struct dtr_uart_data *data = dev->data; + + LOG_DBG("api_tx: %zu bytes", len); + + if (buf == NULL || len == 0) { + struct uart_event evt = { + .type = UART_TX_DONE, + .data.tx.buf = buf, + .data.tx.len = 0, + }; + user_callback(data->dev, &evt); + return 0; + } + + if (data->tx_buf) { + LOG_WRN("TX: already scheduled"); + return -EBUSY; + } + + if (data->dtr_state) { + return uart_tx(config->uart, buf, len, timeout); + } + data->tx_buf = buf; + data->tx_len = len; + + /* Start RI pulse. */ + ri_start(data); + + /* Buffer the data until DTR is down. */ + return 0; +} + +static int api_tx_abort(const struct device *dev) +{ + struct dtr_uart_data *data = dev->data; + + LOG_DBG("api_tx_abort"); + + return deactivate_tx(data); +} + +static int api_rx_enable(const struct device *dev, uint8_t *buf, size_t len, int32_t timeout) +{ + const struct dtr_uart_config *config = dev->config; + struct dtr_uart_data *data = dev->data; + + LOG_DBG("api_rx_enable: %p, %zu", (void *)buf, len); + + if (data->app_rx_enabled) { + LOG_ERR("RX already enabled"); + return -EBUSY; + } + data->app_rx_enabled = true; + data->rx_timeout = timeout; + + if (!data->dtr_state) { + LOG_DBG("RX: DTR not asserted, releasing buffer."); + struct uart_event evt = { + .type = UART_RX_BUF_RELEASED, + .data.rx_buf.buf = buf, + }; + user_callback(dev, &evt); + return 0; + } + data->rx_active = true; + return uart_rx_enable(config->uart, buf, len, timeout); +} + +static int api_rx_buf_rsp(const struct device *dev, uint8_t *buf, size_t len) +{ + const struct dtr_uart_config *config = dev->config; + struct dtr_uart_data *data = dev->data; + int err = 0; + + LOG_DBG("api_rx_buf_rsp: %p, len: %zu", (void *)buf, len); + + if (!data->dtr_state) { + goto release; + } + + if (!data->app_rx_enabled) { + goto release; + } + + if (!data->rx_active) { + data->rx_active = true; + err = uart_rx_enable(config->uart, buf, len, data->rx_timeout); + if (err == -EBUSY) { + LOG_ERR("RX: Busy"); + err = 0; + goto release; + } + if (err) { + LOG_ERR("RX: Enable failed (%d).", err); + data->rx_active = false; + goto release; + } + + LOG_DBG("RX: Enabled"); + return 0; + } + return uart_rx_buf_rsp(config->uart, buf, len); + +release: + struct uart_event evt = { + .type = UART_RX_BUF_RELEASED, + .data.rx_buf.buf = buf, + }; + user_callback(dev, &evt); + return err; +} + +static int api_rx_disable(const struct device *dev) +{ + struct dtr_uart_data *data = dev->data; + + LOG_DBG("api_rx_disable"); + + data->app_rx_enabled = false; + return deactivate_rx(data); +} + +static int api_err_check(const struct device *dev) +{ + const struct dtr_uart_config *config = dev->config; + + return uart_err_check(config->uart); +} + +#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) +static int api_configure(const struct device *dev, const struct uart_config *cfg) +{ + const struct dtr_uart_config *config = dev->config; + + return uart_configure(config->uart, cfg); +} + +static int api_config_get(const struct device *dev, struct uart_config *cfg) +{ + const struct dtr_uart_config *config = dev->config; + + return uart_config_get(config->uart, cfg); +} +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + +/* --- PM Device Management --- */ +#if defined(CONFIG_PM_DEVICE) +static int dtr_uart_pm_action(const struct device *dev, enum pm_device_action action) +{ + struct dtr_uart_data *data = dev->data; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + LOG_DBG("PM SUSPEND - Disobey DTR and disable UART"); + data->pm_suspended = true; + dtr_work_handler(&data->dtr_work.work); + break; + case PM_DEVICE_ACTION_RESUME: + LOG_DBG("PM RESUME - Obey DTR"); + data->pm_suspended = false; + dtr_work_handler(&data->dtr_work.work); + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif + +/* --- Initialization --- */ +static int dtr_uart_init(const struct device *dev) +{ + struct dtr_uart_data *data = dev->data; + const struct dtr_uart_config *config = dev->config; + int err; + + data->dev = dev; + + /* Check UART device readiness. */ + if (!device_is_ready(config->uart)) { + LOG_ERR("UART device not ready"); + return -ENODEV; + } + + /* Check and configure DTR GPIO as input. */ + if (!gpio_is_ready_dt(&config->dtr_gpio)) { + LOG_ERR("DTR GPIO not ready"); + return -ENODEV; + } + err = gpio_pin_configure_dt(&config->dtr_gpio, GPIO_INPUT); + if (err < 0) { + LOG_ERR("Failed to configure DTR GPIO (%d).", err); + return err; + } + + /* Check and configure RI GPIO as output. */ + if (!gpio_is_ready_dt(&config->ri_gpio)) { + LOG_ERR("RI GPIO not ready"); + return -ENODEV; + } + err = gpio_pin_configure_dt(&config->ri_gpio, GPIO_OUTPUT_INACTIVE); + if (err < 0) { + LOG_ERR("Failed to configure RI GPIO (%d).", err); + return err; + } + + /* Initialize data structure. */ + data->rx_timeout = SYS_FOREVER_US; + k_mutex_init(&data->dtr_mutex); + k_sem_init(&data->rx_disable_sync, 0, 1); + k_work_init_delayable(&data->dtr_work, dtr_work_handler); + k_work_init_delayable(&data->ri_work, ri_work_fn); + + /* Set UART callback. */ + err = uart_callback_set(config->uart, uart_callback, (void *)dev); + if (err < 0) { + LOG_ERR("Failed to set UART callback (%d).", err); + return -EINVAL; + } + + /* Read initial DTR state. */ + int initial_dtr_state = gpio_pin_get_dt(&config->dtr_gpio); + + if (initial_dtr_state < 0) { + LOG_ERR("Failed to read initial DTR state (%d).", initial_dtr_state); + return initial_dtr_state; + } + /* Map GPIO input level directly to DTR state: + * Input level 0 → DTR deasserted (dtr_state = 0, UART inactive) + * Input level 1 → DTR asserted (dtr_state = 1, UART active) + */ + data->dtr_state = initial_dtr_state; + + /* Set up GPIO interrupt for DTR changes. */ + gpio_init_callback(&data->dtr_cb, uart_dtr_input_gpio_callback, BIT(config->dtr_gpio.pin)); + err = gpio_add_callback(config->dtr_gpio.port, &data->dtr_cb); + if (err < 0) { + LOG_ERR("Failed to add DTR GPIO callback (%d).", err); + return err; + } + err = gpio_pin_interrupt_configure_dt(&config->dtr_gpio, GPIO_INT_EDGE_BOTH); + if (err < 0) { + LOG_ERR("Failed to configure DTR GPIO interrupt (%d).", err); + return err; + } + + LOG_DBG("DTR UART initialized, initial DTR state: %d", data->dtr_state); + return 0; +} + +/* --- UART driver API ---- */ +static const struct uart_driver_api dtr_uart_api = { + .callback_set = api_callback_set, + .tx = api_tx, + .tx_abort = api_tx_abort, + .rx_enable = api_rx_enable, + .rx_buf_rsp = api_rx_buf_rsp, + .rx_disable = api_rx_disable, + .err_check = api_err_check, +#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) + .configure = api_configure, + .config_get = api_config_get, +#endif +}; + +/* --- Device Instantiation --- */ +#define DTR_UART_INIT(n) \ + static const struct dtr_uart_config dtr_uart_config_##n = { \ + .dtr_gpio = GPIO_DT_SPEC_INST_GET(n, dtr_gpios), \ + .ri_gpio = GPIO_DT_SPEC_INST_GET(n, ri_gpios), \ + .uart = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(n))), \ + }; \ + static struct dtr_uart_data dtr_uart_data_##n; \ + PM_DEVICE_DT_INST_DEFINE(n, dtr_uart_pm_action); \ + DEVICE_DT_INST_DEFINE(n, dtr_uart_init, PM_DEVICE_DT_INST_GET(n), &dtr_uart_data_##n, \ + &dtr_uart_config_##n, POST_KERNEL, 51, &dtr_uart_api); + +DT_INST_FOREACH_STATUS_OKAY(DTR_UART_INIT) diff --git a/dts/bindings/dte_dtr/nordic,dtr-dte.yaml b/dts/bindings/dte_dtr/nordic,dtr-dte.yaml new file mode 100644 index 00000000..c458c7f6 --- /dev/null +++ b/dts/bindings/dte_dtr/nordic,dtr-dte.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +description: | + DTE side of DTR/RI UART + +compatible: "nordic,dte-dtr" + +properties: + dtr-gpios: + type: phandle-array + description: Output. Data Terminal Ready pin + required: true + + ri-gpios: + type: phandle-array + description: Input. Ring Indicator pin + required: true diff --git a/dts/bindings/dtr_uart/nordic,dtr-uart.yaml b/dts/bindings/dtr_uart/nordic,dtr-uart.yaml new file mode 100644 index 00000000..bb26a3bc --- /dev/null +++ b/dts/bindings/dtr_uart/nordic,dtr-uart.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +description: | + DTR/RI UART + +compatible: "nordic,dtr-uart" + +include: uart-device.yaml + +properties: + dtr-gpios: + type: phandle-array + description: Data Terminal Ready pin + required: true + + ri-gpios: + type: phandle-array + description: Ring Indicator pin + required: true diff --git a/dts/bindings/sm/nordic,sm-power-key.yaml b/dts/bindings/sm/nordic,sm-power-key.yaml new file mode 100644 index 00000000..9fb11fd2 --- /dev/null +++ b/dts/bindings/sm/nordic,sm-power-key.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +description: | + Serial Modem power pin for Zephyr Modem. + +compatible: "nordic,sm-power-key" + +properties: + gpios: + type: phandle-array + description: Input. Power GPIO pin. + required: true diff --git a/include/sm_host.h b/include/sm_host.h index 637adf2f..e28f9084 100644 --- a/include/sm_host.h +++ b/include/sm_host.h @@ -57,45 +57,62 @@ enum at_cmd_state { typedef void (*sm_data_handler_t)(const uint8_t *data, size_t datalen); /** - * @typedef sm_ind_handler_t + * @typedef sm_ri_handler_t * - * Handler to handle @kconfig{CONFIG_SM_HOST_INDICATE_PIN} signal from Serial Modem. + * Handler to handle the Ring Indicate (RI) signal from Serial Modem. */ -typedef void (*sm_ind_handler_t)(void); +typedef void (*sm_ri_handler_t)(void); /**@brief Initialize Serial Modem Host library. * * @param handler Pointer to a handler function of type @ref sm_data_handler_t. + * @param automatic_uart If true, DTR and UART are automatically managed by the library. + * @param inactivity_timeout Inactivity timeout for DTR and UART disablement. Only used if @p + * automatic is true. * * @return Zero on success, non-zero otherwise. */ -int sm_host_init(sm_data_handler_t handler); +int sm_host_init(sm_data_handler_t handler, bool automatic_uart, k_timeout_t inactivity_timeout); /**@brief Un-initialize Serial Modem Host */ int sm_host_uninit(void); /** - * @brief Register callback for @kconfig{CONFIG_SM_HOST_INDICATE_PIN} indication + * @brief Register callback for Ring Indicate (RI) pin. * - * @param handler Pointer to a handler function of type @ref sm_ind_handler_t. - * @param wakeup Enable/disable System Off wakeup by GPIO Sense. + * @param handler Pointer to a handler function of type @ref sm_ri_handler_t. * - * @retval Zero Success. - * @retval -EFAULT if @kconfig{CONFIG_SM_HOST_INDICATE_PIN} is not defined. + * @retval Zero on success. Otherwise, a (negative) error code is returned. */ -int sm_host_register_ind(sm_ind_handler_t handler, bool wakeup); +int sm_host_register_ri_handler(sm_ri_handler_t handler); /** - * @brief Toggle power pin of the nRF91 Series device configured with - * @kconfig{CONFIG_SM_HOST_POWER_PIN}. + * @brief Configure automatic DTR UART handling * - * The pin is enabled for the time specified in @kconfig{CONFIG_SM_HOST_POWER_PIN_TIME} - * and then disabled. + * If automatic DTR UART handling is enabled, the library will enable DTR UART when RI + * signal is detected, and disable it after inactivity timeout. + * + * @param automatic If true, DTR UART is automatically managed by the library. + * @param inactivity Inactivity timeout for DTR UART disablement. Only used if @p + * automatic is true. * * @return Zero on success, non-zero otherwise. */ -int sm_host_power_pin_toggle(void); +void sm_host_configure_dtr_uart(bool automatic, k_timeout_t inactivity); + +/** + * @brief Disable DTR UART + * + * Disables DTR UART. Disables automatic DTR UART handling. + */ +void sm_host_disable_dtr_uart(void); + +/** @brief Enable DTR UART + * + * Enables DTR UART. Disables automatic DTR UART handling. + */ +void sm_host_enable_dtr_uart(void); /** * @brief Function to send an AT command in Serial Modem command mode diff --git a/lib/sm_host/Kconfig b/lib/sm_host/Kconfig index f7337439..9ff56139 100644 --- a/lib/sm_host/Kconfig +++ b/lib/sm_host/Kconfig @@ -7,9 +7,11 @@ menuconfig SM_HOST bool "Serial Modem Host Library" depends on SERIAL - depends on UART_ASYNC_API - depends on RING_BUFFER + select UART_ASYNC_API + select RING_BUFFER select UART_USE_RUNTIME_CONFIGURE + select GPIO_GET_CONFIG + select PM_DEVICE if SM_HOST @@ -72,26 +74,6 @@ config SM_HOST_CR_LF_TERMINATION endchoice -config SM_HOST_POWER_PIN - int "Power pin" - default -1 - help - Interface GPIO to toggle power pin of the nRF91 Series device. - -config SM_HOST_POWER_PIN_TIME - int "Power pin active time" - default 100 - help - GPIO active time in milliseconds. This setting specifies - the period length for the pin to be active. - -config SM_HOST_INDICATE_PIN - int "Indicate pin" - default -1 - help - Interface GPIO pin used by Serial Modem to indicate that data is available or - an unexpected reset has occurred. - module = SM_HOST module-str = SM Host source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config" diff --git a/lib/sm_host/sm_host.c b/lib/sm_host/sm_host.c index 57da75ab..bdec3d39 100644 --- a/lib/sm_host/sm_host.c +++ b/lib/sm_host/sm_host.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -12,12 +13,11 @@ #include #include #include +#include #include LOG_MODULE_REGISTER(sm_host, CONFIG_SM_HOST_LOG_LEVEL); -BUILD_ASSERT(CONFIG_SM_HOST_POWER_PIN >= 0, "Power pin not configured"); - #define UART_RX_MARGIN_MS 10 #define UART_RX_TIMEOUT_US 2000 #define UART_ERROR_DELAY_MS 500 @@ -57,27 +57,23 @@ K_MSGQ_DEFINE(rx_event_queue, sizeof(struct rx_event_t), UART_RX_EVENT_COUNT, 1) /* Ring buffer for TX data. */ RING_BUF_DECLARE(tx_buf, CONFIG_SM_HOST_UART_TX_BUF_SIZE); static K_SEM_DEFINE(tx_done_sem, 0, 1); +static K_SEM_DEFINE(uart_disabled_sem, 0, 1); -enum uart_recovery_state { - RECOVERY_DISABLED, - RECOVERY_IDLE, - RECOVERY_ONGOING +enum sm_host_uart_state { + SM_HOST_TX_ENABLED_BIT, + SM_HOST_RX_ENABLED_BIT, + SM_HOST_RX_RECOVERY_BIT, + SM_HOST_RX_RECOVERY_DISABLED_BIT }; -static atomic_t recovery_state; +static atomic_t uart_state; + +static sm_ri_handler_t ri_handler; + static K_SEM_DEFINE(at_rsp, 0, 1); static sm_data_handler_t data_handler; static enum at_cmd_state sm_at_state; -#if DT_HAS_CHOSEN(ncs_sm_gpio) -static const struct device *gpio_dev = DEVICE_DT_GET(DT_CHOSEN(ncs_sm_gpio)); -#else -static const struct device *gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0)); -#endif -static struct k_work_delayable gpio_power_pin_disable_work; -static sm_ind_handler_t ind_handler; -static sm_ind_handler_t ind_handler_backup; - #if defined(CONFIG_SM_HOST_SHELL) static const struct shell *global_shell; static const char at_usage_str[] = "Usage: sm "; @@ -85,70 +81,35 @@ static const char at_usage_str[] = "Usage: sm "; extern void sm_monitor_dispatch(const char *notif, size_t len); extern char *strnstr(const char *haystack, const char *needle, size_t haystack_sz); +static int dtr_uart_enable(void); -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) -static bool indicate_pin_enabled; - -static void gpio_cb_func(const struct device *dev, struct gpio_callback *gpio_cb, uint32_t pins); -static struct gpio_callback gpio_cb; -#endif +struct dtr_config { -static int indicate_pin_enable(void) -{ -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) - int err = 0; + /* DTR */ + const struct gpio_dt_spec dtr_gpio; - if (!indicate_pin_enabled) { - err = gpio_pin_configure(gpio_dev, CONFIG_SM_HOST_INDICATE_PIN, - GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW); - if (err) { - LOG_ERR("GPIO config error: %d", err); - return err; - } + /* RI */ + const struct gpio_dt_spec ri_gpio; - gpio_init_callback(&gpio_cb, gpio_cb_func, BIT(CONFIG_SM_HOST_INDICATE_PIN)); - err = gpio_add_callback(gpio_dev, &gpio_cb); - if (err) { - LOG_WRN("GPIO add callback error: %d", err); - } - err = gpio_pin_interrupt_configure(gpio_dev, CONFIG_SM_HOST_INDICATE_PIN, - GPIO_INT_LEVEL_LOW); - if (err) { - LOG_WRN("GPIO interrupt configure error: %d", err); - } - indicate_pin_enabled = true; - LOG_DBG("Indicate pin enabled"); - } -#endif - return 0; -} + /* Automatically activate DTR UART from RI and disable it after inactivity. */ + bool automatic; + k_timeout_t inactivity; -static void indicate_pin_disable(void) -{ -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) - if (indicate_pin_enabled) { - gpio_remove_callback(gpio_dev, &gpio_cb); - gpio_pin_interrupt_configure(gpio_dev, CONFIG_SM_HOST_INDICATE_PIN, - GPIO_INT_DISABLE); - gpio_pin_configure(gpio_dev, CONFIG_SM_HOST_INDICATE_PIN, GPIO_DISCONNECTED); - indicate_pin_enabled = false; - LOG_DBG("Indicate pin disabled"); - } -#endif -} + /* Current DTR state. */ + bool active; -static void gpio_power_pin_disable_work_fn(struct k_work *work) -{ - ARG_UNUSED(work); + /* Work items for enabling and disabling DTR UART. */ + struct k_work dtr_uart_enable_work; + struct k_work_delayable dtr_uart_disable_work; +}; - if (gpio_pin_set(gpio_dev, CONFIG_SM_HOST_POWER_PIN, 0) != 0) { - LOG_WRN("GPIO set error"); - } - /* When Serial Modem is woken up, indicate pin must be enabled */ - (void)indicate_pin_enable(); +static struct dtr_config dtr_config = { + .dtr_gpio = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(dte_dtr), dtr_gpios, {0}), + .ri_gpio = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(dte_dtr), ri_gpios, {0}), +}; - LOG_INF("Disable power pin"); -} +static void gpio_cb_func(const struct device *dev, struct gpio_callback *gpio_cb, uint32_t pins); +static struct gpio_callback gpio_cb; static inline struct rx_buf_t *block_start_get(uint8_t *buf) { @@ -197,6 +158,10 @@ static int rx_enable(void) struct rx_buf_t *buf; int ret; + if (atomic_test_bit(&uart_state, SM_HOST_RX_ENABLED_BIT)) { + return 0; + } + buf = buf_alloc(); if (!buf) { LOG_DBG("Failed to allocate RX buffer"); @@ -206,9 +171,12 @@ static int rx_enable(void) ret = uart_rx_enable(uart_dev, buf->buf, sizeof(buf->buf), UART_RX_TIMEOUT_US); if (ret) { LOG_WRN("uart_rx_enable failed: %d", ret); + buf_unref(buf->buf); return ret; } + atomic_set_bit(&uart_state, SM_HOST_RX_ENABLED_BIT); + return 0; } @@ -216,18 +184,29 @@ static int rx_disable(void) { int err; - /* Wait for possible rx_enable to complete. */ - if (atomic_set(&recovery_state, RECOVERY_DISABLED) == RECOVERY_ONGOING) { + atomic_set_bit(&uart_state, SM_HOST_RX_RECOVERY_DISABLED_BIT); + + while (atomic_test_bit(&uart_state, SM_HOST_RX_RECOVERY_BIT)) { + /* Wait until possible recovery is complete. */ k_sleep(K_MSEC(10)); } + if (!atomic_test_bit(&uart_state, SM_HOST_RX_ENABLED_BIT)) { + return 0; + } + + k_sem_reset(&uart_disabled_sem); + err = uart_rx_disable(uart_dev); if (err) { LOG_ERR("UART RX disable failed: %d", err); - atomic_set(&recovery_state, RECOVERY_IDLE); return err; } + /* Wait until RX is actually disabled. */ + k_sem_take(&uart_disabled_sem, K_MSEC(100)); + atomic_clear_bit(&uart_state, SM_HOST_RX_ENABLED_BIT); + return 0; } @@ -235,17 +214,18 @@ static void rx_recovery(void) { int err; - if (atomic_get(&recovery_state) != RECOVERY_ONGOING) { + if (atomic_test_bit(&uart_state, SM_HOST_RX_RECOVERY_DISABLED_BIT)) { return; } + atomic_set_bit(&uart_state, SM_HOST_RX_RECOVERY_BIT); + err = rx_enable(); if (err) { k_work_schedule(&rx_process_work, K_MSEC(UART_RX_MARGIN_MS)); - return; } - atomic_cas(&recovery_state, RECOVERY_ONGOING, RECOVERY_IDLE); + atomic_clear_bit(&uart_state, SM_HOST_RX_RECOVERY_BIT); } /* Attempts to find AT responses in the UART buffer. */ @@ -360,6 +340,10 @@ static int tx_start(void) size_t len; int err; + if (!atomic_test_bit(&uart_state, SM_HOST_TX_ENABLED_BIT)) { + return -EAGAIN; + } + len = ring_buf_get_claim(&tx_buf, &buf, ring_buf_capacity_get(&tx_buf)); err = uart_tx(uart_dev, buf, len, UART_TX_TIMEOUT_US); if (err) { @@ -380,12 +364,23 @@ static int tx_write(const uint8_t *data, size_t len, bool flush) LOG_HEXDUMP_DBG(data, len, "TX"); + if (dtr_config.automatic && !dtr_config.active) { + err = dtr_uart_enable(); + if (err) { + LOG_ERR("Failed to enable DTR (%d).", err); + } + } + while (sent < len) { ret = ring_buf_put(&tx_buf, data + sent, len - sent); if (ret) { sent += ret; } else { /* Buffer full, block and start TX. */ + if (atomic_test_bit(&uart_state, SM_HOST_TX_ENABLED_BIT) == 0) { + LOG_ERR("TX disabled, %zu bytes dropped", len - sent); + return -EIO; + } k_sem_take(&tx_done_sem, K_FOREVER); err = tx_start(); if (err) { @@ -398,18 +393,66 @@ static int tx_write(const uint8_t *data, size_t len, bool flush) } } - if (flush && k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { - err = tx_start(); - if (err) { - LOG_ERR("tx_start failed: %d", err); - k_sem_give(&tx_done_sem); - return err; + if (flush) { + if (atomic_test_bit(&uart_state, SM_HOST_TX_ENABLED_BIT) == 0) { + LOG_INF("TX disabled, data will be sent when enabled"); + return -EAGAIN; } + if (k_sem_take(&tx_done_sem, K_NO_WAIT) == 0) { + err = tx_start(); + if (err) { + LOG_ERR("tx_start failed: %d", err); + k_sem_give(&tx_done_sem); + return err; + } + } + } + + return 0; +} + +static int tx_enable(void) +{ + if (!atomic_test_and_set_bit(&uart_state, SM_HOST_TX_ENABLED_BIT)) { + k_sem_give(&tx_done_sem); + } + return 0; +} + +static int tx_disable(k_timeout_t timeout) +{ + int err; + + if (!atomic_test_and_clear_bit(&uart_state, SM_HOST_TX_ENABLED_BIT)) { + return 0; + } + + if (k_sem_take(&tx_done_sem, timeout) == 0) { + return 0; + } + + err = uart_tx_abort(uart_dev); + if (!err) { + LOG_INF("TX aborted"); + } else if (err != -EFAULT) { + LOG_ERR("uart_tx_abort failed (%d).", err); + return err; } return 0; } +static void reschedule_disable(void) +{ + if (dtr_config.active && dtr_config.automatic) { + /* Restart the inactivity timer. */ + k_work_reschedule(&dtr_config.dtr_uart_disable_work, dtr_config.inactivity); + } else { + /* Stop the inactivity timer. */ + k_work_cancel_delayable(&dtr_config.dtr_uart_disable_work); + } +} + static void uart_callback(const struct device*, struct uart_event *evt, void*) { struct rx_buf_t *buf; @@ -431,6 +474,7 @@ static void uart_callback(const struct device*, struct uart_event *evt, void*) LOG_ERR("tx_start failed: %d", err); k_sem_give(&tx_done_sem); } + reschedule_disable(); break; case UART_TX_ABORTED: err = ring_buf_get_finish(&tx_buf, evt->data.tx.len); @@ -452,6 +496,7 @@ static void uart_callback(const struct device*, struct uart_event *evt, void*) break; } k_work_submit((struct k_work *)&rx_process_work); + reschedule_disable(); break; case UART_RX_BUF_REQUEST: buf = buf_alloc(); @@ -470,9 +515,8 @@ static void uart_callback(const struct device*, struct uart_event *evt, void*) } break; case UART_RX_DISABLED: - if (atomic_cas(&recovery_state, RECOVERY_IDLE, RECOVERY_ONGOING)) { - k_work_submit((struct k_work *)&rx_process_work); - } + k_sem_give(&uart_disabled_sem); + k_work_submit((struct k_work *)&rx_process_work); break; default: break; @@ -523,156 +567,327 @@ static int uart_init(const struct device *uart_dev) } /* Enable RX */ - atomic_set(&recovery_state, RECOVERY_IDLE); + atomic_clear(&uart_state); err = rx_enable(); /* Enable TX */ - k_sem_give(&tx_done_sem); + tx_enable(); return err; } -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) -static struct gpio_callback gpio_cb; +static int uart_power_state_action(enum pm_device_action action) +{ + enum pm_device_state state = PM_DEVICE_STATE_OFF; + int err = pm_device_state_get(uart_dev, &state); + + if (err) { + LOG_ERR("Failed to get PM device state: %d", err); + return err; + } + + if (action != PM_DEVICE_ACTION_RESUME && action != PM_DEVICE_ACTION_SUSPEND) { + return -EOPNOTSUPP; + } + + if ((action == PM_DEVICE_ACTION_RESUME && state == PM_DEVICE_STATE_ACTIVE) || + (action == PM_DEVICE_ACTION_SUSPEND && state == PM_DEVICE_STATE_SUSPENDED)) { + return 0; + } + + err = pm_device_action_run(uart_dev, action); + if (err) { + LOG_ERR("Action %d failed on UART device: %d", action, err); + return err; + } + + return 0; +} + +static int dtr_pin_set(bool level) +{ + int err; + + if (gpio_is_ready_dt(&dtr_config.dtr_gpio)) { + err = gpio_pin_set_dt(&dtr_config.dtr_gpio, level); + if (err) { + LOG_ERR("Failed to set DTR pin: %d", err); + return -EFAULT; + } + } else { + LOG_WRN("DTR pin not configured"); + return -EFAULT; + } + return 0; +} + +static int dtr_uart_disable(void) +{ + int err; + + /* Wait until TX is done and disable TX. */ + err = tx_disable(K_NO_WAIT); + if (err) { + LOG_ERR("TX disable failed (%d).", err); + return err; + } + + /* Ask Serial Modem to disable UART. */ + err = dtr_pin_set(0); + if (err) { + LOG_ERR("Failed to set DTR pin (%d).", err); + return err; + } + + /* Optional: Wait for possible Serial Modem TX to complete. */ + /* k_sleep(K_MSEC(100)); */ + + /* Disable RX. */ + err = rx_disable(); + if (err) { + LOG_ERR("RX disable failed (%d).", err); + return err; + } + + /* Power off UART module */ + err = uart_power_state_action(PM_DEVICE_ACTION_SUSPEND); + if (err) { + LOG_ERR("Failed to suspend UART (%d).", err); + return err; + } + + dtr_config.active = false; + + LOG_DBG("DTR UART disabled"); + return 0; +} + +static void dtr_uart_disable_work_fn(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + err = dtr_uart_disable(); + if (err) { + LOG_ERR("Failed to disable DTR UART (%d).", err); + } +} + +static int dtr_uart_enable(void) +{ + int err; + + /* Power on UART module */ + err = uart_power_state_action(PM_DEVICE_ACTION_RESUME); + if (err) { + LOG_ERR("Failed to resume UART (%d).", err); + return err; + } + + /* Enable RX. */ + atomic_clear_bit(&uart_state, SM_HOST_RX_RECOVERY_DISABLED_BIT); + err = rx_enable(); + if (err) { + LOG_ERR("Failed to enable RX (%d).", err); + return err; + } + + /* Ask Serial Modem to enable UART. */ + err = dtr_pin_set(1); + if (err) { + LOG_ERR("Failed to set DTR pin (%d).", err); + return err; + } + + /* Optional: Wait for Serial Modem to be ready */ + /* k_sleep(K_MSEC(100)); */ + + /* Enable TX. */ + err = tx_enable(); + if (err) { + LOG_ERR("Failed to enable TX (%d).", err); + return err; + } + + /* Start TX in case there is pending data. */ + err = tx_start(); + if (err) { + LOG_ERR("Failed to start TX (%d).", err); + return err; + } + + dtr_config.active = true; + + reschedule_disable(); + + LOG_DBG("DTR UART enabled"); + return 0; +} + +static void dtr_uart_enable_work_fn(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + err = dtr_uart_enable(); + if (err) { + LOG_ERR("Failed to enable DTR UART (%d).", err); + } +} static void gpio_cb_func(const struct device *dev, struct gpio_callback *gpio_cb, uint32_t pins) { - if ((BIT(CONFIG_SM_HOST_INDICATE_PIN) & pins) == 0) { + if ((BIT(dtr_config.ri_gpio.pin) & pins) == 0) { return; } - if (k_work_delayable_is_pending(&gpio_power_pin_disable_work)) { - (void)k_work_cancel_delayable(&gpio_power_pin_disable_work); - (void)gpio_pin_set(gpio_dev, CONFIG_SM_HOST_POWER_PIN, 0); - } else { - /* Disable indicate pin so that callbacks doesn't keep on coming. */ - indicate_pin_disable(); + if (dtr_config.automatic && !dtr_config.active) { + /* Wake up the application */ + k_work_submit(&dtr_config.dtr_uart_enable_work); } - LOG_INF("Remote indication"); - if (ind_handler) { - ind_handler(); + if (ri_handler) { + ri_handler(); } else { - LOG_WRN("Indicate PIN configured but sm_ind_handler_t not defined"); + LOG_INF("Ring Indicate received, but no handler registered"); } } -#endif /* CONFIG_SM_HOST_INDICATE_PIN */ static int gpio_init(void) { int err; - if (!device_is_ready(gpio_dev)) { - LOG_ERR("GPIO controller not ready"); - return -ENODEV; + if (gpio_is_ready_dt(&dtr_config.dtr_gpio)) { + err = gpio_pin_configure_dt(&dtr_config.dtr_gpio, GPIO_OUTPUT_ACTIVE); + if (err) { + LOG_ERR("Failed to configure DTR pin: %d", err); + return -EFAULT; + } + } else { + LOG_WRN("DTR GPIO is not ready"); } - err = gpio_pin_configure(gpio_dev, CONFIG_SM_HOST_POWER_PIN, - GPIO_OUTPUT_INACTIVE | GPIO_ACTIVE_LOW); - if (err) { - LOG_ERR("GPIO config error: %d", err); - return err; - } + if (gpio_is_ready_dt(&dtr_config.ri_gpio)) { + gpio_flags_t flags; + nrf_gpio_pin_sense_t sense; - err = indicate_pin_enable(); + err = gpio_pin_configure_dt(&dtr_config.ri_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("GPIO config error: %d", err); + return err; + } - return err; + err = gpio_pin_get_config_dt(&dtr_config.ri_gpio, &flags); + if (err) { + LOG_ERR("Failed to get RI pin config: %d", err); + return err; + } + + if (flags & GPIO_PULL_DOWN) { + LOG_DBG("Wakeup sense %s", "high"); + sense = NRF_GPIO_PIN_SENSE_HIGH; + } else { + LOG_DBG("Wakeup sense %s", "low"); + sense = NRF_GPIO_PIN_SENSE_LOW; + } + nrf_gpio_cfg_sense_set(dtr_config.ri_gpio.pin, sense); + + gpio_init_callback(&gpio_cb, gpio_cb_func, BIT(dtr_config.ri_gpio.pin)); + err = gpio_add_callback_dt(&dtr_config.ri_gpio, &gpio_cb); + if (err) { + LOG_WRN("GPIO add callback error: %d", err); + return err; + } + err = gpio_pin_interrupt_configure_dt(&dtr_config.ri_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (err) { + LOG_WRN("GPIO interrupt configure error: %d", err); + return err; + } + } else { + LOG_WRN("RI GPIO is not ready"); + } + + return 0; } -int sm_host_init(sm_data_handler_t handler) +int sm_host_init(sm_data_handler_t handler, bool automatic, k_timeout_t inactivity) { int err; + static bool initialized; - if (handler != NULL && data_handler != NULL) { - LOG_ERR("Already initialized"); - return -EFAULT; + if (initialized) { + return -EALREADY; } + initialized = true; data_handler = handler; - ind_handler = NULL; - ind_handler_backup = NULL; + ri_handler = NULL; sm_at_state = AT_CMD_OK; err = gpio_init(); - if (err != 0) { - LOG_WRN("Failed to init gpio"); - return err; + if (err) { + LOG_ERR("GPIO init (%d)", err); + return -EFAULT; } err = uart_init(uart_dev); if (err) { - LOG_ERR("UART could not be initialized: %d", err); + LOG_ERR("UART init (%d)", err); return -EFAULT; } - k_work_init_delayable(&gpio_power_pin_disable_work, gpio_power_pin_disable_work_fn); k_work_init_delayable(&rx_process_work, rx_process); + k_work_init(&dtr_config.dtr_uart_enable_work, dtr_uart_enable_work_fn); + k_work_init_delayable(&dtr_config.dtr_uart_disable_work, dtr_uart_disable_work_fn); + /* Initialize shell pointer so it's available for printing in callbacks */ #if defined(CONFIG_SHELL_BACKEND_SERIAL) global_shell = shell_backend_uart_get_ptr(); #elif defined(CONFIG_SHELL_BACKEND_RTT) global_shell = shell_backend_rtt_get_ptr(); #endif + + dtr_config.automatic = automatic; + dtr_config.inactivity = inactivity; + dtr_config.active = true; + + reschedule_disable(); + return 0; } int sm_host_uninit(void) { - rx_disable(); + int err; - gpio_pin_configure(gpio_dev, CONFIG_SM_HOST_POWER_PIN, GPIO_DISCONNECTED); + err = dtr_uart_disable(); + if (err) { + LOG_ERR("Failed to disable DTR UART (%d).", err); + } - indicate_pin_disable(); + err = gpio_pin_configure_dt(&dtr_config.dtr_gpio, GPIO_DISCONNECTED); + if (err) { + LOG_ERR("Failed to disconnect DTR pin: %d", err); + } data_handler = NULL; - ind_handler = NULL; - ind_handler_backup = NULL; + ri_handler = NULL; sm_at_state = AT_CMD_OK; return 0; } -int sm_host_register_ind(sm_ind_handler_t handler, bool wakeup) +int sm_host_register_ri_handler(sm_ri_handler_t handler) { -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) - if (ind_handler != NULL) { - ind_handler_backup = ind_handler; - } - ind_handler = handler; - - if (wakeup) { - /* - * Due to errata 4, Always configure PIN_CNF[n].INPUT before PIN_CNF[n].SENSE. - * At this moment indicate pin has already been configured as INPUT at init_gpio(). - */ - nrf_gpio_cfg_sense_set(CONFIG_SM_HOST_INDICATE_PIN, NRF_GPIO_PIN_SENSE_LOW); - } - - return 0; -#else - return -EFAULT; -#endif -} - -int sm_host_power_pin_toggle(void) -{ - int err; - - if (k_work_delayable_is_pending(&gpio_power_pin_disable_work)) { - return 0; + if (!gpio_is_ready_dt(&dtr_config.ri_gpio)) { + LOG_WRN("RI GPIO is not ready"); + return -EFAULT; } - LOG_INF("Enable power pin"); - - err = gpio_pin_set(gpio_dev, CONFIG_SM_HOST_POWER_PIN, 1); - if (err) { - LOG_ERR("GPIO set error: %d", err); - } else { - k_work_reschedule( - &gpio_power_pin_disable_work, - K_MSEC(CONFIG_SM_HOST_POWER_PIN_TIME)); - } + ri_handler = handler; return 0; } @@ -681,11 +896,6 @@ int sm_host_send_cmd(const char *const command, uint32_t timeout) { int ret; - /* Enable indicate pin when command is sent. This should not be needed but is made - * currently to make sure it wouldn't be disabled forever. - */ - (void)indicate_pin_enable(); - sm_at_state = AT_CMD_PENDING; ret = tx_write(command, strlen(command), false); if (ret < 0) { @@ -720,6 +930,31 @@ int sm_host_send_data(const uint8_t *const data, size_t datalen) return tx_write(data, datalen, true); } +void sm_host_configure_dtr_uart(bool automatic, k_timeout_t inactivity) +{ + dtr_config.automatic = automatic; + dtr_config.inactivity = inactivity; + + if (dtr_config.automatic && !dtr_config.active && !ring_buf_is_empty(&tx_buf)) { + /* If automatic DTR UART is enabled and there is data to send, enable DTR UART. */ + k_work_submit(&dtr_config.dtr_uart_enable_work); + } else { + reschedule_disable(); + } +} + +void sm_host_disable_dtr_uart(void) +{ + sm_host_configure_dtr_uart(false, K_NO_WAIT); + k_work_reschedule(&dtr_config.dtr_uart_disable_work, K_NO_WAIT); +} + +void sm_host_enable_dtr_uart(void) +{ + sm_host_configure_dtr_uart(false, K_NO_WAIT); + k_work_submit(&dtr_config.dtr_uart_enable_work); +} + #if defined(CONFIG_SM_HOST_SHELL) int sm_host_shell(const struct shell *shell, size_t argc, char **argv) @@ -732,51 +967,59 @@ int sm_host_shell(const struct shell *shell, size_t argc, char **argv) return sm_host_send_cmd(argv[1], 10); } -int sm_host_shell_smsh_powerpin(const struct shell *shell, size_t argc, char **argv) +int sm_host_shell_smsh_dtr_uart_disable(const struct shell *shell, size_t argc, char **argv) { - int err; + shell_print(shell, "Disable DTR UART."); + sm_host_disable_dtr_uart(); - err = sm_host_power_pin_toggle(); - if (err) { - LOG_ERR("Failed to toggle power pin"); - } return 0; } -int sm_host_shell_smsh_indicate_enable(const struct shell *shell, size_t argc, char **argv) +int sm_host_shell_smsh_dtr_uart_enable(const struct shell *shell, size_t argc, char **argv) { - LOG_INF("Enable indicate pin callback"); - (void)sm_host_register_ind(ind_handler_backup, true); - (void)indicate_pin_enable(); + shell_print(shell, "Enable DTR UART."); + sm_host_enable_dtr_uart(); + return 0; } -int sm_host_shell_smsh_indicate_disable(const struct shell *shell, size_t argc, char **argv) +int sm_host_shell_smsh_dtr_uart_auto(const struct shell *shell, size_t argc, char **argv) { - LOG_INF("Disable indicate pin callback"); - /* indicate_pin_disable() is not called so we get one indication where we just log - * a warning that indications are not coming and then disable the indication pin. - */ - return sm_host_register_ind(NULL, true); + uint32_t timeout = 0; + + if (argc >= 2) { + timeout = strtoul(argv[1], NULL, 10); + } + if (timeout == 0) { + shell_print(shell, "Usage: smsh uart auto "); + return -EINVAL; + } + + sm_host_configure_dtr_uart(true, K_MSEC(timeout)); + + shell_print(shell, "Automatic DTR UART. Inactivity timeout %u ms", timeout); + + return 0; } SHELL_CMD_REGISTER(sm, NULL, "Send AT commands to Serial Modem device", sm_host_shell); SHELL_STATIC_SUBCMD_SET_CREATE( - sub_indicate, - SHELL_CMD(enable, NULL, "Enable/disable indicate pin callback", - sm_host_shell_smsh_indicate_enable), - SHELL_CMD(disable, NULL, "Disable indicate pin callback", - sm_host_shell_smsh_indicate_disable), - SHELL_SUBCMD_SET_END -); + sub_uart, + SHELL_CMD(auto, NULL, + "[]\n" + "(Default) Automatically enable DTR UART from RI. Disable DTR UART after " + "inactivity period (default value is 100ms).", + sm_host_shell_smsh_dtr_uart_auto), + SHELL_CMD(enable, NULL, "Enable DTR UART. Disable automatic handling.", + sm_host_shell_smsh_dtr_uart_enable), + SHELL_CMD(disable, NULL, "Disable DTR UART. Disable automatic handling.", + sm_host_shell_smsh_dtr_uart_disable), + SHELL_SUBCMD_SET_END); SHELL_STATIC_SUBCMD_SET_CREATE( sub_smsh, - SHELL_CMD(powerpin, NULL, "Toggle power pin configured with CONFIG_SM_HOST_POWER_PIN", - sm_host_shell_smsh_powerpin), - SHELL_CMD(indicate, &sub_indicate, "Enable/disable indicate pin callback", - NULL), + SHELL_CMD(uart, &sub_uart, "Enable/Disable DTR UART.", NULL), SHELL_SUBCMD_SET_END ); diff --git a/samples/sm_host_shell/boards/nrf52840dk_nrf52840.conf b/samples/sm_host_shell/boards/nrf52840dk_nrf52840.conf index 3153f246..133e0a13 100644 --- a/samples/sm_host_shell/boards/nrf52840dk_nrf52840.conf +++ b/samples/sm_host_shell/boards/nrf52840dk_nrf52840.conf @@ -11,5 +11,3 @@ CONFIG_NRFX_UARTE1=y CONFIG_UART_1_INTERRUPT_DRIVEN=n CONFIG_UART_1_ASYNC=y -CONFIG_SM_HOST_POWER_PIN=11 -CONFIG_SM_HOST_INDICATE_PIN=13 diff --git a/samples/sm_host_shell/boards/nrf52840dk_nrf52840.overlay b/samples/sm_host_shell/boards/nrf52840dk_nrf52840.overlay index 1836a44a..7be3d8a9 100644 --- a/samples/sm_host_shell/boards/nrf52840dk_nrf52840.overlay +++ b/samples/sm_host_shell/boards/nrf52840dk_nrf52840.overlay @@ -7,7 +7,6 @@ / { chosen { ncs,sm-uart = &uart1; - ncs,sm-gpio = &gpio0; }; }; @@ -29,6 +28,22 @@ pinctrl-names = "default", "sleep"; }; +/* DTR gpios for uart1 */ +/ { + dte_dtr: dte_dtr { + compatible = "nordic,dte-dtr"; + dtr-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; + ri-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (11) and RI (13) pins to ensure lower power consumption. */ + sense-edge-mask = <0x00002800>; +}; + + &pinctrl { uart1_default: uart1_default { group1 { diff --git a/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.conf index b9778776..16122e6f 100644 --- a/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -11,5 +11,3 @@ CONFIG_NRFX_UARTE2=y CONFIG_UART_2_INTERRUPT_DRIVEN=n CONFIG_UART_2_ASYNC=y -CONFIG_SM_HOST_POWER_PIN=23 -CONFIG_SM_HOST_INDICATE_PIN=28 diff --git a/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.overlay index 1ec13be6..988fb3a7 100644 --- a/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/sm_host_shell/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -7,7 +7,6 @@ / { chosen { ncs,sm-uart = &uart2; - ncs,sm-gpio = &gpio0; }; }; @@ -28,6 +27,22 @@ pinctrl-names = "default", "sleep"; }; +/* DTR gpios for uart2 */ +/ { + dte_dtr: dte_dtr { + compatible = "nordic,dte-dtr"; + dtr-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + ri-gpios = <&gpio0 25 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (26) and RI (25) pins to ensure lower power consumption. */ + sense-edge-mask = <0x06000000>; +}; + + &pinctrl { uart2_default: uart2_default { group1 { diff --git a/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.conf b/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.conf index 4779a72e..44081965 100644 --- a/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.conf +++ b/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -11,5 +11,3 @@ CONFIG_NRFX_UARTE2=y CONFIG_UART_2_INTERRUPT_DRIVEN=n CONFIG_UART_2_ASYNC=y -CONFIG_SM_HOST_POWER_PIN=30 -CONFIG_SM_HOST_INDICATE_PIN=31 diff --git a/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.overlay b/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.overlay index 491bbcc9..cc81a74a 100644 --- a/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.overlay +++ b/samples/sm_host_shell/boards/nrf7002dk_nrf5340_cpuapp.overlay @@ -7,7 +7,6 @@ / { chosen { ncs,sm-uart = &uart2; - ncs,sm-gpio = &gpio0; }; }; @@ -29,6 +28,21 @@ pinctrl-names = "default", "sleep"; }; +/* DTR gpios for uart2 */ +/ { + dte_dtr: dte_dtr { + compatible = "nordic,dte-dtr"; + dtr-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; + ri-gpios = <&gpio0 31 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + }; +}; + +&gpio0 { + status = "okay"; + /* Use PORT event for DTR (30) and RI (31) pins to ensure lower power consumption. */ + sense-edge-mask = <0xC0000000>; +}; + &pinctrl { uart2_default: uart2_default { group1 { diff --git a/samples/sm_host_shell/sample.yaml b/samples/sm_host_shell/sample.yaml index 3f736be9..1a54e27d 100644 --- a/samples/sm_host_shell/sample.yaml +++ b/samples/sm_host_shell/sample.yaml @@ -17,18 +17,3 @@ tests: - ci_build - sysbuild - ci_samples_cellular - sample.cellular.sm_host_shell.no_indicate_pin: - sysbuild: true - build_only: true - extra_configs: - - CONFIG_SM_HOST_INDICATE_PIN=-1 - platform_allow: - - nrf52840dk/nrf52840 - - nrf5340dk/nrf5340/cpuapp - - nrf7002dk/nrf5340/cpuapp - integration_platforms: - - nrf5340dk/nrf5340/cpuapp - tags: - - ci_build - - sysbuild - - ci_samples_cellular diff --git a/samples/sm_host_shell/src/main.c b/samples/sm_host_shell/src/main.c index b84e7351..43a77e13 100644 --- a/samples/sm_host_shell/src/main.c +++ b/samples/sm_host_shell/src/main.c @@ -26,18 +26,10 @@ void sm_host_shell_data_indication(const uint8_t *data, size_t datalen) LOG_INF("Data received (len=%d): %.*s", datalen, datalen, (const char *)data); } -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) -void sm_host_shell_indication_handler(void) +void sm_host_shell_ri_handler(void) { - int err; - - LOG_INF("Serial Modem indicate pin triggered"); - err = sm_host_power_pin_toggle(); - if (err) { - LOG_ERR("Failed to toggle power pin"); - } + LOG_INF("Ring Indicate (RI) triggered"); } -#endif /* CONFIG_SM_HOST_INDICATE_PIN */ int main(void) { @@ -45,17 +37,15 @@ int main(void) LOG_INF("Serial Modem Host Shell starts on %s", CONFIG_BOARD); - err = sm_host_init(sm_host_shell_data_indication); + err = sm_host_init(sm_host_shell_data_indication, true, K_MSEC(100)); if (err) { LOG_ERR("Failed to initialize Serial Modem: %d", err); } -#if (CONFIG_SM_HOST_INDICATE_PIN >= 0) - err = sm_host_register_ind(sm_host_shell_indication_handler, true); + err = sm_host_register_ri_handler(sm_host_shell_ri_handler); if (err) { - LOG_ERR("Failed to register indication: %d", err); + LOG_ERR("Failed to register RI handler (%d).", err); } -#endif /* CONFIG_SM_HOST_INDICATE_PIN */ return 0; } diff --git a/zephyr/module.yml b/zephyr/module.yml index dd09778c..0a8f954c 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -9,3 +9,7 @@ build: # Path to the folder that contains the CMakeLists.txt file to be included by # Zephyr build system. The `.` is the root of this repository. cmake: . + settings: + # Path to the folder containing additional device tree source files. The + # `.` is the root of this repository. + dts_root: .