| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <ztest.h> |
| |
| #include <zephyr.h> |
| #include <errno.h> |
| #include <stdio.h> |
| |
| #include <net/ieee802154_radio.h> |
| #include <net/net_pkt.h> |
| |
| #include <openthread/platform/radio.h> |
| #include <openthread/message.h> |
| #include <platform-zephyr.h> |
| |
| /** |
| * @brief Tests for the radio.c - OpenThread radio api |
| * @defgroup openthread_tests radio |
| * @ingroup all_tests |
| * @{ |
| */ |
| |
| #define ACK_PKT_LENGTH 3 |
| #define FRAME_TYPE_MASK 0x07 |
| #define FRAME_TYPE_ACK 0x02 |
| |
| K_SEM_DEFINE(ot_sem, 0, 1); |
| |
| energy_scan_done_cb_t scan_done_cb; |
| |
| /** |
| * Fake pointer as it should not be accessed by the code. |
| * Should not be null to be sure it was properly passed. |
| */ |
| otInstance *ot = (otInstance *)0xAAAA; |
| otMessage *ip_msg = (otMessage *)0xBBBB; |
| |
| /* forward declarations */ |
| static int scan_mock(const struct device *dev, uint16_t duration, |
| energy_scan_done_cb_t done_cb); |
| static enum ieee802154_hw_caps get_capabilities(const struct device *dev); |
| static int cca_mock(const struct device *dev); |
| static int set_channel_mock(const struct device *dev, uint16_t channel); |
| static int filter_mock(const struct device *dev, bool set, |
| enum ieee802154_filter_type type, |
| const struct ieee802154_filter *filter); |
| static int set_txpower_mock(const struct device *dev, int16_t dbm); |
| static int tx_mock(const struct device *dev, enum ieee802154_tx_mode mode, |
| struct net_pkt *pkt, struct net_buf *frag); |
| static int start_mock(const struct device *dev); |
| static int stop_mock(const struct device *dev); |
| static int configure_mock(const struct device *dev, |
| enum ieee802154_config_type type, |
| const struct ieee802154_config *config); |
| |
| /* mocks */ |
| static struct ieee802154_radio_api rapi = { |
| .get_capabilities = get_capabilities, |
| .cca = cca_mock, |
| .set_channel = set_channel_mock, |
| .filter = filter_mock, |
| .set_txpower = set_txpower_mock, |
| .tx = tx_mock, |
| .start = start_mock, |
| .stop = stop_mock, |
| .configure = configure_mock, |
| #ifdef CONFIG_NET_L2_IEEE802154_SUB_GHZ |
| .get_subg_channel_count = NULL, |
| #endif /* CONFIG_NET_L2_IEEE802154_SUB_GHZ */ |
| .ed_scan = scan_mock |
| }; |
| |
| static struct device radio = { .api = &rapi }; |
| |
| static int scan_mock(const struct device *dev, uint16_t duration, |
| energy_scan_done_cb_t done_cb) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(duration); |
| scan_done_cb = done_cb; |
| return ztest_get_return_value(); |
| } |
| |
| static int rssi_scan_mock(const struct device *dev, uint16_t duration, |
| energy_scan_done_cb_t done_cb) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| zassert_equal(duration, 1, |
| "otPlatRadioGetRssi shall pass minimal allowed value."); |
| |
| /* use return value as callback param */ |
| done_cb(&radio, ztest_get_return_value()); |
| |
| return 0; |
| } |
| |
| static int set_channel_mock(const struct device *dev, uint16_t channel) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(channel); |
| return ztest_get_return_value(); |
| } |
| |
| void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| ztest_check_expected_value(aEnergyScanMaxRssi); |
| } |
| |
| void otSysEventSignalPending(void) |
| { |
| k_sem_give(&ot_sem); |
| } |
| |
| void otTaskletsSignalPending(otInstance *aInstance) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| k_sem_give(&ot_sem); |
| } |
| |
| static void make_sure_sem_set(k_timeout_t timeout) |
| { |
| zassert_equal(k_sem_take(&ot_sem, timeout), 0, "Sem not released."); |
| } |
| |
| void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, |
| otError aError) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| ztest_check_expected_value(aFrame->mChannel); |
| ztest_check_expected_value(aFrame->mLength); |
| ztest_check_expected_data(aFrame->mPsdu, aFrame->mLength); |
| ztest_check_expected_value(aError); |
| } |
| |
| void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, |
| otRadioFrame *aAckFrame, otError aError) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| ztest_check_expected_value(aError); |
| } |
| |
| static enum ieee802154_hw_caps get_capabilities(const struct device *dev) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| |
| return IEEE802154_HW_FCS | IEEE802154_HW_2_4_GHZ | |
| IEEE802154_HW_TX_RX_ACK | IEEE802154_HW_FILTER | |
| IEEE802154_HW_ENERGY_SCAN | IEEE802154_HW_SLEEP_TO_TX; |
| } |
| |
| static enum ieee802154_hw_caps get_capabilities_caps_mock(const struct device *dev) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| |
| return ztest_get_return_value(); |
| } |
| |
| static int configure_mock(const struct device *dev, |
| enum ieee802154_config_type type, |
| const struct ieee802154_config *config) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| |
| zassert_equal(type, IEEE802154_CONFIG_EVENT_HANDLER, |
| "Only event handler configuration was expected."); |
| |
| return 0; |
| } |
| |
| static int configure_match_mock(const struct device *dev, |
| enum ieee802154_config_type type, |
| const struct ieee802154_config *config) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(type); |
| |
| switch (type) { |
| case IEEE802154_CONFIG_AUTO_ACK_FPB: |
| ztest_check_expected_value(config->auto_ack_fpb.mode); |
| ztest_check_expected_value(config->auto_ack_fpb.enabled); |
| break; |
| case IEEE802154_CONFIG_ACK_FPB: |
| ztest_check_expected_value(config->ack_fpb.extended); |
| ztest_check_expected_value(config->ack_fpb.enabled); |
| ztest_check_expected_data( |
| config->ack_fpb.addr, |
| (config->ack_fpb.extended) ? sizeof(otExtAddress) : 2); |
| break; |
| default: |
| zassert_unreachable("Unexpected config type %d.", type); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int configure_promiscuous_mock(const struct device *dev, |
| enum ieee802154_config_type type, |
| const struct ieee802154_config *config) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| zassert_equal(type, IEEE802154_CONFIG_PROMISCUOUS, |
| "Config type incorrect."); |
| ztest_check_expected_value(config->promiscuous); |
| |
| return 0; |
| } |
| |
| static int cca_mock(const struct device *dev) |
| { |
| /* not using assert to verify function called */ |
| ztest_check_expected_value(dev); |
| return 0; |
| } |
| |
| static int filter_mock(const struct device *dev, bool set, |
| enum ieee802154_filter_type type, |
| const struct ieee802154_filter *filter) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(set); |
| ztest_check_expected_value(type); |
| switch (type) { |
| case IEEE802154_FILTER_TYPE_IEEE_ADDR: |
| ztest_check_expected_data(filter->ieee_addr, |
| OT_EXT_ADDRESS_SIZE); |
| break; |
| case IEEE802154_FILTER_TYPE_SHORT_ADDR: |
| ztest_check_expected_value(filter->short_addr); |
| break; |
| case IEEE802154_FILTER_TYPE_PAN_ID: |
| ztest_check_expected_value(filter->pan_id); |
| break; |
| default: |
| zassert_false(true, "Type not supported in mock: %d.", type); |
| break; |
| } |
| return 0; |
| } |
| |
| static int set_txpower_mock(const struct device *dev, int16_t dbm) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(dbm); |
| |
| return 0; |
| } |
| |
| static int tx_mock(const struct device *dev, enum ieee802154_tx_mode mode, |
| struct net_pkt *pkt, struct net_buf *frag) |
| { |
| zassert_equal(dev, &radio, "Device handle incorrect."); |
| ztest_check_expected_value(frag->data); |
| |
| return 0; |
| } |
| |
| static int start_mock(const struct device *dev) |
| { |
| ztest_check_expected_value(dev); |
| return 0; |
| } |
| |
| static int stop_mock(const struct device *dev) |
| { |
| ztest_check_expected_value(dev); |
| return 0; |
| } |
| |
| const struct device *device_get_binding_stub(const char *name) |
| { |
| return &radio; |
| } |
| |
| otError otIp6Send(otInstance *aInstance, otMessage *aMessage) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| ztest_check_expected_value(aMessage); |
| return ztest_get_return_value(); |
| } |
| |
| otMessage *otIp6NewMessage(otInstance *aInstance, |
| const otMessageSettings *aSettings) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| return ip_msg; |
| } |
| |
| otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength) |
| { |
| void *buf = (void *)aBuf; |
| |
| ztest_check_expected_value(aMessage); |
| ztest_check_expected_value(aLength); |
| ztest_check_expected_data(buf, aLength); |
| return ztest_get_return_value(); |
| } |
| |
| void otMessageFree(otMessage *aMessage) |
| { |
| ztest_check_expected_value(aMessage); |
| } |
| |
| void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| zassert_equal(aInstance, ot, "Incorrect instance."); |
| } |
| |
| /** |
| * @brief Test for immediate energy scan |
| * Tests for case when radio energy scan returns success at the first call. |
| * |
| */ |
| static void test_energy_scan_immediate_test(void) |
| { |
| const uint8_t chan = 10; |
| const uint8_t dur = 100; |
| const int16_t energy = -94; |
| |
| scan_done_cb = NULL; |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan); |
| |
| ztest_returns_value(scan_mock, 0); |
| ztest_expect_value(scan_mock, duration, dur); |
| |
| zassert_equal(otPlatRadioEnergyScan(ot, chan, dur), OT_ERROR_NONE, |
| "Energy scan returned error."); |
| zassert_not_null(scan_done_cb, "Scan callback not specified."); |
| |
| scan_done_cb(&radio, energy); |
| make_sure_sem_set(K_NO_WAIT); |
| |
| ztest_expect_value(otPlatRadioEnergyScanDone, aEnergyScanMaxRssi, |
| energy); |
| platformRadioProcess(ot); |
| } |
| |
| /** |
| * @brief Test for delayed energy scan |
| * Tests for case when radio returns not being able to start energy scan and |
| * the scan should be sheduled for later. |
| * |
| */ |
| static void test_energy_scan_delayed_test(void) |
| { |
| const uint8_t chan = 10; |
| const uint8_t dur = 100; |
| const int16_t energy = -94; |
| |
| scan_done_cb = NULL; |
| |
| /* request scan */ |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan); |
| |
| ztest_returns_value(scan_mock, -EBUSY); |
| ztest_expect_value(scan_mock, duration, dur); |
| |
| zassert_equal(otPlatRadioEnergyScan(ot, chan, dur), OT_ERROR_NONE, |
| "Energy scan returned error."); |
| zassert_not_null(scan_done_cb, "Scan callback not specified."); |
| make_sure_sem_set(K_NO_WAIT); |
| |
| /* process reported event */ |
| ztest_returns_value(scan_mock, 0); |
| ztest_expect_value(scan_mock, duration, dur); |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan); |
| platformRadioProcess(ot); |
| zassert_not_null(scan_done_cb, "Scan callback not specified."); |
| |
| /* invoke scan done */ |
| scan_done_cb(&radio, energy); |
| make_sure_sem_set(K_NO_WAIT); |
| |
| ztest_expect_value(otPlatRadioEnergyScanDone, aEnergyScanMaxRssi, |
| energy); |
| platformRadioProcess(ot); |
| } |
| |
| static void create_ack_frame(void) |
| { |
| struct net_pkt *packet; |
| struct net_buf *buf; |
| const uint8_t lqi = 230; |
| const int8_t rssi = -80; |
| |
| packet = net_pkt_alloc(K_NO_WAIT); |
| buf = net_pkt_get_reserve_tx_data(K_NO_WAIT); |
| net_pkt_append_buffer(packet, buf); |
| |
| buf->len = ACK_PKT_LENGTH; |
| buf->data[0] = FRAME_TYPE_ACK; |
| |
| net_pkt_set_ieee802154_rssi(packet, rssi); |
| net_pkt_set_ieee802154_lqi(packet, lqi); |
| zassert_equal(ieee802154_radio_handle_ack(NULL, packet), NET_OK, |
| "Handling ack failed."); |
| net_pkt_unref(packet); |
| } |
| |
| /** |
| * @brief Test for tx data handling |
| * Tests if OT frame is correctly passed to the radio driver. |
| * Additionally verifies ACK frame passing back to the OT. |
| * |
| */ |
| static void test_tx_test(void) |
| { |
| const uint8_t chan = 20; |
| uint8_t chan2 = chan - 1; |
| const int8_t power = -3; |
| |
| otRadioFrame *frm = otPlatRadioGetTransmitBuffer(ot); |
| |
| zassert_not_null(frm, "Transmit buffer is null."); |
| |
| zassert_equal(otPlatRadioSetTransmitPower(ot, power), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| ztest_expect_value(start_mock, dev, &radio); |
| zassert_equal(otPlatRadioReceive(ot, chan), OT_ERROR_NONE, |
| "Failed to receive."); |
| |
| /* ACKed frame */ |
| frm->mChannel = chan2; |
| frm->mInfo.mTxInfo.mCsmaCaEnabled = true; |
| frm->mPsdu[0] = IEEE802154_AR_FLAG_SET; |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan2); |
| ztest_expect_value(cca_mock, dev, &radio); |
| ztest_expect_value(tx_mock, frag->data, frm->mPsdu); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| zassert_equal(otPlatRadioTransmit(ot, frm), OT_ERROR_NONE, |
| "Transmit failed."); |
| |
| create_ack_frame(); |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| |
| ztest_expect_value(otPlatRadioTxDone, aError, OT_ERROR_NONE); |
| platformRadioProcess(ot); |
| |
| /* Non-ACKed frame */ |
| frm->mChannel = --chan2; |
| frm->mInfo.mTxInfo.mCsmaCaEnabled = false; |
| frm->mPsdu[0] = 0; |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan2); |
| ztest_expect_value(tx_mock, frag->data, frm->mPsdu); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| zassert_equal(otPlatRadioTransmit(ot, frm), OT_ERROR_NONE, |
| "Transmit failed."); |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| ztest_expect_value(otPlatRadioTxDone, aError, OT_ERROR_NONE); |
| platformRadioProcess(ot); |
| |
| /* ACKed frame, no ACK */ |
| frm->mChannel = --chan2; |
| frm->mInfo.mTxInfo.mCsmaCaEnabled = false; |
| frm->mPsdu[0] = IEEE802154_AR_FLAG_SET; |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, chan2); |
| ztest_expect_value(tx_mock, frag->data, frm->mPsdu); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| zassert_equal(otPlatRadioTransmit(ot, frm), OT_ERROR_NONE, |
| "Transmit failed."); |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| |
| ztest_expect_value(otPlatRadioTxDone, aError, OT_ERROR_NO_ACK); |
| platformRadioProcess(ot); |
| } |
| |
| /** |
| * @brief Test for tx power setting |
| * Tests if tx power requested by the OT is correctly passed to the radio. |
| * |
| */ |
| static void test_tx_power_test(void) |
| { |
| int8_t out_power = 0; |
| |
| zassert_equal(otPlatRadioSetTransmitPower(ot, -3), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| zassert_equal(otPlatRadioGetTransmitPower(ot, &out_power), |
| OT_ERROR_NONE, "Failed to obtain TX power."); |
| zassert_equal(out_power, -3, "Got different power than set."); |
| zassert_equal(otPlatRadioSetTransmitPower(ot, -6), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| zassert_equal(otPlatRadioGetTransmitPower(ot, &out_power), |
| OT_ERROR_NONE, "Failed to obtain TX power."); |
| zassert_equal(out_power, -6, |
| "Second call to otPlatRadioSetTransmitPower failed."); |
| } |
| |
| /** |
| * @brief Test for getting radio sensitivity |
| * There is no api to get radio sensitivity from the radio so the value is |
| * hardcoded in radio.c. Test only verifies if the value returned makes any |
| * sense. |
| * |
| */ |
| static void test_sensitivity_test(void) |
| { |
| /* |
| * Nothing to test actually as this is constant 100. |
| * When radio interface will be extended to get sensitivity this test |
| * can be extended with the radio api call. For now just verify if the |
| * value is reasonable. |
| */ |
| zassert_true(-80 > otPlatRadioGetReceiveSensitivity(ot), |
| "Radio sensitivity not in range."); |
| } |
| |
| static void set_expected_match_values(enum ieee802154_config_type type, |
| uint8_t *addr, bool extended, bool enabled) |
| { |
| ztest_expect_value(configure_match_mock, type, type); |
| switch (type) { |
| case IEEE802154_CONFIG_AUTO_ACK_FPB: |
| ztest_expect_value(configure_match_mock, |
| config->auto_ack_fpb.enabled, enabled); |
| ztest_expect_value(configure_match_mock, |
| config->auto_ack_fpb.mode, |
| IEEE802154_FPB_ADDR_MATCH_THREAD); |
| break; |
| case IEEE802154_CONFIG_ACK_FPB: |
| ztest_expect_value(configure_match_mock, |
| config->ack_fpb.extended, extended); |
| ztest_expect_value(configure_match_mock, |
| config->ack_fpb.enabled, enabled); |
| ztest_expect_data(configure_match_mock, config->ack_fpb.addr, |
| addr); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * @brief Test different types of OT source match. |
| * Tests if Enable, Disable, Add and Clear Source Match calls are passed to the |
| * radio driver correctly. |
| * |
| */ |
| static void test_source_match_test(void) |
| { |
| otExtAddress ext_addr; |
| |
| rapi.configure = configure_match_mock; |
| /* Enable/Disable */ |
| set_expected_match_values(IEEE802154_CONFIG_AUTO_ACK_FPB, NULL, false, |
| true); |
| otPlatRadioEnableSrcMatch(ot, true); |
| set_expected_match_values(IEEE802154_CONFIG_AUTO_ACK_FPB, NULL, false, |
| false); |
| otPlatRadioEnableSrcMatch(ot, false); |
| |
| set_expected_match_values(IEEE802154_CONFIG_AUTO_ACK_FPB, NULL, false, |
| true); |
| otPlatRadioEnableSrcMatch(ot, true); |
| |
| /* Add */ |
| sys_put_le16(12345, ext_addr.m8); |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, false, |
| true); |
| zassert_equal(otPlatRadioAddSrcMatchShortEntry(ot, 12345), |
| OT_ERROR_NONE, "Failed to add short src entry."); |
| |
| |
| for (int i = 0; i < sizeof(ext_addr.m8); i++) { |
| ext_addr.m8[i] = i; |
| } |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, true, |
| true); |
| zassert_equal(otPlatRadioAddSrcMatchExtEntry(ot, &ext_addr), |
| OT_ERROR_NONE, "Failed to add ext src entry."); |
| |
| /* Clear */ |
| sys_put_le16(12345, ext_addr.m8); |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, false, |
| false); |
| zassert_equal(otPlatRadioClearSrcMatchShortEntry(ot, 12345), |
| OT_ERROR_NONE, "Failed to clear short src entry."); |
| |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, true, |
| false); |
| zassert_equal(otPlatRadioClearSrcMatchExtEntry(ot, &ext_addr), |
| OT_ERROR_NONE, "Failed to clear ext src entry."); |
| |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, NULL, false, |
| false); |
| otPlatRadioClearSrcMatchShortEntries(ot); |
| |
| set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, NULL, true, false); |
| otPlatRadioClearSrcMatchExtEntries(ot); |
| |
| rapi.configure = configure_mock; |
| } |
| |
| /** |
| * @brief Test for enabling or disabling promiscuous mode |
| * Tests if OT can successfully enable or disable promiscuous mode. |
| * |
| */ |
| static void test_promiscuous_mode_set_test(void) |
| { |
| rapi.configure = configure_promiscuous_mock; |
| |
| zassert_false(otPlatRadioGetPromiscuous(ot), |
| "By default promiscuous mode shall be disabled."); |
| |
| ztest_expect_value(configure_promiscuous_mock, config->promiscuous, |
| true); |
| otPlatRadioSetPromiscuous(ot, true); |
| zassert_true(otPlatRadioGetPromiscuous(ot), "Mode not enabled."); |
| |
| ztest_expect_value(configure_promiscuous_mock, config->promiscuous, |
| false); |
| otPlatRadioSetPromiscuous(ot, false); |
| zassert_false(otPlatRadioGetPromiscuous(ot), "Mode still enabled."); |
| |
| rapi.configure = configure_mock; |
| } |
| |
| /** |
| * @brief Test of proper radio to OT capabilities mapping |
| * Tests if different radio capabilities map for their corresponding OpenThread |
| * capability |
| * |
| */ |
| static void test_get_caps_test(void) |
| { |
| rapi.get_capabilities = get_capabilities_caps_mock; |
| |
| /* no caps */ |
| ztest_returns_value(get_capabilities_caps_mock, 0); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| |
| /* not used by OT */ |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_FCS); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_2_4_GHZ); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_SUB_GHZ); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| |
| /* not implemented or not fully supported */ |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_TXTIME); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_PROMISC); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_NONE, |
| "Incorrect capabilities returned."); |
| |
| /* proper mapping */ |
| ztest_returns_value(get_capabilities_caps_mock, IEEE802154_HW_CSMA); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_CSMA_BACKOFF, |
| "Incorrect capabilities returned."); |
| |
| ztest_returns_value(get_capabilities_caps_mock, |
| IEEE802154_HW_ENERGY_SCAN); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_ENERGY_SCAN, |
| "Incorrect capabilities returned."); |
| |
| ztest_returns_value(get_capabilities_caps_mock, |
| IEEE802154_HW_TX_RX_ACK); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_ACK_TIMEOUT, |
| "Incorrect capabilities returned."); |
| |
| ztest_returns_value(get_capabilities_caps_mock, |
| IEEE802154_HW_SLEEP_TO_TX); |
| zassert_equal(otPlatRadioGetCaps(ot), OT_RADIO_CAPS_SLEEP_TO_TX, |
| "Incorrect capabilities returned."); |
| |
| /* all at once */ |
| ztest_returns_value( |
| get_capabilities_caps_mock, |
| IEEE802154_HW_FCS | IEEE802154_HW_PROMISC | |
| IEEE802154_HW_FILTER | IEEE802154_HW_CSMA | |
| IEEE802154_HW_2_4_GHZ | IEEE802154_HW_TX_RX_ACK | |
| IEEE802154_HW_SUB_GHZ | IEEE802154_HW_ENERGY_SCAN | |
| IEEE802154_HW_TXTIME | IEEE802154_HW_SLEEP_TO_TX); |
| zassert_equal( |
| otPlatRadioGetCaps(ot), |
| OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_ENERGY_SCAN | |
| OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_SLEEP_TO_TX, |
| "Incorrect capabilities returned."); |
| |
| rapi.get_capabilities = get_capabilities; |
| } |
| |
| /** |
| * @brief Test for getting the rssi value from the radio |
| * Tests if correct value is returned from the otPlatRadioGetRssi function. |
| * |
| */ |
| static void test_get_rssi_test(void) |
| { |
| const int8_t rssi = -103; |
| |
| rapi.ed_scan = rssi_scan_mock; |
| |
| ztest_returns_value(rssi_scan_mock, rssi); |
| zassert_equal(otPlatRadioGetRssi(ot), rssi, |
| "Invalid RSSI value reveiced."); |
| |
| rapi.ed_scan = scan_mock; |
| } |
| |
| /** |
| * @brief Test switching between radio states |
| * Tests if radio is correctly switched between states. |
| * |
| */ |
| static void test_radio_state_test(void) |
| { |
| const uint8_t channel = 12; |
| const uint8_t power = 10; |
| |
| zassert_equal(otPlatRadioSetTransmitPower(ot, power), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| zassert_equal(otPlatRadioDisable(ot), OT_ERROR_NONE, |
| "Failed to disable radio."); |
| |
| zassert_false(otPlatRadioIsEnabled(ot), "Radio reports as enabled."); |
| |
| zassert_equal(otPlatRadioSleep(ot), OT_ERROR_INVALID_STATE, |
| "Changed to sleep regardles being disabled."); |
| |
| zassert_equal(otPlatRadioEnable(ot), OT_ERROR_NONE, |
| "Enabling radio failed."); |
| |
| zassert_true(otPlatRadioIsEnabled(ot), "Radio reports disabled."); |
| |
| ztest_expect_value(stop_mock, dev, &radio); |
| zassert_equal(otPlatRadioSleep(ot), OT_ERROR_NONE, |
| "Failed to switch to sleep mode."); |
| |
| zassert_true(otPlatRadioIsEnabled(ot), "Radio reports as disabled."); |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, channel); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| ztest_expect_value(start_mock, dev, &radio); |
| zassert_equal(otPlatRadioReceive(ot, channel), OT_ERROR_NONE, |
| "Failed to receive."); |
| zassert_equal(platformRadioChannelGet(ot), channel, |
| "Channel number not remembered."); |
| |
| zassert_true(otPlatRadioIsEnabled(ot), "Radio reports as disabled."); |
| } |
| |
| /** |
| * @brief Test address filtering |
| * Tests if short, extended address and PanID are correctly passed to the radio |
| * driver. |
| * |
| */ |
| static void test_address_test(void) |
| { |
| const uint16_t pan_id = 0xDEAD; |
| const uint16_t short_add = 0xCAFE; |
| otExtAddress ieee_addr; |
| |
| for (int i = 0; i < sizeof(ieee_addr.m8); i++) { |
| ieee_addr.m8[i] = 'a' + i; |
| } |
| |
| ztest_expect_value(filter_mock, set, true); |
| ztest_expect_value(filter_mock, type, IEEE802154_FILTER_TYPE_PAN_ID); |
| ztest_expect_value(filter_mock, filter->pan_id, pan_id); |
| otPlatRadioSetPanId(ot, pan_id); |
| |
| ztest_expect_value(filter_mock, set, true); |
| ztest_expect_value(filter_mock, type, |
| IEEE802154_FILTER_TYPE_SHORT_ADDR); |
| ztest_expect_value(filter_mock, filter->short_addr, short_add); |
| otPlatRadioSetShortAddress(ot, short_add); |
| |
| ztest_expect_value(filter_mock, set, true); |
| ztest_expect_value(filter_mock, type, IEEE802154_FILTER_TYPE_IEEE_ADDR); |
| ztest_expect_data(filter_mock, filter->ieee_addr, ieee_addr.m8); |
| otPlatRadioSetExtendedAddress(ot, &ieee_addr); |
| } |
| |
| |
| uint8_t alloc_pkt(struct net_pkt **out_packet, uint8_t buf_ct, uint8_t offset) |
| { |
| struct net_pkt *packet; |
| struct net_buf *buf; |
| uint8_t len = 0; |
| uint8_t buf_num; |
| |
| packet = net_pkt_alloc(K_NO_WAIT); |
| for (buf_num = 0; buf_num < buf_ct; buf_num++) { |
| buf = net_pkt_get_reserve_tx_data(K_NO_WAIT); |
| net_pkt_append_buffer(packet, buf); |
| |
| for (int i = 0; i < buf->size; i++) { |
| buf->data[i] = (offset + i + buf_num) & 0xFF; |
| } |
| |
| len = buf->size - 3; |
| buf->len = len; |
| } |
| |
| *out_packet = packet; |
| return len; |
| } |
| |
| /** |
| * @brief Test received messages handling. |
| * Tests if received frames are properly passed to the OpenThread |
| * |
| */ |
| static void test_receive_test(void) |
| { |
| struct net_pkt *packet; |
| struct net_buf *buf; |
| const uint8_t channel = 21; |
| const int8_t power = -5; |
| const uint8_t lqi = 240; |
| const int8_t rssi = -90; |
| uint8_t len; |
| |
| len = alloc_pkt(&packet, 1, 'a'); |
| buf = packet->buffer; |
| |
| net_pkt_set_ieee802154_lqi(packet, lqi); |
| net_pkt_set_ieee802154_rssi(packet, rssi); |
| |
| zassert_equal(otPlatRadioSetTransmitPower(ot, power), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, channel); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| ztest_expect_value(start_mock, dev, &radio); |
| zassert_equal(otPlatRadioReceive(ot, channel), OT_ERROR_NONE, |
| "Failed to receive."); |
| |
| /* |
| * Not setting any expect values as nothing shall be called from |
| * notify_new_rx_frame calling thread. OT functions can be called only |
| * after semaphore for main thread is released. |
| */ |
| notify_new_rx_frame(packet); |
| |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| ztest_expect_value(otPlatRadioReceiveDone, aError, OT_ERROR_NONE); |
| ztest_expect_value(otPlatRadioReceiveDone, aFrame->mChannel, channel); |
| ztest_expect_value(otPlatRadioReceiveDone, aFrame->mLength, len); |
| ztest_expect_data(otPlatRadioReceiveDone, aFrame->mPsdu, buf->data); |
| platformRadioProcess(ot); |
| } |
| |
| /** |
| * @brief Test received messages handling. |
| * Tests if received frames are properly passed to the OpenThread |
| * |
| */ |
| static void test_net_pkt_transmit(void) |
| { |
| struct net_pkt *packet; |
| struct net_buf *buf; |
| const uint8_t channel = 21; |
| const int8_t power = -5; |
| uint8_t len; |
| |
| /* success */ |
| len = alloc_pkt(&packet, 2, 'a'); |
| buf = packet->buffer; |
| zassert_equal(otPlatRadioSetTransmitPower(ot, power), OT_ERROR_NONE, |
| "Failed to set TX power."); |
| |
| ztest_returns_value(set_channel_mock, 0); |
| ztest_expect_value(set_channel_mock, channel, channel); |
| ztest_expect_value(set_txpower_mock, dbm, power); |
| ztest_expect_value(start_mock, dev, &radio); |
| zassert_equal(otPlatRadioReceive(ot, channel), OT_ERROR_NONE, |
| "Failed to receive."); |
| |
| notify_new_tx_frame(packet); |
| |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| |
| ztest_expect_value(otMessageAppend, aMessage, ip_msg); |
| ztest_expect_value(otMessageAppend, aLength, len); |
| ztest_expect_data(otMessageAppend, buf, buf->data); |
| ztest_returns_value(otMessageAppend, OT_ERROR_NONE); |
| |
| buf = buf->frags; |
| ztest_expect_value(otMessageAppend, aMessage, ip_msg); |
| ztest_expect_value(otMessageAppend, aLength, len); |
| ztest_expect_data(otMessageAppend, buf, buf->data); |
| ztest_returns_value(otMessageAppend, OT_ERROR_NONE); |
| |
| ztest_expect_value(otIp6Send, aMessage, ip_msg); |
| ztest_returns_value(otIp6Send, OT_ERROR_NONE); |
| |
| /* Do not expect free in case of success */ |
| |
| platformRadioProcess(ot); |
| |
| /* fail on append */ |
| len = alloc_pkt(&packet, 2, 'b'); |
| buf = packet->buffer; |
| |
| notify_new_tx_frame(packet); |
| |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| |
| ztest_expect_value(otMessageAppend, aMessage, ip_msg); |
| ztest_expect_value(otMessageAppend, aLength, len); |
| ztest_expect_data(otMessageAppend, buf, buf->data); |
| ztest_returns_value(otMessageAppend, OT_ERROR_NO_BUFS); |
| |
| /* Expect free in case of failure in appending buffer*/ |
| ztest_expect_value(otMessageFree, aMessage, ip_msg); |
| |
| platformRadioProcess(ot); |
| |
| /* fail on send */ |
| len = alloc_pkt(&packet, 1, 'c'); |
| buf = packet->buffer; |
| |
| notify_new_tx_frame(packet); |
| |
| make_sure_sem_set(Z_TIMEOUT_MS(100)); |
| |
| ztest_expect_value(otMessageAppend, aMessage, ip_msg); |
| ztest_expect_value(otMessageAppend, aLength, len); |
| ztest_expect_data(otMessageAppend, buf, buf->data); |
| ztest_returns_value(otMessageAppend, OT_ERROR_NONE); |
| |
| ztest_expect_value(otIp6Send, aMessage, ip_msg); |
| ztest_returns_value(otIp6Send, OT_ERROR_BUSY); |
| |
| /* Do not expect free in case of failure in send */ |
| |
| platformRadioProcess(ot); |
| |
| } |
| |
| void test_main(void) |
| { |
| platformRadioInit(); |
| |
| ztest_test_suite(openthread_radio, |
| ztest_unit_test(test_energy_scan_immediate_test), |
| ztest_unit_test(test_energy_scan_delayed_test), |
| ztest_unit_test(test_tx_test), |
| ztest_unit_test(test_tx_power_test), |
| ztest_unit_test(test_sensitivity_test), |
| ztest_unit_test(test_source_match_test), |
| ztest_unit_test(test_promiscuous_mode_set_test), |
| ztest_unit_test(test_get_caps_test), |
| ztest_unit_test(test_get_rssi_test), |
| ztest_unit_test(test_radio_state_test), |
| ztest_unit_test(test_address_test), |
| ztest_unit_test(test_receive_test), |
| ztest_unit_test(test_net_pkt_transmit)); |
| |
| |
| ztest_run_test_suite(openthread_radio); |
| } |