text: /*
   * 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 <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;
  
  /* forward declarations */
  static int scan_mock(struct device *dev, u16_t duration,
  		     energy_scan_done_cb_t done_cb);
  static enum ieee802154_hw_caps get_capabilities(struct device *dev);
  static int cca_mock(struct device *dev);
  static int set_channel_mock(struct device *dev, u16_t channel);
  static int filter_mock(struct device *dev, bool set,
  		       enum ieee802154_filter_type type,
  		       const struct ieee802154_filter *filter);
  static int set_txpower_mock(struct device *dev, s16_t dbm);
  static int tx_mock(struct device *dev, enum ieee802154_tx_mode mode,
  		   struct net_pkt *pkt, struct net_buf *frag);
  static int start_mock(struct device *dev);
  static int stop_mock(struct device *dev);
  static int configure_mock(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 = { .driver_api = &rapi };
  
  static int scan_mock(struct device *dev, u16_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(struct device *dev, u16_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(struct device *dev, u16_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(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;
  }
  
  static enum ieee802154_hw_caps get_capabilities_caps_mock(struct device *dev)
  {
  	zassert_equal(dev, &radio, "Device handle incorrect.");
  
  	return ztest_get_return_value();
  }
  
  static int configure_mock(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(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(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(struct device *dev)
  {
  	/* not using assert to verify function called */
  	ztest_check_expected_value(dev);
  	return 0;
  }
  
  static int filter_mock(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(struct device *dev, s16_t dbm)
  {
  	zassert_equal(dev, &radio, "Device handle incorrect.");
  	ztest_check_expected_value(dbm);
  
  	return 0;
  }
  
  static int tx_mock(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(struct device *dev)
  {
  	ztest_check_expected_value(dev);
  	return 0;
  }
  
  static int stop_mock(struct device *dev)
  {
  	ztest_check_expected_value(dev);
  	return 0;
  }
  
  struct device *device_get_binding_stub(const char *name)
  {
  	return &radio;
  }
  
  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 energy_scan_immediate_test(void)
  {
  	const u8_t chan = 10;
  	const u8_t dur = 100;
  	const s16_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 energy_scan_delayed_test(void)
  {
  	const u8_t chan = 10;
  	const u8_t dur = 100;
  	const s16_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 u8_t lqi = 230;
  	const s8_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 tx_test(void)
  {
  	const u8_t chan = 20;
  	u8_t chan2 = chan - 1;
  	const s8_t power = -3;
  
  	otRadioFrame *frm = otPlatRadioGetTransmitBuffer(ot);
  
  	zassert_not_null(frm, "Transmit buffer is null.");
  
  	otPlatRadioSetTransmitPower(ot, 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);
  	otPlatRadioReceive(ot, chan);
  
  	/* 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 tx_power_test(void)
  {
  	s8_t out_power = 0;
  
  	otPlatRadioSetTransmitPower(ot, -3);
  	otPlatRadioGetTransmitPower(ot, &out_power);
  	zassert_equal(out_power, -3, "Got different power than set.");
  	otPlatRadioSetTransmitPower(ot, -6);
  	otPlatRadioGetTransmitPower(ot, &out_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 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,
  				      u8_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 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);
  	otPlatRadioAddSrcMatchShortEntry(ot, 12345);
  
  	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);
  	otPlatRadioAddSrcMatchExtEntry(ot, &ext_addr);
  
  	/* Clear */
  	sys_put_le16(12345, ext_addr.m8);
  	set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, false,
  				  false);
  	otPlatRadioClearSrcMatchShortEntry(ot, 12345);
  
  	set_expected_match_values(IEEE802154_CONFIG_ACK_FPB, ext_addr.m8, true,
  				  false);
  	otPlatRadioClearSrcMatchExtEntry(ot, &ext_addr);
  
  	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 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 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_TRANSMIT_RETRIES |
  			      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.");
  
  	/* 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);
  	zassert_equal(
  		otPlatRadioGetCaps(ot),
  		OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF |
  			OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_ACK_TIMEOUT,
  		"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 get_rssi_test(void)
  {
  	const s8_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 radio_state_test(void)
  {
  	const u8_t channel = 12;
  	const u8_t power = 10;
  
  	otPlatRadioSetTransmitPower(ot, 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);
  	otPlatRadioReceive(ot, channel);
  	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 address_test(void)
  {
  	const u16_t pan_id = 0xDEAD;
  	const u16_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);
  }
  
  /**
   * @brief Test received messages handling.
   * Tests if received frames are properly passed to the OpenThread
   *
   */
  static void receive_test(void)
  {
  	struct net_pkt *packet;
  	struct net_buf *buf;
  	const u8_t channel = 21;
  	const s8_t power = -5;
  	const u8_t lqi = 240;
  	const s8_t rssi = -90;
  	u8_t len;
  
  	packet = net_pkt_alloc(K_NO_WAIT);
  	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] = ('a' + i) & 0xFF;
  	}
  	len = buf->size - 3;
  	buf->len = len;
  
  	net_pkt_set_ieee802154_lqi(packet, lqi);
  	net_pkt_set_ieee802154_rssi(packet, rssi);
  
  	otPlatRadioSetTransmitPower(ot, 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);
  	otPlatRadioReceive(ot, channel);
  
  	/*
  	 * 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);
  }
  
  void test_main(void)
  {
  	platformRadioInit();
  
  	ztest_test_suite(openthread_radio,
  		ztest_unit_test(energy_scan_immediate_test),
  		ztest_unit_test(energy_scan_delayed_test),
  		ztest_unit_test(tx_test),
  		ztest_unit_test(tx_power_test),
  		ztest_unit_test(sensitivity_test),
  		ztest_unit_test(source_match_test),
  		ztest_unit_test(promiscuous_mode_set_test),
  		ztest_unit_test(get_caps_test),
  		ztest_unit_test(get_rssi_test),
  		ztest_unit_test(radio_state_test),
  		ztest_unit_test(address_test),
  		ztest_unit_test(receive_test));
  
  	ztest_run_test_suite(openthread_radio);
  }
  