diff --git a/drivers/bluetooth/hci/hci_stm32wba.c b/drivers/bluetooth/hci/hci_stm32wba.c index 77b8ee64fa039..fc72e8394656a 100644 --- a/drivers/bluetooth/hci/hci_stm32wba.c +++ b/drivers/bluetooth/hci/hci_stm32wba.c @@ -452,7 +452,7 @@ static int bt_hci_stm32wba_open(const struct device *dev, bt_hci_recv_t recv) struct hci_data *data = dev->data; int ret = 0; - link_layer_register_isr(); + link_layer_register_isr(0); ret = bt_ble_ctlr_init(); if (ret == 0) { @@ -563,7 +563,7 @@ static int radio_pm_action(const struct device *dev, enum pm_device_action actio #if defined(CONFIG_PM_S2RAM) if (LL_PWR_IsActiveFlag_SB() == 1U) { /* Put the radio in active state */ - link_layer_register_isr(); + link_layer_register_isr(1); } #endif /* CONFIG_PM_S2RAM */ LINKLAYER_PLAT_NotifyWFIExit(); diff --git a/drivers/ieee802154/ieee802154_stm32wba.c b/drivers/ieee802154/ieee802154_stm32wba.c index 5f1714a213cc4..0b8ad2dbefb3d 100644 --- a/drivers/ieee802154/ieee802154_stm32wba.c +++ b/drivers/ieee802154/ieee802154_stm32wba.c @@ -686,7 +686,7 @@ static void stm32wba_802154_iface_init(struct net_if *iface) .stm32wba_802154_ral_cbk_tx_ack_started = stm32wba_802154_tx_ack_started, }; - link_layer_register_isr(); + link_layer_register_isr(0); #if !defined(CONFIG_NET_L2_CUSTOM_IEEE802154_STM32WBA) ll_sys_thread_init(); @@ -995,7 +995,7 @@ static int radio_pm_action(const struct device *dev, enum pm_device_action actio if (LL_PWR_IsActiveFlag_SB() == 1U) { /* Put the radio in active state */ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_RADIO); - link_layer_register_isr(); + link_layer_register_isr(1); } LINKLAYER_PLAT_NotifyWFIExit(); ll_sys_dp_slp_exit(); diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/CMakeLists.txt b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/CMakeLists.txt new file mode 100644 index 0000000000000..1c5567173a63e --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/CMakeLists.txt @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sockets_echo_client) + +if(CONFIG_NET_SOCKETS_SOCKOPT_TLS AND + CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED AND + (CONFIG_NET_SAMPLE_PSK_HEADER_FILE STREQUAL "dummy_psk.h")) + add_custom_target(development_psk + COMMAND ${CMAKE_COMMAND} -E echo "----------------------------------------------------------" + COMMAND ${CMAKE_COMMAND} -E echo "--- WARNING: Using dummy PSK! Only suitable for ---" + COMMAND ${CMAKE_COMMAND} -E echo "--- development. Set NET_SAMPLE_PSK_HEADER_FILE to use ---" + COMMAND ${CMAKE_COMMAND} -E echo "--- own pre-shared key. ---" + COMMAND ${CMAKE_COMMAND} -E echo "----------------------------------------------------------" + ) + add_dependencies(app development_psk) +endif() + +target_sources( app PRIVATE src/echo-client.c) +target_sources( app PRIVATE src/app_ble.c) +target_sources_ifdef(CONFIG_NET_UDP app PRIVATE src/udp.c) +target_sources_ifdef(CONFIG_NET_TCP app PRIVATE src/tcp.c) + +include(${ZEPHYR_BASE}/samples/net/common/common.cmake) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +generate_inc_file_for_target( + app + src/echo-apps-cert.der + ${gen_dir}/echo-apps-cert.der.inc + ) diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/Kconfig b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/Kconfig new file mode 100644 index 0000000000000..ad1bb05c6485f --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/Kconfig @@ -0,0 +1,61 @@ +# Private config options for ble HR - Networking echo-client sample app + +# Copyright (c) 2018 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +mainmenu "Ble Heart Rate - Networking echo-client sample application" + +config NET_SAMPLE_IFACE2_MY_IPV6_ADDR + string "My IPv6 address for second interface" + help + The value depends on your network setup. + +config NET_SAMPLE_IFACE2_MY_IPV4_ADDR + string "My IPv4 address for second interface" + help + The value depends on your network setup. + +config NET_SAMPLE_IFACE2_VLAN_TAG + int "VLAN tag for second interface" + default 100 + range 0 4094 + depends on NET_VLAN + help + Set VLAN (virtual LAN) tag (id) that is used in the sample + application. + +config NET_SAMPLE_IFACE3_MY_IPV6_ADDR + string "My IPv6 address for third interface" + help + The value depends on your network setup. + +config NET_SAMPLE_IFACE3_MY_IPV4_ADDR + string "My IPv4 address for third interface" + help + The value depends on your network setup. + +config NET_SAMPLE_IFACE3_VLAN_TAG + int "VLAN tag for third interface" + default 200 + range 0 4094 + depends on NET_VLAN + help + Set VLAN (virtual LAN) tag (id) that is used in the sample + application. + +config NET_SAMPLE_PSK_HEADER_FILE + string "Header file containing PSK" + default "dummy_psk.h" + depends on MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + help + Name of a header file containing a + pre-shared key. + +config NET_SAMPLE_SEND_ITERATIONS + int "Send sample data this many times" + default 0 + help + Send sample data this many times before exiting. A value of + zero means that the sample application is run forever. + +source "Kconfig.zephyr" diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/README.rst b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/README.rst new file mode 100644 index 0000000000000..878c192bfdb2b --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/README.rst @@ -0,0 +1,68 @@ +.. zephyr:code-sample:: stm32_ble_hr_802154_echo_client + :name: Ble HR - 802154 Echo client (advanced) + + Implement a Bluetooth Heart Rate - 802154 client. + The Bluetooth Heart Rate (HR) GATT Service generates dummy heart-rate + values and the 802154 echo client sends IP packets, waits for data + to be sent back, and verifies it. + +Overview +******** + +The sample application for Zephyr implements a concurrent mode +BLE - IEEE802.15.4. +The 802.15.4 part implements UDP/TCP client that will send IPv4 +or IPv6 packets, wait for the data to be sent back, and then verify +it matches the data that was sent. +The BLE part exposes the HR (Heart Rate) GATT Service. Once a device +connects it will generate dummy heart-rate values. + +The source code for this sample application can be found at: +:zephyr_file:`samples/boards/st/ble_802154/ble_hr_802154_echo_client`. + +Requirements +************ + +- :ref:`networking_with_host` +* BlueZ running on the host, or +* A board with Bluetooth LE support + +Building and Running +******************** + + +There are configuration files for different boards and setups in the +samplet directory: + +- :file:`prj.conf` + Generic config file, normally you should use this. + +- :file:`overlay-802154.conf` + This overlay config enables support for native IEEE 802.15.4 connectivity. + Note, that by default IEEE 802.15.4 L2 uses unacknowledged communication. To + improve connection reliability, acknowledgments can be enabled with shell + command: ``ieee802154 ack set``. + +Build sample application like this: + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/st/ble_802154/ble_hr_802154_echo_client + :board: + :conf: + :goals: build + :compact: + +Example building for the IEEE 802.15.4 on nucleo_wba65ri: + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/st/ble_802154/ble_hr_802154_echo_client + :board: nucleo_wba65ri + :gen-args: -DEXTRA_CONF_FILE=overlay-802154.conf + :goals: build + :compact: + +In a terminal window you can check if communication is happen: + +.. code-block:: console + + $ minicom -D /dev/ttyACM1 diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/docker-test.sh b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/docker-test.sh new file mode 100644 index 0000000000000..a26deebadb4bf --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/docker-test.sh @@ -0,0 +1,18 @@ +# Copyright (c) 2020 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +if [ -z "$RUNNING_FROM_MAIN_SCRIPT" ]; then + echo "Do not run this script directly!" + echo "Run $ZEPHYR_BASE/scripts/net/run-sample-tests.sh instead." + exit 1 +fi + +start_configuration "--ip=192.0.2.1 --ip6=2001:db8::1" || return $? +start_docker "/net-tools/echo-server -i eth0" || return $? + +start_zephyr "$overlay" "-DCONFIG_NET_SAMPLE_SEND_ITERATIONS=10" + +wait_zephyr +result=$? + +stop_docker diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/overlay-802154.conf b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/overlay-802154.conf new file mode 100644 index 0000000000000..c5dcc2c6d9442 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/overlay-802154.conf @@ -0,0 +1,19 @@ +# Disable TCP and IPv4 (TCP disabled to avoid heavy traffic) +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n +CONFIG_NET_CONFIG_AUTO_INIT=n + +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="" +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" + +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_L2_IEEE802154_SHELL=y +CONFIG_NET_L2_IEEE802154_LOG_LEVEL_INF=y + +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=26 + +CONFIG_NET_L2_IEEE802154_FRAGMENT_REASS_CACHE_SIZE=5 diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/prj.conf b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/prj.conf new file mode 100644 index 0000000000000..43c006b3b80f2 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/prj.conf @@ -0,0 +1,65 @@ +# Generic networking options +CONFIG_NETWORKING=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_ZVFS_POLL_MAX=5 +CONFIG_NET_CONNECTION_MANAGER=y + +CONFIG_POSIX_API=y + +# Kernel options +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_INIT_STACKS=y +CONFIG_MAX_THREAD_BYTES=3 + +# Logging +CONFIG_NET_LOG=y +CONFIG_LOG=y +CONFIG_NET_STATISTICS=y +CONFIG_PRINTK=y + +# Network buffers +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 +CONFIG_NET_BUF_RX_COUNT=80 +CONFIG_NET_BUF_TX_COUNT=80 +CONFIG_NET_CONTEXT_NET_PKT_POOL=y + +# IP address options +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_IF_MAX_IPV6_COUNT=3 +CONFIG_NET_IF_MAX_IPV4_COUNT=3 + +# Network shell +CONFIG_NET_SHELL=y + +# The addresses are selected so that qemu<->qemu connectivity works ok. +# For linux<->qemu connectivity, create a new conf file and swap the +# addresses (so that peer address is ending to 2). +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.2" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.1" +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Bluetootn configuration +CONFIG_BT=y +CONFIG_BT_SMP=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DIS=y +CONFIG_BT_DIS_PNP=n +CONFIG_BT_BAS=y +CONFIG_BT_HRS=y +CONFIG_BT_DEVICE_NAME="Zephyr HR 802154 Echo Client" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB=y diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/sample.yaml b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/sample.yaml new file mode 100644 index 0000000000000..e1b0f4b82c846 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/sample.yaml @@ -0,0 +1,12 @@ +sample: + description: Test concurrent mode Ble HeartRate - IEEE802.15.4 echo client + name: Ble Heart Rate - Socket Echo Client +tests: + sample.boards.stm32.ble_802154.ble_hr_802154_echo_client: + harness: net + extra_args: EXTRA_CONF_FILE="overlay-802154.conf" + platform_allow: nucleo_wba65ri + tags: + - bluetooth + - net + - socket diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/app_ble.c b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/app_ble.c new file mode 100644 index 0000000000000..45740bf46f6c5 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/app_ble.c @@ -0,0 +1,330 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +static bool hrf_ntf_enabled; +#if defined(CONFIG_BT) +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, + BT_UUID_16_ENCODE(BT_UUID_HRS_VAL), + BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), + BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)), +#if defined(CONFIG_BT_EXT_ADV) + BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), +#endif /* CONFIG_BT_EXT_ADV */ +}; + +#if !defined(CONFIG_BT_EXT_ADV) +static const struct bt_data sd[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), +}; +#endif /* !CONFIG_BT_EXT_ADV */ + +/* Use atomic variable, 2 bits for connection and disconnection state */ +static ATOMIC_DEFINE(state, 2U); + +#define STATE_CONNECTED 1U +#define STATE_DISCONNECTED 2U + +static void process_ble_hr(void); +K_THREAD_DEFINE(ble_thread_id, UDP_STACK_SIZE, + process_ble_hr, NULL, NULL, NULL, + THREAD_PRIORITY, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0, -1); + +static void connected(struct bt_conn *conn, uint8_t err) +{ + if (err) { + printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err)); + } else { + printk("Connected\n"); + + (void)atomic_set_bit(state, STATE_CONNECTED); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason)); + + (void)atomic_set_bit(state, STATE_DISCONNECTED); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, +}; + +static void hrs_ntf_changed(bool enabled) +{ + hrf_ntf_enabled = enabled; + + printk("HRS notification status changed: %s\n", + enabled ? "enabled" : "disabled"); +} + +static struct bt_hrs_cb hrs_cb = { + .ntf_changed = hrs_ntf_changed, +}; + +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing cancelled: %s\n", addr); +} + +static struct bt_conn_auth_cb auth_cb_display = { + .cancel = auth_cancel, +}; + +static void bas_notify(void) +{ + uint8_t battery_level = bt_bas_get_battery_level(); + + battery_level--; + + if (!battery_level) { + battery_level = 100U; + } + + bt_bas_set_battery_level(battery_level); +} + +static void hrs_notify(void) +{ + static uint8_t heartrate = 90U; + + /* Heartrate measurements simulation */ + heartrate++; + if (heartrate == 160U) { + heartrate = 90U; + } + + if (hrf_ntf_enabled) { + bt_hrs_notify(heartrate); + } +} + +#if defined(CONFIG_GPIO) +/* The devicetree node identifier for the "led0" alias. */ +#define LED0_NODE DT_ALIAS(led0) + +#if DT_NODE_HAS_STATUS_OKAY(LED0_NODE) +#include +#define HAS_LED 1 +static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); +#define BLINK_ONOFF K_MSEC(500) + +static struct k_work_delayable blink_work; +static bool led_is_on; + +static void blink_timeout(struct k_work *work) +{ + led_is_on = !led_is_on; + gpio_pin_set(led.port, led.pin, (int)led_is_on); + + k_work_schedule(&blink_work, BLINK_ONOFF); +} + +static int blink_setup(void) +{ + int err; + + printk("Checking LED device..."); + if (!gpio_is_ready_dt(&led)) { + printk("failed.\n"); + return -EIO; + } + printk("done.\n"); + + printk("Configuring GPIO pin..."); + err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + if (err) { + printk("failed.\n"); + return -EIO; + } + printk("done.\n"); + + k_work_init_delayable(&blink_work, blink_timeout); + + return 0; +} + +static void blink_start(void) +{ + printk("Start blinking LED...\n"); + led_is_on = false; + gpio_pin_set(led.port, led.pin, (int)led_is_on); + k_work_schedule(&blink_work, BLINK_ONOFF); +} + +static void blink_stop(void) +{ + struct k_work_sync work_sync; + + printk("Stop blinking LED.\n"); + k_work_cancel_delayable_sync(&blink_work, &work_sync); + + /* Keep LED on */ + led_is_on = true; + gpio_pin_set(led.port, led.pin, (int)led_is_on); +} +#endif /* LED0_NODE */ +#endif /* CONFIG_GPIO */ + +static void process_ble_hr(void) +{ + int err; + /* Implement notification. */ + while (1) { + k_sleep(K_SECONDS(1)); + + /* Heartrate measurements simulation */ + hrs_notify(); + + /* Battery level simulation */ + bas_notify(); + + if (atomic_test_and_clear_bit(state, STATE_CONNECTED)) { + /* Connected callback executed */ + +#if defined(HAS_LED) + blink_stop(); +#endif /* HAS_LED */ + } else if (atomic_test_and_clear_bit(state, STATE_DISCONNECTED)) { +#if !defined(CONFIG_BT_EXT_ADV) + printk("Starting Legacy Advertising (connectable and scannable)\n"); + err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, + ARRAY_SIZE(sd)); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + break; + } + +#else /* CONFIG_BT_EXT_ADV */ + printk("Starting Extended Advertising (connectable and non-scannable)\n"); + err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err) { + printk("Failed to start extended advertising set (err %d)\n", err); + break; + } +#endif /* CONFIG_BT_EXT_ADV */ + +#if defined(HAS_LED) + blink_start(); +#endif /* HAS_LED */ + } + } +} + +int ble_init(void) +{ + int err; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + printk("Bluetooth initialized\n"); + + return 0; +} + +int start_ble_task(void) +{ + int err; + + bt_conn_auth_cb_register(&auth_cb_display); + + bt_hrs_cb_register(&hrs_cb); + +#if !defined(CONFIG_BT_EXT_ADV) + printk("Starting Legacy Advertising (connectable and scannable)\n"); + err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return 0; + } + +#else /* CONFIG_BT_EXT_ADV */ + struct bt_le_adv_param adv_param = { + .id = BT_ID_DEFAULT, + .sid = 0U, + .secondary_max_skip = 0U, + .options = (BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_CODED), + .interval_min = BT_GAP_ADV_FAST_INT_MIN_2, + .interval_max = BT_GAP_ADV_FAST_INT_MAX_2, + .peer = NULL, + }; + struct bt_le_ext_adv *adv; + + printk("Creating a Coded PHY connectable non-scannable advertising set\n"); + err = bt_le_ext_adv_create(&adv_param, NULL, &adv); + if (err) { + printk("Failed to create Coded PHY extended advertising set (err %d)\n", err); + + printk("Creating a non-Coded PHY connectable non-scannable advertising set\n"); + adv_param.options &= ~BT_LE_ADV_OPT_CODED; + err = bt_le_ext_adv_create(&adv_param, NULL, &adv); + if (err) { + printk("Failed to create extended advertising set (err %d)\n", err); + return 0; + } + } + + printk("Setting extended advertising data\n"); + err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Failed to set extended advertising data (err %d)\n", err); + return 0; + } + + printk("Starting Extended Advertising (connectable non-scannable)\n"); + err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT); + if (err) { + printk("Failed to start extended advertising set (err %d)\n", err); + return 0; + } +#endif /* CONFIG_BT_EXT_ADV */ + + printk("Advertising successfully started\n"); + +#if defined(HAS_LED) + err = blink_setup(); + if (err) { + return 0; + } + + blink_start(); +#endif /* HAS_LED */ + + k_thread_name_set(ble_thread_id, "ble_hr"); + k_thread_start(ble_thread_id); + + return 0; +} +#endif diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/ca_certificate.h b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/ca_certificate.h new file mode 100644 index 0000000000000..c2c5a68882897 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/ca_certificate.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CA_CERTIFICATE_H__ +#define __CA_CERTIFICATE_H__ + +#define CA_CERTIFICATE_TAG 1 +#define PSK_TAG 2 + +#define TLS_PEER_HOSTNAME "localhost" + +/* This is the same cert as what is found in net-tools/echo-apps-cert.pem file + */ +static const unsigned char ca_certificate[] = { +#include "echo-apps-cert.der.inc" +}; + +#if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) +#include CONFIG_NET_SAMPLE_PSK_HEADER_FILE +#endif + +#endif /* __CA_CERTIFICATE_H__ */ diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/common.h b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/common.h new file mode 100644 index 0000000000000..9d70d689648c3 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/common.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 Intel Corporation. + * Copyright (c) 2018 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "net_sample_common.h" + +/* Value of 0 will cause the IP stack to select next free port */ +#define MY_PORT 0 + +#define PEER_PORT 4242 + +/* Turn off the progress printing so that shell can be used. + * Set to true if you want to see progress output. + */ +#define PRINT_PROGRESS false + +#if defined(CONFIG_USERSPACE) +#include +extern struct k_mem_partition app_partition; +extern struct k_mem_domain app_domain; +#define APP_BMEM K_APP_BMEM(app_partition) +#define APP_DMEM K_APP_DMEM(app_partition) +#else +#define APP_BMEM +#define APP_DMEM +#endif + +#if defined(CONFIG_NET_TC_THREAD_PREEMPTIVE) +#define THREAD_PRIORITY K_PRIO_PREEMPT(8) +#else +#define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1) +#endif + +#define UDP_STACK_SIZE 2048 + +struct udp_control { + struct k_poll_signal tx_signal; + struct k_timer tx_timer; + struct k_timer rx_timer; +}; + +struct sample_data { + const char *proto; + + struct { + int sock; + uint32_t expecting; + uint32_t counter; + uint32_t mtu; + struct udp_control *ctrl; + } udp; + + struct { + int sock; + uint32_t expecting; + uint32_t received; + uint32_t counter; + } tcp; +}; + +struct configs { + struct sample_data ipv4; + struct sample_data ipv6; +}; + +#if !defined(CONFIG_NET_CONFIG_PEER_IPV4_ADDR) +#define CONFIG_NET_CONFIG_PEER_IPV4_ADDR "" +#endif + +#if !defined(CONFIG_NET_CONFIG_PEER_IPV6_ADDR) +#define CONFIG_NET_CONFIG_PEER_IPV6_ADDR "" +#endif + +extern const char lorem_ipsum[]; +extern const int ipsum_len; +extern struct configs conf; + +#if defined(CONFIG_NET_UDP) +/* init_udp initializes kernel objects, hence it has to be called from + * supervisor thread. + */ +void init_udp(void); +int start_udp(void); +int process_udp(void); +void stop_udp(void); +#else +static inline void init_udp(void) { } +static inline int start_udp(void) { return 0; } +static inline int process_udp(void) { return 0; } +static inline void stop_udp(void) { } +#endif /* defined(CONFIG_NET_UDP) */ + +int start_tcp(void); +int process_tcp(void); +void stop_tcp(void); + +#if defined(CONFIG_BT) +extern int ble_init(void); +extern int start_ble_task(void); +#else +static inline int ble_init(void) +{ + return 0; +} + +static inline int start_ble_task(void) +{ + return 0; +} +#endif diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/dummy_psk.h b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/dummy_psk.h new file mode 100644 index 0000000000000..e67107266fda4 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/dummy_psk.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DUMMY_PSK_H__ +#define __DUMMY_PSK_H__ + +static const unsigned char psk[] = {0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, +0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; +static const char psk_id[] = "PSK_identity"; + +#endif /* __DUMMY_PSK_H__ */ diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-apps-cert.der b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-apps-cert.der new file mode 100644 index 0000000000000..bfcb335e31c8c Binary files /dev/null and b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-apps-cert.der differ diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-client.c b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-client.c new file mode 100644 index 0000000000000..d1aeb08850574 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/echo-client.c @@ -0,0 +1,466 @@ +/* ble-hr-802154-echo-client.c - Ble Heart Rate - Networking echo client */ + +/* + * Copyright (c) 2017 Intel Corporation. + * Copyright (c) 2018 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The application is acting as a bluetooth peripheral heart rate and + * a 802.15.4 Networking echo-client that is run in Zephyr OS, and + * echo-server is run in the host acting as a server. + * The 802.15.4 Networking client will send either unicast or multicast + * packets to the server which will reply the packet back to the originator. + */ + +#include +LOG_MODULE_REGISTER(ble_hr_802154_echo_client_sample, LOG_LEVEL_DBG); + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_USERSPACE) +#include +K_APPMEM_PARTITION_DEFINE(app_partition); +struct k_mem_domain app_domain; +#endif + +#include "common.h" +#include "ca_certificate.h" + +#define APP_BANNER "Run echo client" + +#define INVALID_SOCK (-1) + +#define EVENT_MASK (NET_EVENT_L4_CONNECTED | \ + NET_EVENT_L4_DISCONNECTED) +#define IPV6_EVENT_MASK (NET_EVENT_IPV6_ADDR_ADD | \ + NET_EVENT_IPV6_ADDR_DEPRECATED) + +const char lorem_ipsum[] = LOREM_IPSUM; + +const int ipsum_len = sizeof(lorem_ipsum) - 1; + +APP_DMEM struct configs conf = { + .ipv4 = { + .proto = "IPv4", + .udp.sock = INVALID_SOCK, + .tcp.sock = INVALID_SOCK, + }, + .ipv6 = { + .proto = "IPv6", + .udp.sock = INVALID_SOCK, + .tcp.sock = INVALID_SOCK, + }, +}; + +static APP_BMEM struct pollfd fds[1 + 4]; +static APP_BMEM int nfds; + +static APP_BMEM bool connected; +static APP_BMEM bool need_restart; + +K_SEM_DEFINE(run_app, 0, 1); + +static struct net_mgmt_event_callback mgmt_cb; +static struct net_mgmt_event_callback ipv6_mgmt_cb; + +static void prepare_fds(void) +{ + nfds = 0; + + /* eventfd is used to trigger restart */ + fds[nfds].fd = eventfd(0, 0); + fds[nfds].events = POLLIN; + nfds++; + + if (conf.ipv4.udp.sock >= 0) { + fds[nfds].fd = conf.ipv4.udp.sock; + fds[nfds].events = POLLIN; + nfds++; + } + + if (conf.ipv4.tcp.sock >= 0) { + fds[nfds].fd = conf.ipv4.tcp.sock; + fds[nfds].events = POLLIN; + nfds++; + } + + if (conf.ipv6.udp.sock >= 0) { + fds[nfds].fd = conf.ipv6.udp.sock; + fds[nfds].events = POLLIN; + nfds++; + } + + if (conf.ipv6.tcp.sock >= 0) { + fds[nfds].fd = conf.ipv6.tcp.sock; + fds[nfds].events = POLLIN; + nfds++; + } +} + +static void wait(void) +{ + int ret; + + /* Wait for event on any socket used. Once event occurs, + * we'll check them all. + */ + ret = poll(fds, nfds, -1); + if (ret < 0) { + static bool once; + + if (!once) { + once = true; + LOG_ERR("Error in poll:%d", errno); + } + + return; + } + + if (ret > 0 && fds[0].revents) { + eventfd_t value; + + eventfd_read(fds[0].fd, &value); + LOG_DBG("Received restart event."); + return; + } +} + +static int start_udp_and_tcp(void) +{ + int ret; + + LOG_INF("Starting..."); + + if (IS_ENABLED(CONFIG_NET_TCP)) { + ret = start_tcp(); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_UDP)) { + ret = start_udp(); + if (ret < 0) { + return ret; + } + } + + prepare_fds(); + + return 0; +} + +static int run_udp_and_tcp(void) +{ + int ret; + + wait(); + + if (IS_ENABLED(CONFIG_NET_TCP)) { + ret = process_tcp(); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_UDP)) { + ret = process_udp(); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static void stop_udp_and_tcp(void) +{ + LOG_INF("Stopping..."); + + if (IS_ENABLED(CONFIG_NET_UDP)) { + stop_udp(); + } + + if (IS_ENABLED(CONFIG_NET_TCP)) { + stop_tcp(); + } +} + +static int check_our_ipv6_sockets(int sock, + struct in6_addr *deprecated_addr) +{ + struct sockaddr_in6 addr = { 0 }; + socklen_t addrlen = sizeof(addr); + int ret; + + if (sock < 0) { + return -EINVAL; + } + + ret = getsockname(sock, (struct sockaddr *)&addr, &addrlen); + if (ret != 0) { + return -errno; + } + + if (!net_ipv6_addr_cmp(deprecated_addr, &addr.sin6_addr)) { + return -ENOENT; + } + + need_restart = true; + + return 0; +} + +static void ipv6_event_handler(struct net_mgmt_event_callback *cb, + uint64_t mgmt_event, struct net_if *iface) +{ + static char addr_str[INET6_ADDRSTRLEN]; + + if (!IS_ENABLED(CONFIG_NET_IPV6_PE)) { + return; + } + + if ((mgmt_event & IPV6_EVENT_MASK) != mgmt_event) { + return; + } + + if (cb->info == NULL || + cb->info_length != sizeof(struct in6_addr)) { + return; + } + + if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) { + struct net_if_addr *ifaddr; + struct in6_addr added_addr; + + memcpy(&added_addr, cb->info, sizeof(struct in6_addr)); + + ifaddr = net_if_ipv6_addr_lookup(&added_addr, &iface); + if (ifaddr == NULL) { + return; + } + + /* Wait until we get a temporary address before continuing after + * boot. + */ + if (ifaddr->is_temporary) { + static bool once; + + LOG_INF("Temporary IPv6 address %s added", + inet_ntop(AF_INET6, &added_addr, addr_str, + sizeof(addr_str) - 1)); + + if (!once) { + k_sem_give(&run_app); + once = true; + } + } + } + + if (mgmt_event == NET_EVENT_IPV6_ADDR_DEPRECATED) { + struct in6_addr deprecated_addr; + + memcpy(&deprecated_addr, cb->info, sizeof(struct in6_addr)); + + LOG_INF("IPv6 address %s deprecated", + inet_ntop(AF_INET6, &deprecated_addr, addr_str, + sizeof(addr_str) - 1)); + + (void)check_our_ipv6_sockets(conf.ipv6.tcp.sock, + &deprecated_addr); + (void)check_our_ipv6_sockets(conf.ipv6.udp.sock, + &deprecated_addr); + + if (need_restart) { + eventfd_write(fds[0].fd, 1); + } + + return; + } +} + +static void event_handler(struct net_mgmt_event_callback *cb, + uint64_t mgmt_event, struct net_if *iface) +{ + if ((mgmt_event & EVENT_MASK) != mgmt_event) { + return; + } + + if (mgmt_event == NET_EVENT_L4_CONNECTED) { + LOG_INF("Network connected"); + + connected = true; + conf.ipv4.udp.mtu = net_if_get_mtu(iface); + conf.ipv6.udp.mtu = conf.ipv4.udp.mtu; + + if (!IS_ENABLED(CONFIG_NET_IPV6_PE)) { + k_sem_give(&run_app); + } + + return; + } + + if (mgmt_event == NET_EVENT_L4_DISCONNECTED) { + LOG_INF("Network disconnected"); + + connected = false; + k_sem_reset(&run_app); + + return; + } +} + +static void init_app(void) +{ + LOG_INF(APP_BANNER); + + (void)net_config_init_app(NULL, "Initializing network"); +#if defined(CONFIG_USERSPACE) + struct k_mem_partition *parts[] = { +#if Z_LIBC_PARTITION_EXISTS + &z_libc_partition, +#endif + &app_partition + }; + + int ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts); + + __ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret); + ARG_UNUSED(ret); +#endif + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + int err = tls_credential_add(CA_CERTIFICATE_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + ca_certificate, + sizeof(ca_certificate)); + if (err < 0) { + LOG_ERR("Failed to register public certificate: %d", err); + } + +#if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + err = tls_credential_add(PSK_TAG, + TLS_CREDENTIAL_PSK, + psk, + sizeof(psk)); + if (err < 0) { + LOG_ERR("Failed to register PSK: %d", err); + } + err = tls_credential_add(PSK_TAG, + TLS_CREDENTIAL_PSK_ID, + psk_id, + sizeof(psk_id) - 1); + if (err < 0) { + LOG_ERR("Failed to register PSK ID: %d", err); + } +#endif /* defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) */ +#endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */ + + + if (IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) { + net_mgmt_init_event_callback(&mgmt_cb, + event_handler, EVENT_MASK); + net_mgmt_add_event_callback(&mgmt_cb); + + conn_mgr_mon_resend_status(); + } + + net_mgmt_init_event_callback(&ipv6_mgmt_cb, + ipv6_event_handler, IPV6_EVENT_MASK); + net_mgmt_add_event_callback(&ipv6_mgmt_cb); + + init_vlan(); + init_udp(); +} + +static void start_client(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + int iterations = CONFIG_NET_SAMPLE_SEND_ITERATIONS; + int i = 0; + int ret; + + while (iterations == 0 || i < iterations) { + /* Wait for the connection. */ + k_sem_take(&run_app, K_FOREVER); + + if (IS_ENABLED(CONFIG_NET_IPV6_PE)) { + /* Make sure that we have a temporary address */ + k_sleep(K_SECONDS(1)); + } + + do { + if (need_restart) { + /* Close all sockets and get a fresh restart */ + stop_udp_and_tcp(); + need_restart = false; + } + + ret = start_udp_and_tcp(); + + while (connected && (ret == 0)) { + ret = run_udp_and_tcp(); + + if (iterations > 0) { + i++; + if (i >= iterations) { + break; + } + } + + if (need_restart) { + break; + } + } + } while (need_restart); + + stop_udp_and_tcp(); + } +} + +int main(void) +{ + ble_init(); + init_app(); + start_ble_task(); + + if (!IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) { + /* If the config library has not been configured to start the + * app only after we have a connection, then we can start + * it right away. + */ + connected = true; + k_sem_give(&run_app); + } + + k_thread_priority_set(k_current_get(), THREAD_PRIORITY); + +#if defined(CONFIG_USERSPACE) + k_thread_access_grant(k_current_get(), &run_app); + k_mem_domain_add_thread(&app_domain, k_current_get()); + + k_thread_user_mode_enter(start_client, NULL, NULL, NULL); +#else + start_client(NULL, NULL, NULL); +#endif + return 0; +} diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/tcp.c b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/tcp.c new file mode 100644 index 0000000000000..5c8da99c05715 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/tcp.c @@ -0,0 +1,315 @@ +/* tcp.c - TCP specific code for echo client */ + +/* + * Copyright (c) 2017 Intel Corporation. + * Copyright (c) 2018 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(ble_hr_802154_echo_client_sample, LOG_LEVEL_DBG); + +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "ca_certificate.h" + +#define RECV_BUF_SIZE 128 + +/* These proxy server addresses are only used when CONFIG_SOCKS + * is enabled. To connect to a proxy server that is not running + * under the same IP as the peer or uses a different port number, + * modify the values. + */ +#define SOCKS5_PROXY_V6_ADDR CONFIG_NET_CONFIG_PEER_IPV6_ADDR +#define SOCKS5_PROXY_V4_ADDR CONFIG_NET_CONFIG_PEER_IPV4_ADDR +#define SOCKS5_PROXY_PORT 1080 + +static ssize_t sendall(int sock, const void *buf, size_t len) +{ + while (len) { + ssize_t out_len = send(sock, buf, len, 0); + + if (out_len < 0) { + return out_len; + } + buf = (const char *)buf + out_len; + len -= out_len; + } + + return 0; +} + +static int send_tcp_data(struct sample_data *data) +{ + int ret; + + do { + data->tcp.expecting = sys_rand32_get() % ipsum_len; + } while (data->tcp.expecting == 0U); + + data->tcp.received = 0U; + + ret = sendall(data->tcp.sock, lorem_ipsum, data->tcp.expecting); + + if (ret < 0) { + LOG_ERR("%s TCP: Failed to send data, errno %d", data->proto, + errno); + } else { + if (PRINT_PROGRESS) { + LOG_DBG("%s TCP: Sent %d bytes", data->proto, + data->tcp.expecting); + } + } + + return ret; +} + +static int compare_tcp_data(struct sample_data *data, const char *buf, uint32_t received) +{ + if (data->tcp.received + received > data->tcp.expecting) { + LOG_ERR("Too much data received: TCP %s", data->proto); + return -EIO; + } + + if (memcmp(buf, lorem_ipsum + data->tcp.received, received) != 0) { + LOG_ERR("Invalid data received: TCP %s", data->proto); + return -EIO; + } + + return 0; +} + +static int start_tcp_proto(struct sample_data *data, sa_family_t family, + struct sockaddr *addr, socklen_t addrlen) +{ + int optval; + int ret; + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + data->tcp.sock = socket(family, SOCK_STREAM, IPPROTO_TLS_1_2); +#else + data->tcp.sock = socket(family, SOCK_STREAM, IPPROTO_TCP); +#endif + if (data->tcp.sock < 0) { + LOG_ERR("Failed to create TCP socket (%s): %d", data->proto, + errno); + return -errno; + } + + if (IS_ENABLED(CONFIG_SOCKS)) { + struct sockaddr proxy_addr; + socklen_t proxy_addrlen; + + if (family == AF_INET) { + struct sockaddr_in *proxy4 = + (struct sockaddr_in *)&proxy_addr; + + proxy4->sin_family = AF_INET; + proxy4->sin_port = htons(SOCKS5_PROXY_PORT); + inet_pton(AF_INET, SOCKS5_PROXY_V4_ADDR, + &proxy4->sin_addr); + proxy_addrlen = sizeof(struct sockaddr_in); + } else if (family == AF_INET6) { + struct sockaddr_in6 *proxy6 = + (struct sockaddr_in6 *)&proxy_addr; + + proxy6->sin6_family = AF_INET6; + proxy6->sin6_port = htons(SOCKS5_PROXY_PORT); + inet_pton(AF_INET6, SOCKS5_PROXY_V6_ADDR, + &proxy6->sin6_addr); + proxy_addrlen = sizeof(struct sockaddr_in6); + } else { + return -EINVAL; + } + + ret = setsockopt(data->tcp.sock, SOL_SOCKET, SO_SOCKS5, + &proxy_addr, proxy_addrlen); + if (ret < 0) { + return ret; + } + } + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t sec_tag_list[] = { + CA_CERTIFICATE_TAG, +#if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + PSK_TAG, +#endif + }; + + ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_list, sizeof(sec_tag_list)); + if (ret < 0) { + LOG_ERR("Failed to set TLS_SEC_TAG_LIST option (%s): %d", + data->proto, errno); + ret = -errno; + } + + ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_HOSTNAME, + TLS_PEER_HOSTNAME, sizeof(TLS_PEER_HOSTNAME)); + if (ret < 0) { + LOG_ERR("Failed to set TLS_HOSTNAME option (%s): %d", + data->proto, errno); + ret = -errno; + } +#endif + + /* Prefer IPv6 temporary addresses */ + if (family == AF_INET6) { + optval = IPV6_PREFER_SRC_TMP; + (void)setsockopt(data->tcp.sock, IPPROTO_IPV6, + IPV6_ADDR_PREFERENCES, + &optval, sizeof(optval)); + } + + ret = connect(data->tcp.sock, addr, addrlen); + if (ret < 0) { + LOG_ERR("Cannot connect to TCP remote (%s): %d", data->proto, + errno); + ret = -errno; + } + + return ret; +} + +static int process_tcp_proto(struct sample_data *data) +{ + int ret, received; + char buf[RECV_BUF_SIZE]; + + do { + received = recv(data->tcp.sock, buf, sizeof(buf), MSG_DONTWAIT); + + /* No data or error. */ + if (received == 0) { + ret = -EIO; + continue; + } else if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = 0; + } else { + ret = -errno; + } + continue; + } + + ret = compare_tcp_data(data, buf, received); + if (ret != 0) { + break; + } + + /* Successful comparison. */ + data->tcp.received += received; + if (data->tcp.received < data->tcp.expecting) { + continue; + } + + if (PRINT_PROGRESS) { + /* Response complete */ + LOG_DBG("%s TCP: Received and compared %d bytes, all ok", + data->proto, data->tcp.received); + } + + if (++data->tcp.counter % 1000 == 0U) { + LOG_INF("%s TCP: Exchanged %u packets", data->proto, + data->tcp.counter); + } + + ret = send_tcp_data(data); + break; + } while (received > 0); + + return ret; +} + +int start_tcp(void) +{ + int ret = 0; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(PEER_PORT); + inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, + &addr6.sin6_addr); + + ret = start_tcp_proto(&conf.ipv6, AF_INET6, + (struct sockaddr *)&addr6, + sizeof(addr6)); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + addr4.sin_family = AF_INET; + addr4.sin_port = htons(PEER_PORT); + inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, + &addr4.sin_addr); + + ret = start_tcp_proto(&conf.ipv4, AF_INET, + (struct sockaddr *)&addr4, + sizeof(addr4)); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + ret = send_tcp_data(&conf.ipv6); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + ret = send_tcp_data(&conf.ipv4); + } + + return ret; +} + +int process_tcp(void) +{ + int ret = 0; + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + ret = process_tcp_proto(&conf.ipv6); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + ret = process_tcp_proto(&conf.ipv4); + if (ret < 0) { + return ret; + } + } + + return ret; +} + +void stop_tcp(void) +{ + if (IS_ENABLED(CONFIG_NET_IPV6)) { + if (conf.ipv6.tcp.sock >= 0) { + (void)close(conf.ipv6.tcp.sock); + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + if (conf.ipv4.tcp.sock >= 0) { + (void)close(conf.ipv4.tcp.sock); + } + } +} diff --git a/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/udp.c b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/udp.c new file mode 100644 index 0000000000000..567f9673990e4 --- /dev/null +++ b/samples/boards/st/ble_802154/ble_hr_802154_echo_client/src/udp.c @@ -0,0 +1,400 @@ +/* udp.c - UDP specific code for echo client */ + +/* + * Copyright (c) 2017 Intel Corporation. + * Copyright (c) 2018 Nordic Semiconductor ASA. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(ble_hr_802154_echo_client_sample, LOG_LEVEL_DBG); + +#include +#include +#include + +#include +#include +#include + +#include "common.h" +#include "ca_certificate.h" + +#define RECV_BUF_SIZE 1280 +#define UDP_SLEEP K_MSEC(150) +#define UDP_WAIT K_SECONDS(10) + +static APP_BMEM char recv_buf[RECV_BUF_SIZE]; + +static K_THREAD_STACK_DEFINE(udp_tx_thread_stack, UDP_STACK_SIZE); +static struct k_thread udp_tx_thread; + +/* Kernel objects should not be placed in a memory area accessible from user + * threads. + */ +static struct udp_control udp4_ctrl, udp6_ctrl; +static struct k_poll_signal udp_kill; + +static int send_udp_data(struct sample_data *data); +static void wait_reply(struct k_timer *timer); +static void wait_transmit(struct k_timer *timer); + +static void process_udp_tx(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct k_poll_event events[] = { + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &udp_kill), +#if defined(CONFIG_NET_IPV4) + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &udp4_ctrl.tx_signal), +#endif +#if defined(CONFIG_NET_IPV6) + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &udp6_ctrl.tx_signal), +#endif + }; + + while (true) { + k_poll(events, ARRAY_SIZE(events), K_FOREVER); + + for (int i = 0; i < ARRAY_SIZE(events); i++) { + unsigned int signaled; + int result; + + k_poll_signal_check(events[i].signal, &signaled, &result); + if (signaled == 0) { + continue; + } + + k_poll_signal_reset(events[i].signal); + events[i].state = K_POLL_STATE_NOT_READY; + + if (events[i].signal == &udp_kill) { + return; + } else if (events[i].signal == &udp4_ctrl.tx_signal) { + send_udp_data(&conf.ipv4); + } else if (events[i].signal == &udp6_ctrl.tx_signal) { + send_udp_data(&conf.ipv6); + } + } + } +} + +static void udp_control_init(struct udp_control *ctrl) +{ + k_timer_init(&ctrl->rx_timer, wait_reply, NULL); + k_timer_init(&ctrl->tx_timer, wait_transmit, NULL); + k_poll_signal_init(&ctrl->tx_signal); +} + +static void udp_control_access_grant(struct udp_control *ctrl) +{ + k_thread_access_grant(k_current_get(), + &ctrl->rx_timer, + &ctrl->tx_timer, + &ctrl->tx_signal); +} + +void init_udp(void) +{ + /* k_timer_init() is not a system call, therefore initialize kernel + * objects here. + */ + if (IS_ENABLED(CONFIG_NET_IPV4)) { + udp_control_init(&udp4_ctrl); + conf.ipv4.udp.ctrl = &udp4_ctrl; + } + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + udp_control_init(&udp6_ctrl); + conf.ipv6.udp.ctrl = &udp6_ctrl; + } + + k_poll_signal_init(&udp_kill); + + if (IS_ENABLED(CONFIG_USERSPACE)) { + k_thread_access_grant(k_current_get(), + &udp_tx_thread, + &udp_tx_thread_stack, + &udp_kill); + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + udp_control_access_grant(&udp4_ctrl); + } + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + udp_control_access_grant(&udp6_ctrl); + } + } +} + +static int send_udp_data(struct sample_data *data) +{ + int ret; + + do { + data->udp.expecting = sys_rand32_get() % ipsum_len; + } while (data->udp.expecting == 0U || + data->udp.expecting > data->udp.mtu); + + ret = send(data->udp.sock, lorem_ipsum, data->udp.expecting, 0); + + if (PRINT_PROGRESS) { + LOG_DBG("%s UDP: Sent %d bytes", data->proto, data->udp.expecting); + } + + k_timer_start(&data->udp.ctrl->rx_timer, UDP_WAIT, K_NO_WAIT); + + return ret < 0 ? -EIO : 0; +} + +static int compare_udp_data(struct sample_data *data, const char *buf, uint32_t received) +{ + if (received != data->udp.expecting) { + LOG_ERR("Invalid amount of data received: UDP %s", data->proto); + return -EIO; + } + + if (memcmp(buf, lorem_ipsum, received) != 0) { + LOG_ERR("Invalid data received: UDP %s", data->proto); + return -EIO; + } + + return 0; +} + +static void wait_reply(struct k_timer *timer) +{ + /* This means that we did not receive response in time. */ + struct udp_control *ctrl = CONTAINER_OF(timer, struct udp_control, rx_timer); + struct sample_data *data = (ctrl == conf.ipv4.udp.ctrl) ? &conf.ipv4 : &conf.ipv6; + + LOG_ERR("UDP %s: Data packet not received", data->proto); + + /* Send a new packet at this point */ + k_poll_signal_raise(&ctrl->tx_signal, 0); +} + +static void wait_transmit(struct k_timer *timer) +{ + struct udp_control *ctrl = CONTAINER_OF(timer, struct udp_control, tx_timer); + + k_poll_signal_raise(&ctrl->tx_signal, 0); +} + +static int start_udp_proto(struct sample_data *data, sa_family_t family, + struct sockaddr *addr, socklen_t addrlen) +{ + int optval; + int ret; + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + data->udp.sock = socket(family, SOCK_DGRAM, IPPROTO_DTLS_1_2); +#else + data->udp.sock = socket(family, SOCK_DGRAM, IPPROTO_UDP); +#endif + if (data->udp.sock < 0) { + LOG_ERR("Failed to create UDP socket (%s): %d", data->proto, + errno); + return -errno; + } + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t sec_tag_list[] = { + CA_CERTIFICATE_TAG, +#if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + PSK_TAG, +#endif + }; + + ret = setsockopt(data->udp.sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_list, sizeof(sec_tag_list)); + if (ret < 0) { + LOG_ERR("Failed to set TLS_SEC_TAG_LIST option (%s): %d", + data->proto, errno); + ret = -errno; + } + + ret = setsockopt(data->udp.sock, SOL_TLS, TLS_HOSTNAME, + TLS_PEER_HOSTNAME, sizeof(TLS_PEER_HOSTNAME)); + if (ret < 0) { + LOG_ERR("Failed to set TLS_HOSTNAME option (%s): %d", + data->proto, errno); + ret = -errno; + } +#endif + + /* Prefer IPv6 temporary addresses */ + if (family == AF_INET6) { + optval = IPV6_PREFER_SRC_TMP; + (void)setsockopt(data->udp.sock, IPPROTO_IPV6, + IPV6_ADDR_PREFERENCES, + &optval, sizeof(optval)); + } + + /* Call connect so we can use send and recv. */ + ret = connect(data->udp.sock, addr, addrlen); + if (ret < 0) { + LOG_ERR("Cannot connect to UDP remote (%s): %d", data->proto, + errno); + ret = -errno; + } + + return ret; +} + +static int process_udp_proto(struct sample_data *data) +{ + int ret, received; + + received = recv(data->udp.sock, recv_buf, sizeof(recv_buf), + MSG_DONTWAIT); + + if (received == 0) { + return -EIO; + } + if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = 0; + } else { + ret = -errno; + } + return ret; + } + + ret = compare_udp_data(data, recv_buf, received); + if (ret != 0) { + LOG_WRN("%s UDP: Received and compared %d bytes, data " + "mismatch", data->proto, received); + return 0; + } + + if (PRINT_PROGRESS) { + /* Correct response received */ + LOG_DBG("%s UDP: Received and compared %d bytes, all ok", + data->proto, received); + } + + if (++data->udp.counter % 1000 == 0U) { + LOG_INF("%s UDP: Exchanged %u packets", data->proto, + data->udp.counter); + } + + k_timer_stop(&data->udp.ctrl->rx_timer); + + /* Do not flood the link if we have also TCP configured */ + if (IS_ENABLED(CONFIG_NET_TCP)) { + k_timer_start(&data->udp.ctrl->tx_timer, UDP_SLEEP, K_NO_WAIT); + } else { + k_poll_signal_raise(&data->udp.ctrl->tx_signal, 0); + } + + return ret; +} + +int start_udp(void) +{ + int ret = 0; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(PEER_PORT); + inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, + &addr6.sin6_addr); + + ret = start_udp_proto(&conf.ipv6, AF_INET6, + (struct sockaddr *)&addr6, + sizeof(addr6)); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + addr4.sin_family = AF_INET; + addr4.sin_port = htons(PEER_PORT); + inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, + &addr4.sin_addr); + + ret = start_udp_proto(&conf.ipv4, AF_INET, + (struct sockaddr *)&addr4, + sizeof(addr4)); + if (ret < 0) { + return ret; + } + } + + k_thread_create(&udp_tx_thread, udp_tx_thread_stack, + K_THREAD_STACK_SIZEOF(udp_tx_thread_stack), + process_udp_tx, + NULL, NULL, NULL, THREAD_PRIORITY, + IS_ENABLED(CONFIG_USERSPACE) ? + K_USER | K_INHERIT_PERMS : 0, + K_NO_WAIT); + + k_thread_name_set(&udp_tx_thread, "udp_tx"); + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + k_poll_signal_raise(&conf.ipv6.udp.ctrl->tx_signal, 0); + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + k_poll_signal_raise(&conf.ipv4.udp.ctrl->tx_signal, 0); + } + + return ret; +} + +int process_udp(void) +{ + int ret = 0; + + if (IS_ENABLED(CONFIG_NET_IPV6)) { + ret = process_udp_proto(&conf.ipv6); + if (ret < 0) { + return ret; + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + ret = process_udp_proto(&conf.ipv4); + if (ret < 0) { + return ret; + } + } + + return ret; +} + +void stop_udp(void) +{ + if (IS_ENABLED(CONFIG_NET_IPV6)) { + k_timer_stop(&udp6_ctrl.tx_timer); + k_timer_stop(&udp6_ctrl.rx_timer); + + if (conf.ipv6.udp.sock >= 0) { + (void)close(conf.ipv6.udp.sock); + } + } + + if (IS_ENABLED(CONFIG_NET_IPV4)) { + k_timer_stop(&udp4_ctrl.tx_timer); + k_timer_stop(&udp4_ctrl.rx_timer); + + if (conf.ipv4.udp.sock >= 0) { + (void)close(conf.ipv4.udp.sock); + } + } + + k_poll_signal_raise(&udp_kill, 0); +} diff --git a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c index d091af9388188..27591ae447932 100644 --- a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c +++ b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c @@ -38,6 +38,8 @@ static uint32_t primask_bit; /* Radio SW low ISR global variable */ volatile uint8_t radio_sw_low_isr_is_running_high_prio; +/* ISR registration state variable */ +static uint8_t is_isr_registered; void LINKLAYER_PLAT_DelayUs(uint32_t delay) { @@ -96,27 +98,33 @@ void radio_low_prio_isr(void) } -void link_layer_register_isr(void) +void link_layer_register_isr(uint8_t force) { - ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_INTR_NUM, 0, 0, reschedule); - /* Ensure the IRQ is disabled before enabling it at run time */ - irq_disable(RADIO_INTR_NUM); + if ((force == 1) || (is_isr_registered == 0)) { - irq_connect_dynamic(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH_Z, - (void (*)(const void *))radio_high_prio_isr, NULL, 0); + is_isr_registered = 1; - irq_enable(RADIO_INTR_NUM); + ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_INTR_NUM, 0, 0, reschedule); - ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_SW_LOW_INTR_NUM, 0, 0, reschedule); + /* Ensure the IRQ is disabled before enabling it at run time */ + irq_disable(RADIO_INTR_NUM); - /* Ensure the IRQ is disabled before enabling it at run time */ - irq_disable(RADIO_SW_LOW_INTR_NUM); + irq_connect_dynamic(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH_Z, + (void (*)(const void *))radio_high_prio_isr, NULL, 0); - irq_connect_dynamic(RADIO_SW_LOW_INTR_NUM, RADIO_SW_LOW_INTR_PRIO, - (void (*)(const void *))radio_low_prio_isr, NULL, 0); + irq_enable(RADIO_INTR_NUM); - irq_enable(RADIO_SW_LOW_INTR_NUM); + ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_SW_LOW_INTR_NUM, 0, 0, reschedule); + + /* Ensure the IRQ is disabled before enabling it at run time */ + irq_disable(RADIO_SW_LOW_INTR_NUM); + + irq_connect_dynamic(RADIO_SW_LOW_INTR_NUM, RADIO_SW_LOW_INTR_PRIO, + (void (*)(const void *))radio_low_prio_isr, NULL, 0); + + irq_enable(RADIO_SW_LOW_INTR_NUM); + } } diff --git a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_local.h b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_local.h index 1cf621b89ded0..223cf820ffb68 100644 --- a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_local.h +++ b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_local.h @@ -8,6 +8,6 @@ #ifndef _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_ #define _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_ -void link_layer_register_isr(void); +void link_layer_register_isr(uint8_t force); #endif /* _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_ */ diff --git a/west.yml b/west.yml index d5a99416892fa..38915c59f100d 100644 --- a/west.yml +++ b/west.yml @@ -250,7 +250,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 5d27023564c9fbd9bb3aa51b0529ac3d2cf18134 + revision: pull/319/head path: modules/hal/stm32 groups: - hal