2018-02-08 10:45:47 +00:00
|
|
|
/*
|
2018-02-15 11:48:02 +00:00
|
|
|
* Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
|
2018-02-08 10:45:47 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
|
|
* \addtogroup rf-core-prop
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* \file
|
|
|
|
* Implementation of the CC13xx prop mode NETSTACK_RADIO driver
|
|
|
|
*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include "contiki.h"
|
|
|
|
#include "net/packetbuf.h"
|
|
|
|
#include "net/netstack.h"
|
|
|
|
#include "sys/energest.h"
|
|
|
|
#include "sys/clock.h"
|
|
|
|
#include "sys/rtimer.h"
|
|
|
|
#include "sys/cc.h"
|
2018-02-09 11:50:55 +00:00
|
|
|
#include "dev/watchdog.h"
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* RF Core Mailbox API */
|
2018-05-31 15:17:07 +00:00
|
|
|
#include <ti/devices/DeviceFamily.h>
|
|
|
|
#include DeviceFamily_constructPath(driverlib/rf_mailbox.h)
|
|
|
|
#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h)
|
|
|
|
#include DeviceFamily_constructPath(driverlib/rf_data_entry.h)
|
|
|
|
#include DeviceFamily_constructPath(driverlib/rf_prop_cmd.h)
|
|
|
|
#include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
|
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
#include <ti/drivers/rf/RF.h>
|
2018-02-15 11:48:02 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* RF settings */
|
|
|
|
#ifdef PROP_MODE_CONF_RF_SETTINGS
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_RF_SETTINGS PROP_MODE_CONF_RF_SETTINGS
|
2018-02-15 11:48:02 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_RF_SETTINGS "prop-settings.h"
|
2018-02-15 11:48:02 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include PROP_MODE_RF_SETTINGS
|
2018-02-14 16:56:35 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Platform RF dev */
|
2018-02-15 11:48:02 +00:00
|
|
|
#include "rf-common.h"
|
2018-02-14 16:56:35 +00:00
|
|
|
#include "dot-15-4g.h"
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
2018-02-09 11:50:55 +00:00
|
|
|
#include <assert.h>
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-14 16:56:35 +00:00
|
|
|
#ifdef NDEBUG
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PRINTF(...)
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PRINTF(...) printf(__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Configuration for default Prop channel */
|
|
|
|
#ifdef PROP_MODE_CONF_CHANNEL
|
|
|
|
# define PROP_MODE_CHANNEL PROP_MODE_CONF_CHANNEL
|
|
|
|
#else
|
|
|
|
# define PROP_MODE_CHANNEL RF_CHANNEL
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Data whitener. 1: Whitener, 0: No whitener */
|
|
|
|
#ifdef PROP_MODE_CONF_DW
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_DW PROP_MODE_CONF_DW
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_DW 0
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PROP_MODE_CONF_USE_CRC16
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_USE_CRC16 PROP_MODE_CONF_USE_CRC16
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_USE_CRC16 0
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Used for checking result of CCA_REQ command */
|
|
|
|
#define CCA_STATE_IDLE 0
|
|
|
|
#define CCA_STATE_BUSY 1
|
|
|
|
#define CCA_STATE_INVALID 2
|
2018-02-08 10:45:47 +00:00
|
|
|
|
|
|
|
/* Used as an error return value for get_cca_info */
|
|
|
|
#define RF_GET_CCA_INFO_ERROR 0xFF
|
|
|
|
|
|
|
|
#ifdef PROP_MODE_CONF_RSSI_THRESHOLD
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_RSSI_THRESHOLD PROP_MODE_CONF_RSSI_THRESHOLD
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define PROP_MODE_RSSI_THRESHOLD 0xA6
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Defines and variables related to the .15.4g PHY HDR */
|
|
|
|
#define DOT_4G_MAX_FRAME_LEN 2047
|
|
|
|
#define DOT_4G_PHR_LEN 2
|
|
|
|
|
|
|
|
/* PHY HDR bits */
|
|
|
|
#define DOT_4G_PHR_CRC16 0x10
|
|
|
|
#define DOT_4G_PHR_DW 0x08
|
|
|
|
|
|
|
|
#if PROP_MODE_USE_CRC16
|
|
|
|
/* CRC16 */
|
2018-06-15 14:37:51 +00:00
|
|
|
# define DOT_4G_PHR_CRC_BIT DOT_4G_PHR_CRC16
|
|
|
|
# define CRC_LEN 2
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
|
|
|
/* CRC32 */
|
2018-06-15 14:37:51 +00:00
|
|
|
# define DOT_4G_PHR_CRC_BIT 0
|
|
|
|
# define CRC_LEN 4
|
|
|
|
#endif /* PROP_MODE_USE_CRC16 */
|
2018-02-08 10:45:47 +00:00
|
|
|
|
|
|
|
#if PROP_MODE_DW
|
2018-06-15 14:37:51 +00:00
|
|
|
# define DOT_4G_PHR_DW_BIT DOT_4G_PHR_DW
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
#define DOT_4G_PHR_DW_BIT 0
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* How long to wait for the RF to enter RX in rf_cmd_ieee_rx */
|
|
|
|
#define ENTER_RX_WAIT_TIMEOUT (RTIMER_SECOND >> 10)
|
2018-06-18 17:23:50 +00:00
|
|
|
|
|
|
|
/* How long to wait for the rx read entry to become ready */
|
|
|
|
#define TIMEOUT_DATA_ENTRY_BUSY (RTIMER_SECOND / 250)
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-05-31 15:17:07 +00:00
|
|
|
/* Configuration for TX power table */
|
2018-06-07 08:29:47 +00:00
|
|
|
#ifdef PROP_MODE_CONF_TX_POWER_TABLE
|
2018-06-15 14:37:51 +00:00
|
|
|
# define TX_POWER_TABLE PROP_MODE_CONF_TX_POWER_TABLE
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define TX_POWER_TABLE propTxPowerTable
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-05-31 15:17:07 +00:00
|
|
|
/* TX power table convenience macros */
|
2018-06-19 12:55:18 +00:00
|
|
|
#define TX_POWER_TABLE_SIZE ((sizeof(TX_POWER_TABLE) / sizeof(TX_POWER_TABLE[0])) - 1)
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
#define TX_POWER_MIN (TX_POWER_TABLE[0].power)
|
|
|
|
#define TX_POWER_MAX (TX_POWER_TABLE[TX_POWER_TABLE_SIZE - 1].power)
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
#define TX_POWER_IN_RANGE(dbm) ((TX_POWER_MIN <= (dbm)) && ((dbm) <= TX_POWER_MAX))
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
/* TX buf configuration */
|
|
|
|
#define TX_BUF_HDR_LEN 2
|
|
|
|
#define TX_BUF_PAYLOAD_LEN 180
|
|
|
|
|
|
|
|
#define TX_BUF_SIZE (TX_BUF_HDR_LEN + TX_BUF_PAYLOAD_LEN)
|
|
|
|
|
|
|
|
/* RX buf configuration */
|
2018-02-08 10:45:47 +00:00
|
|
|
#ifdef PROP_MODE_CONF_RX_BUF_CNT
|
2018-06-15 14:37:51 +00:00
|
|
|
# define RX_BUF_CNT PROP_MODE_CONF_RX_BUF_CNT
|
2018-02-08 10:45:47 +00:00
|
|
|
#else
|
2018-06-15 14:37:51 +00:00
|
|
|
# define RX_BUF_CNT 4
|
2018-02-08 10:45:47 +00:00
|
|
|
#endif
|
2018-06-15 14:37:51 +00:00
|
|
|
|
|
|
|
#define RX_BUF_SIZE 140
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Size of the Length field in Data Entry, two bytes in this case */
|
|
|
|
typedef uint16_t lensz_t;
|
2018-06-18 17:23:50 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
#define FRAME_OFFSET sizeof(lensz_t)
|
2018-06-18 17:23:50 +00:00
|
|
|
#define FRAME_SHAVE 2 /* RSSI (1) + Status (1) */
|
2018-06-15 14:37:51 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define MAC_RADIO_RECEIVER_SENSITIVITY_DBM -110
|
|
|
|
#define MAC_RADIO_RECEIVER_SATURATION_DBM 10
|
|
|
|
#define MAC_SPEC_ED_MIN_DBM_ABOVE_RECEIVER_SENSITIVITY 10
|
|
|
|
#define MAC_SPEC_ED_MAX 0xFF
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
#define ED_RF_POWER_MIN_DBM (MAC_RADIO_RECEIVER_SENSITIVITY_DBM + MAC_SPEC_ED_MIN_DBM_ABOVE_RECEIVER_SENSITIVITY)
|
|
|
|
#define ED_RF_POWER_MAX_DBM MAC_RADIO_RECEIVER_SATURATION_DBM
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-19 12:55:18 +00:00
|
|
|
/* RF Core typedefs */
|
|
|
|
typedef dataQueue_t data_queue_t;
|
2018-06-18 17:23:50 +00:00
|
|
|
typedef rfc_dataEntryGeneral_t data_entry_t;
|
2018-06-19 12:55:18 +00:00
|
|
|
typedef rfc_propRxOutput_t rx_output_t;
|
2018-06-18 17:23:50 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Receive buffer entries with room for 1 IEEE 802.15.4 frame in each */
|
2018-06-18 17:23:50 +00:00
|
|
|
typedef union {
|
|
|
|
data_entry_t data_entry;
|
|
|
|
uint8_t buf[RX_BUF_SIZE];
|
|
|
|
} rx_buf_t CC_ALIGN(4);
|
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
typedef struct {
|
|
|
|
/* Outgoing frame buffer */
|
|
|
|
uint8_t tx_buf[TX_BUF_SIZE] CC_ALIGN(4);
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Incoming frame buffers */
|
2018-06-18 17:23:50 +00:00
|
|
|
rx_buf_t rx_bufs[RX_BUF_CNT];
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* RX Data Queue */
|
2018-06-19 12:55:18 +00:00
|
|
|
data_queue_t rx_data_queue;
|
2018-06-15 14:37:51 +00:00
|
|
|
/* RX Statistics struct */
|
2018-06-19 12:55:18 +00:00
|
|
|
rx_output_t rx_stats;
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Receive entry pointer to keep track of read items */
|
2018-06-18 17:23:50 +00:00
|
|
|
data_entry_t* rx_read_entry;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* RSSI Threshold */
|
|
|
|
int8_t rssi_threshold;
|
|
|
|
|
|
|
|
/* Indicates RF is supposed to be on or off */
|
|
|
|
uint8_t rf_is_on;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* RF driver */
|
|
|
|
RF_Object rf_object;
|
|
|
|
RF_Handle rf_handle;
|
|
|
|
} prop_radio_t;
|
|
|
|
|
|
|
|
static prop_radio_t prop_radio;
|
2018-02-08 10:45:47 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
#define cmd_radio_setup (*(volatile rfc_CMD_PROP_RADIO_DIV_SETUP_t *)&rf_cmd_prop_radio_div_setup)
|
|
|
|
#define cmd_fs (*(volatile rfc_CMD_FS_t *) &rf_cmd_prop_fs)
|
|
|
|
#define cmd_tx (*(volatile rfc_CMD_PROP_TX_ADV_t *) &rf_cmd_prop_tx_adv)
|
|
|
|
#define cmd_rx (*(volatile rfc_CMD_PROP_RX_ADV_t *) &rf_cmd_prop_rx_adv)
|
2018-02-14 16:56:35 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static CC_INLINE bool tx_active(void) { return cmd_tx.status == ACTIVE; }
|
|
|
|
static CC_INLINE bool rx_active(void) { return cmd_rx.status == ACTIVE; }
|
2018-02-09 08:13:12 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static int on(void);
|
|
|
|
static int off(void);
|
2018-02-09 11:50:55 +00:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
2018-06-15 14:37:51 +00:00
|
|
|
cmd_rx_cb(RF_Handle client, RF_CmdHandle command, RF_EventMask events)
|
2018-02-09 11:50:55 +00:00
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Unused arguments */
|
|
|
|
(void)client;
|
|
|
|
(void)command;
|
|
|
|
|
|
|
|
if (events & RF_EventRxEntryDone) {
|
|
|
|
process_poll(&rf_process);
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static cmd_result_t
|
|
|
|
start_rx(void)
|
2018-02-09 11:50:55 +00:00
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
cmd_rx.status = IDLE;
|
|
|
|
RF_CmdHandle rx_handle = RF_postCmd(prop_radio.rf_handle, (RF_Op*)&cmd_rx, RF_PriorityNormal,
|
|
|
|
&cmd_rx_cb, RF_EventRxEntryDone);
|
|
|
|
if (rx_handle == RF_ALLOC_ERROR) {
|
|
|
|
PRINTF("start_rx: RF_ALLOC_ERROR for cmd_rx\n");
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Wait to enter RX */
|
|
|
|
const rtimer_clock_t t0 = RTIMER_NOW();
|
|
|
|
while (!rx_active() &&
|
|
|
|
(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ENTER_RX_WAIT_TIMEOUT)));
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
if (!rx_active()) {
|
|
|
|
PRINTF("cmd_rx: status=0x%04x\n", cmd_rx.status);
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_OK;
|
2018-02-09 11:50:55 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static cmd_result_t
|
|
|
|
stop_rx(void)
|
2018-02-09 11:50:55 +00:00
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Abort any ongoing operation. Don't care about the result. */
|
2018-06-18 17:23:50 +00:00
|
|
|
RF_cancelCmd(prop_radio.rf_handle, RF_CMDHANDLE_FLUSH_ALL, RF_ABORT_GRACEFULLY);
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
if (cmd_rx.status != PROP_DONE_STOPPED &&
|
|
|
|
cmd_rx.status != PROP_DONE_ABORT) {
|
|
|
|
PRINTF("RF_cmdPropRxAdv cancel: status=0x%04x\n",
|
|
|
|
cmd_rx.status);
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Stopped gracefully */
|
|
|
|
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
|
|
|
|
return CMD_RESULT_OK;
|
2018-02-14 16:56:35 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static cmd_result_t
|
2018-02-09 11:50:55 +00:00
|
|
|
rf_run_setup()
|
|
|
|
{
|
2018-06-19 12:55:18 +00:00
|
|
|
/* TODO not the right way to do this. */
|
2018-06-18 17:23:50 +00:00
|
|
|
RF_runCmd(prop_radio.rf_handle, (RF_Op*)&cmd_radio_setup, RF_PriorityNormal, NULL, 0);
|
|
|
|
if (cmd_radio_setup.status != PROP_DONE_OK) {
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
return CMD_RESULT_OK;
|
2018-02-09 11:50:55 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-08 10:45:47 +00:00
|
|
|
static radio_value_t
|
|
|
|
get_rssi(void)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
if (tx_active()) {
|
|
|
|
PRINTF("get_rssi: called while in TX\n");
|
|
|
|
return RF_GET_RSSI_ERROR_VAL;
|
|
|
|
}
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
const bool was_off = !rx_active();
|
|
|
|
if (was_off && start_rx() == CMD_RESULT_ERROR) {
|
|
|
|
PRINTF("get_rssi: unable to start RX\n");
|
|
|
|
return RF_GET_RSSI_ERROR_VAL;
|
|
|
|
}
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
int8_t rssi = RF_GET_RSSI_ERROR_VAL;
|
|
|
|
while(rssi == RF_GET_RSSI_ERROR_VAL || rssi == 0) {
|
|
|
|
rssi = RF_getRssi(prop_radio.rf_handle);
|
|
|
|
}
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
if (was_off) {
|
|
|
|
stop_rx();
|
|
|
|
}
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return rssi;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static uint8_t
|
|
|
|
get_channel(void)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
uint32_t freq_khz = cmd_fs.frequency * 1000;
|
2018-02-09 08:13:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For some channels, fractFreq * 1000 / 65536 will return 324.99xx.
|
|
|
|
* Casting the result to uint32_t will truncate decimals resulting in the
|
|
|
|
* function returning channel - 1 instead of channel. Thus, we do a quick
|
|
|
|
* positive integer round up.
|
|
|
|
*/
|
2018-06-15 14:37:51 +00:00
|
|
|
freq_khz += (((cmd_fs.fractFreq * 1000) + 65535) / 65536);
|
2018-02-09 08:13:12 +00:00
|
|
|
|
|
|
|
return (freq_khz - DOT_15_4G_CHAN0_FREQUENCY) / DOT_15_4G_CHANNEL_SPACING;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static cmd_result_t
|
2018-06-19 12:55:18 +00:00
|
|
|
set_channel(uint32_t channel)
|
2018-02-08 10:45:47 +00:00
|
|
|
{
|
2018-06-19 12:55:18 +00:00
|
|
|
const uint32_t new_freq = DOT_15_4G_CHAN0_FREQUENCY + (channel * DOT_15_4G_CHANNEL_SPACING);
|
|
|
|
const uint32_t freq = new_freq / 1000;
|
|
|
|
const uint32_t frac = ((new_freq - (freq * 1000)) * 65536) / 1000;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
PRINTF("set_channel: %u = 0x%04x.0x%04x (%lu)\n",
|
2018-06-19 12:55:18 +00:00
|
|
|
(int)channel, (uint16_t)freq, (uint16_t)frac, new_freq);
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
cmd_radio_setup.centerFreq = freq;
|
2018-06-19 12:55:18 +00:00
|
|
|
cmd_fs.frequency = (uint16_t)freq;
|
|
|
|
cmd_fs.fractFreq = (uint16_t)frac;
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* We don't care whether the FS command is successful because subsequent */
|
|
|
|
/* TX and RX commands will tell us indirectly. */
|
2018-06-15 14:37:51 +00:00
|
|
|
RF_EventMask rf_events = RF_runCmd(prop_radio.rf_handle, (RF_Op*)&cmd_fs,
|
|
|
|
RF_PriorityNormal, NULL, 0);
|
|
|
|
if ((rf_events & (RF_EventCmdDone | RF_EventLastCmdDone)) == 0) {
|
|
|
|
PRINTF("set_channel: RF_runCmd failed, events=0x%llx\n", rf_events);
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_fs.status != DONE_OK) {
|
|
|
|
PRINTF("set_channel: cmd_fs failed, status=0x%04x\n", cmd_fs.status);
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 08:13:12 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_OK;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Returns the current TX power in dBm */
|
2018-05-31 15:17:07 +00:00
|
|
|
static radio_result_t
|
2018-06-19 12:55:18 +00:00
|
|
|
set_tx_power(const radio_value_t dBm)
|
2018-02-08 10:45:47 +00:00
|
|
|
{
|
2018-06-19 12:55:18 +00:00
|
|
|
if (!TX_POWER_IN_RANGE(dBm)) {
|
2018-05-31 15:17:07 +00:00
|
|
|
return RADIO_RESULT_INVALID_VALUE;
|
2018-02-14 16:56:35 +00:00
|
|
|
}
|
2018-06-19 12:55:18 +00:00
|
|
|
|
|
|
|
const RF_TxPowerTable_Value value = RF_TxPowerTable_findValue(TX_POWER_TABLE, (int8_t)dBm);
|
|
|
|
const RF_Stat stat = RF_setTxPower(prop_radio.rf_handle, value);
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-05-31 15:17:07 +00:00
|
|
|
return (stat == RF_StatSuccess)
|
|
|
|
? RADIO_RESULT_OK
|
|
|
|
: RADIO_RESULT_ERROR;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Returns the current TX power in dBm */
|
|
|
|
static radio_value_t
|
|
|
|
get_tx_power(void)
|
|
|
|
{
|
|
|
|
const RF_TxPowerTable_Value value = RF_getTxPower(prop_radio.rf_handle);
|
|
|
|
return (radio_value_t)RF_TxPowerTable_findPowerLevel(TX_POWER_TABLE, value);
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-15 14:37:51 +00:00
|
|
|
static uint8_t
|
|
|
|
calculate_lqi(int8_t rssi)
|
|
|
|
{
|
|
|
|
/* Note : Currently the LQI value is simply the energy detect measurement.
|
|
|
|
* A more accurate value could be derived by using the correlation
|
|
|
|
* value along with the RSSI value. */
|
|
|
|
rssi = CLAMP(rssi, ED_RF_POWER_MIN_DBM, ED_RF_POWER_MAX_DBM);
|
|
|
|
|
|
|
|
/* Create energy detect measurement by normalizing and scaling RF power level.
|
|
|
|
* Note : The division operation below is designed for maximum accuracy and
|
|
|
|
* best granularity. This is done by grouping the math operations to
|
|
|
|
* compute the entire numerator before doing any division. */
|
|
|
|
return (MAC_SPEC_ED_MAX * (rssi - ED_RF_POWER_MIN_DBM)) / (ED_RF_POWER_MAX_DBM - ED_RF_POWER_MIN_DBM);
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-08 10:45:47 +00:00
|
|
|
static void
|
|
|
|
init_rx_buffers(void)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
size_t i = 0;
|
2018-06-18 17:23:50 +00:00
|
|
|
for (i = 0; i < RX_BUF_CNT; ++i) {
|
|
|
|
const data_entry_t data_entry = {
|
2018-06-15 14:37:51 +00:00
|
|
|
.status = DATA_ENTRY_PENDING,
|
|
|
|
.config.type = DATA_ENTRY_TYPE_GEN,
|
2018-06-19 12:55:18 +00:00
|
|
|
.config.lenSz = sizeof(lensz_t),
|
2018-06-18 17:23:50 +00:00
|
|
|
.length = RX_BUF_SIZE - sizeof(data_entry_t), /* TODO: is this sizeof sound? */
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Point to fist entry if this is last entry, else point to next entry */
|
|
|
|
.pNextEntry = (i == (RX_BUF_CNT - 1))
|
2018-06-18 17:23:50 +00:00
|
|
|
? prop_radio.rx_bufs[0].buf
|
|
|
|
: prop_radio.rx_bufs[i].buf
|
2018-06-15 14:37:51 +00:00
|
|
|
};
|
|
|
|
/* Write back data entry struct */
|
2018-06-18 17:23:50 +00:00
|
|
|
prop_radio.rx_bufs[i].data_entry = data_entry;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
prepare(const void *payload, unsigned short payload_len)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
int len = MIN(payload_len, TX_BUF_PAYLOAD_LEN);
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
memcpy(prop_radio.tx_buf + TX_BUF_HDR_LEN, payload, len);
|
|
|
|
return 0;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
transmit(unsigned short transmit_len)
|
|
|
|
{
|
2018-06-18 17:23:50 +00:00
|
|
|
int ret = RADIO_TX_OK;
|
2018-06-15 14:37:51 +00:00
|
|
|
|
|
|
|
if (tx_active()) {
|
|
|
|
PRINTF("transmit: not allowed while transmitting\n");
|
|
|
|
return RADIO_TX_ERR;
|
2018-06-18 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const bool rx_is_on = rx_active();
|
|
|
|
if (rx_is_on) {
|
2018-06-15 14:37:51 +00:00
|
|
|
stop_rx();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Length in .15.4g PHY HDR. Includes the CRC but not the HDR itself */
|
2018-06-18 17:23:50 +00:00
|
|
|
uint16_t total_length = transmit_len + CRC_LEN;
|
|
|
|
/* Prepare the .15.4g PHY header
|
|
|
|
* MS=0, Length MSBits=0, DW and CRC configurable
|
|
|
|
* Total length = transmit_len (payload) + CRC length
|
|
|
|
*
|
|
|
|
* The Radio will flip the bits around, so tx_buf[0] must have the length
|
|
|
|
* LSBs (PHR[15:8] and tx_buf[1] will have PHR[7:0] */
|
2018-06-15 14:37:51 +00:00
|
|
|
prop_radio.tx_buf[0] = total_length & 0xFF;
|
|
|
|
prop_radio.tx_buf[1] = (total_length >> 8) + DOT_4G_PHR_DW_BIT + DOT_4G_PHR_CRC_BIT;
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
/* pktLen: Total number of bytes in the TX buffer, including the header if
|
|
|
|
* one exists, but not including the CRC (which is not present in the buffer) */
|
2018-06-15 14:37:51 +00:00
|
|
|
cmd_tx.pktLen = transmit_len + DOT_4G_PHR_LEN;
|
|
|
|
cmd_tx.pPkt = prop_radio.tx_buf;
|
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
RF_CmdHandle tx_handle = RF_postCmd(prop_radio.rf_handle, (RF_Op*)&cmd_tx, RF_PriorityNormal, NULL, 0);
|
|
|
|
if (tx_handle == RF_ALLOC_ERROR) {
|
|
|
|
/* Failure sending the CMD_PROP_TX command */
|
|
|
|
PRINTF("transmit: unable to allocate RF command handle\n");
|
|
|
|
return RADIO_TX_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
|
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* watchdog_periodic(); */
|
2018-06-18 17:23:50 +00:00
|
|
|
|
|
|
|
/* Idle away while the command is running */
|
|
|
|
RF_EventMask rf_events = RF_pendCmd(prop_radio.rf_handle, tx_handle, RF_EventLastCmdDone);
|
|
|
|
|
|
|
|
if ((rf_events & (RF_EventCmdDone | RF_EventLastCmdDone)) == 0) {
|
|
|
|
PRINTF("transmit: RF_pendCmd failed, events=0x%llx\n", rf_events);
|
|
|
|
ret = RADIO_TX_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (cmd_tx.status != PROP_DONE_OK) {
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Operation completed, but frame was not sent */
|
|
|
|
PRINTF("transmit: Not Sent OK status=0x%04x\n",
|
|
|
|
cmd_tx.status);
|
|
|
|
ret = RADIO_TX_ERR;
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Workaround. Set status to IDLE */
|
|
|
|
cmd_tx.status = IDLE;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
if (rx_is_on) {
|
2018-06-15 14:37:51 +00:00
|
|
|
start_rx();
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return ret;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
send(const void *payload, unsigned short payload_len)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
prepare(payload, payload_len);
|
|
|
|
return transmit(payload_len);
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-06-18 17:23:50 +00:00
|
|
|
static void
|
|
|
|
release_data_entry(void)
|
|
|
|
{
|
|
|
|
data_entry_t *const data_entry = prop_radio.rx_read_entry;
|
|
|
|
uint8_t *const frame_ptr = (uint8_t*)&data_entry->data;
|
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Clear the Length byte(s) and set status to 0: "Pending" */
|
2018-06-18 17:23:50 +00:00
|
|
|
*(lensz_t*)frame_ptr = 0;
|
|
|
|
data_entry->status = DATA_ENTRY_PENDING;
|
|
|
|
prop_radio.rx_read_entry = (data_entry_t*)data_entry->pNextEntry;
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-08 10:45:47 +00:00
|
|
|
static int
|
2018-06-15 14:37:51 +00:00
|
|
|
read(void *buf, unsigned short buf_len)
|
2018-02-08 10:45:47 +00:00
|
|
|
{
|
2018-06-18 17:23:50 +00:00
|
|
|
volatile data_entry_t *data_entry = prop_radio.rx_read_entry;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
const rtimer_clock_t t0 = RTIMER_NOW();
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Only wait if the Radio timer is accessing the entry */
|
2018-06-18 17:23:50 +00:00
|
|
|
while ((data_entry->status == DATA_ENTRY_BUSY) &&
|
|
|
|
RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + TIMEOUT_DATA_ENTRY_BUSY));
|
|
|
|
|
|
|
|
if (data_entry->status != DATA_ENTRY_FINISHED) {
|
2018-06-19 12:55:18 +00:00
|
|
|
/* No available data */
|
2018-06-15 14:37:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First 2 bytes in the data entry are the length.
|
2018-06-18 17:23:50 +00:00
|
|
|
* Data frame is on the following format:
|
|
|
|
* Length (2) + Payload (N) + RSSI (1) + Status (1)
|
|
|
|
* Data frame DOES NOT contain the following:
|
|
|
|
* no Header/PHY bytes
|
|
|
|
* no appended Received CRC bytes
|
|
|
|
* no Timestamp bytes
|
|
|
|
* +---------+---------+--------+--------+
|
|
|
|
* | 2 bytes | N bytes | 1 byte | 1 byte |
|
|
|
|
* +---------+---------+--------+--------+
|
|
|
|
* | Length | Payload | RSSI | Status |
|
|
|
|
* +---------+---------+--------+--------+
|
|
|
|
* Length bytes equal total length of entire frame excluding itself,
|
|
|
|
* i.e.: Length = N + RSSI (1) + Status (1)
|
|
|
|
* = N + 2
|
|
|
|
* N = Length - 2 */
|
|
|
|
|
|
|
|
uint8_t *const frame_ptr = (uint8_t*)&data_entry->data;
|
|
|
|
const lensz_t frame_len = *(lensz_t*)frame_ptr;
|
|
|
|
|
|
|
|
/* Sanity check that Frame is at least Frame Shave bytes long */
|
|
|
|
if (frame_len < FRAME_SHAVE) {
|
|
|
|
PRINTF("read: frame too short len=%d\n", frame_len);
|
|
|
|
release_data_entry();
|
|
|
|
return 0;
|
|
|
|
}
|
2018-06-15 14:37:51 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
const uint8_t *payload_ptr = frame_ptr + sizeof(lensz_t);
|
|
|
|
const unsigned short payload_len = (unsigned short)(frame_len - FRAME_SHAVE);
|
2018-06-15 14:37:51 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
/* Sanity check that Payload fits in Buffer */
|
|
|
|
if (payload_len > buf_len) {
|
|
|
|
PRINTF("read: payload too large for buffer len=%d buf_len=%d\n", payload_len, buf_len);
|
|
|
|
release_data_entry();
|
|
|
|
return 0;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
memcpy(buf, payload_ptr, payload_len);
|
2018-06-15 14:37:51 +00:00
|
|
|
|
2018-06-18 17:23:50 +00:00
|
|
|
/* RSSI stored after payload */
|
|
|
|
const int8_t rssi = (int8_t)payload_ptr[payload_len];
|
|
|
|
/* LQI calculated from RSSI */
|
|
|
|
const uint8_t lqi = calculate_lqi(rssi);
|
|
|
|
|
|
|
|
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, (packetbuf_attr_t)rssi);
|
|
|
|
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, (packetbuf_attr_t)lqi);
|
|
|
|
|
|
|
|
release_data_entry();
|
|
|
|
return (int)payload_len;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
channel_clear(void)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
if (tx_active()) {
|
|
|
|
PRINTF("channel_clear: called while in TX\n");
|
2018-06-19 12:55:18 +00:00
|
|
|
return CCA_STATE_IDLE;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
2018-02-08 15:41:27 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
const bool rx_was_off = !rx_active();
|
|
|
|
if (rx_was_off) {
|
|
|
|
start_rx();
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
int8_t rssi = RF_GET_RSSI_ERROR_VAL;
|
|
|
|
while (rssi == RF_GET_RSSI_ERROR_VAL || rssi == 0) {
|
|
|
|
rssi = RF_getRssi(prop_radio.rf_handle);
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
if (rx_was_off) {
|
|
|
|
stop_rx();
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
if(rssi >= prop_radio.rssi_threshold) {
|
2018-06-19 12:55:18 +00:00
|
|
|
return CCA_STATE_BUSY;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
return CCA_STATE_IDLE;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
receiving_packet(void)
|
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
if (!rx_active()) {
|
2018-06-18 17:23:50 +00:00
|
|
|
return 0;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
if (channel_clear() == CCA_STATE_IDLE) {
|
2018-06-18 17:23:50 +00:00
|
|
|
return 0;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return 1;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
|
|
pending_packet(void)
|
|
|
|
{
|
2018-06-19 12:55:18 +00:00
|
|
|
const data_entry_t *const read_entry = prop_radio.rx_read_entry;
|
|
|
|
volatile const data_entry_t *curr_entry = read_entry;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
int num_pending = 0;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Go through RX Circular buffer and check their status */
|
|
|
|
do {
|
2018-06-19 12:55:18 +00:00
|
|
|
const uint8_t status = curr_entry->status;
|
2018-06-15 14:37:51 +00:00
|
|
|
if ((status == DATA_ENTRY_FINISHED) ||
|
|
|
|
(status == DATA_ENTRY_BUSY)) {
|
2018-06-19 12:55:18 +00:00
|
|
|
num_pending += 1;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Stop when we have looped the circular buffer */
|
|
|
|
curr_entry = (data_entry_t *)curr_entry->pNextEntry;
|
|
|
|
} while (curr_entry != read_entry);
|
2018-06-15 14:37:51 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
if (num_pending > 0) {
|
2018-06-15 14:37:51 +00:00
|
|
|
process_poll(&rf_process);
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* If we didn't find an entry at status finished, no frames are pending */
|
2018-06-19 12:55:18 +00:00
|
|
|
return num_pending;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
2018-06-15 14:37:51 +00:00
|
|
|
on(void)
|
2018-02-08 10:45:47 +00:00
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
if (prop_radio.rf_is_on) {
|
2018-06-18 17:23:50 +00:00
|
|
|
PRINTF("on: Radio already on\n");
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset all RF command statuses */
|
|
|
|
cmd_fs.status = IDLE;
|
|
|
|
cmd_tx.status = IDLE;
|
|
|
|
cmd_rx.status = IDLE;
|
|
|
|
|
|
|
|
init_rx_buffers();
|
|
|
|
|
|
|
|
const int rx_ok = start_rx();
|
|
|
|
if (!rx_ok) {
|
|
|
|
off();
|
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop_radio.rf_is_on = true;
|
|
|
|
return CMD_RESULT_OK;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int
|
2018-06-15 14:37:51 +00:00
|
|
|
off(void)
|
2018-02-08 10:45:47 +00:00
|
|
|
{
|
2018-06-15 14:37:51 +00:00
|
|
|
if (!prop_radio.rf_is_on) {
|
2018-06-18 17:23:50 +00:00
|
|
|
PRINTF("off: Radio already off\n");
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_OK;
|
|
|
|
}
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Force abort of any ongoing RF operation */
|
2018-06-15 14:37:51 +00:00
|
|
|
RF_flushCmd(prop_radio.rf_handle, RF_CMDHANDLE_FLUSH_ALL, RF_ABORT_GRACEFULLY);
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Trigger a manual power-down */
|
2018-06-15 14:37:51 +00:00
|
|
|
RF_yield(prop_radio.rf_handle);
|
2018-02-08 10:45:47 +00:00
|
|
|
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Reset RX buffers if there was an ongoing RX */
|
2018-06-18 17:23:50 +00:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < RX_BUF_CNT; ++i) {
|
|
|
|
data_entry_t *entry = &prop_radio.rx_bufs[i].data_entry;
|
|
|
|
if (entry->status == DATA_ENTRY_BUSY) {
|
|
|
|
entry->status = DATA_ENTRY_PENDING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
prop_radio.rf_is_on = false;
|
|
|
|
return CMD_RESULT_OK;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static radio_result_t
|
|
|
|
get_value(radio_param_t param, radio_value_t *value)
|
|
|
|
{
|
2018-06-07 08:29:47 +00:00
|
|
|
if (!value) {
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
|
|
}
|
|
|
|
|
2018-06-07 08:29:47 +00:00
|
|
|
switch (param) {
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_POWER_MODE:
|
|
|
|
/* On / off */
|
2018-06-15 14:37:51 +00:00
|
|
|
*value = (prop_radio.rf_is_on)
|
|
|
|
? RADIO_POWER_MODE_ON
|
|
|
|
: RADIO_POWER_MODE_OFF;
|
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_CHANNEL:
|
|
|
|
*value = (radio_value_t)get_channel();
|
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_TXPOWER:
|
|
|
|
*value = get_tx_power();
|
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_CCA_THRESHOLD:
|
2018-06-15 14:37:51 +00:00
|
|
|
*value = prop_radio.rssi_threshold;
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_RSSI:
|
|
|
|
*value = get_rssi();
|
2018-06-15 14:37:51 +00:00
|
|
|
return (*value == RF_GET_RSSI_ERROR_VAL)
|
2018-06-07 08:29:47 +00:00
|
|
|
? RADIO_RESULT_ERROR
|
|
|
|
: RADIO_RESULT_OK;
|
2018-02-08 10:45:47 +00:00
|
|
|
|
|
|
|
case RADIO_CONST_CHANNEL_MIN:
|
|
|
|
*value = 0;
|
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_CONST_CHANNEL_MAX:
|
|
|
|
*value = DOT_15_4G_CHANNEL_MAX;
|
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_CONST_TXPOWER_MIN:
|
2018-05-31 15:17:07 +00:00
|
|
|
*value = (radio_value_t)TX_POWER_MIN;
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_CONST_TXPOWER_MAX:
|
2018-05-31 15:17:07 +00:00
|
|
|
*value = (radio_value_t)TX_POWER_MAX;
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
default:
|
|
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static radio_result_t
|
|
|
|
set_value(radio_param_t param, radio_value_t value)
|
|
|
|
{
|
|
|
|
switch(param) {
|
|
|
|
case RADIO_PARAM_POWER_MODE:
|
|
|
|
if(value == RADIO_POWER_MODE_ON) {
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Powering on happens implicitly */
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
|
|
|
}
|
|
|
|
if(value == RADIO_POWER_MODE_OFF) {
|
2018-06-15 14:37:51 +00:00
|
|
|
off();
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_OK;
|
|
|
|
}
|
|
|
|
return RADIO_RESULT_INVALID_VALUE;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_CHANNEL:
|
|
|
|
if(value < 0 ||
|
|
|
|
value > DOT_15_4G_CHANNEL_MAX) {
|
|
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(get_channel() == (uint8_t)value) {
|
|
|
|
/* We already have that very same channel configured.
|
|
|
|
* Nothing to do here. */
|
|
|
|
return RADIO_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_channel((uint8_t)value);
|
|
|
|
break;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_TXPOWER:
|
2018-05-31 15:17:07 +00:00
|
|
|
return set_tx_power(value);
|
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_RX_MODE:
|
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
case RADIO_PARAM_CCA_THRESHOLD:
|
2018-06-15 14:37:51 +00:00
|
|
|
prop_radio.rssi_threshold = (int8_t)value;
|
2018-02-09 11:50:55 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-06-07 08:29:47 +00:00
|
|
|
|
2018-02-08 10:45:47 +00:00
|
|
|
default:
|
|
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we reach here we had no errors. Apply new settings */
|
2018-06-15 14:37:51 +00:00
|
|
|
if (rx_active()) {
|
|
|
|
stop_rx();
|
2018-06-19 12:55:18 +00:00
|
|
|
/* TODO fix this */
|
2018-06-15 14:37:51 +00:00
|
|
|
if (rf_run_setup() != CMD_RESULT_OK) {
|
2018-02-08 10:45:47 +00:00
|
|
|
return RADIO_RESULT_ERROR;
|
2018-06-15 14:37:51 +00:00
|
|
|
}
|
|
|
|
start_rx();
|
|
|
|
} else if (tx_active()) {
|
2018-06-19 12:55:18 +00:00
|
|
|
/* Should not happen. TX is always synchronous and blocking. */
|
|
|
|
/* Todo: maybe remove completely here. */
|
2018-06-15 14:37:51 +00:00
|
|
|
PRINTF("set_value: cannot apply new value while transmitting. \n");
|
|
|
|
return RADIO_RESULT_ERROR;
|
2018-02-09 11:50:55 +00:00
|
|
|
} else {
|
2018-06-19 12:55:18 +00:00
|
|
|
/* was powered off. Nothing to do. New values will be */
|
|
|
|
/* applied automatically on next power-up. */
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 11:50:55 +00:00
|
|
|
return RADIO_RESULT_OK;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static radio_result_t
|
|
|
|
get_object(radio_param_t param, void *dest, size_t size)
|
|
|
|
{
|
2018-06-18 17:23:50 +00:00
|
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static radio_result_t
|
|
|
|
set_object(radio_param_t param, const void *src, size_t size)
|
|
|
|
{
|
2018-06-18 17:23:50 +00:00
|
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
2018-02-08 10:45:47 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-09 11:50:55 +00:00
|
|
|
static int
|
2018-06-15 14:37:51 +00:00
|
|
|
init(void)
|
2018-02-09 11:50:55 +00:00
|
|
|
{
|
2018-06-19 12:55:18 +00:00
|
|
|
if (prop_radio.rf_handle) {
|
|
|
|
PRINTF("init: Radio already initialized\n");
|
|
|
|
return CMD_RESULT_OK;
|
|
|
|
}
|
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Zero initalize TX and RX buffers */
|
2018-06-18 17:23:50 +00:00
|
|
|
memset(prop_radio.tx_buf, 0x0, sizeof(prop_radio.tx_buf));
|
|
|
|
memset(prop_radio.rx_bufs, 0x0, sizeof(prop_radio.rx_bufs));
|
2018-06-15 14:37:51 +00:00
|
|
|
|
|
|
|
/* Circular buffer, no last entry */
|
2018-06-18 17:23:50 +00:00
|
|
|
prop_radio.rx_data_queue.pCurrEntry = prop_radio.rx_bufs[0].buf;
|
2018-06-15 14:37:51 +00:00
|
|
|
prop_radio.rx_data_queue.pLastEntry = NULL;
|
|
|
|
|
|
|
|
/* Initialize current read pointer to first element (used in ISR) */
|
2018-06-18 17:23:50 +00:00
|
|
|
prop_radio.rx_read_entry = &prop_radio.rx_bufs[0].data_entry;
|
2018-06-15 14:37:51 +00:00
|
|
|
|
|
|
|
/* Set configured RSSI threshold */
|
|
|
|
prop_radio.rssi_threshold = PROP_MODE_RSSI_THRESHOLD;
|
|
|
|
|
|
|
|
/* RX is off */
|
|
|
|
prop_radio.rf_is_on = false;
|
|
|
|
|
|
|
|
/* Configure RX command */
|
|
|
|
cmd_rx.maxPktLen = DOT_4G_MAX_FRAME_LEN - cmd_rx.lenOffset;
|
|
|
|
cmd_rx.pQueue = &prop_radio.rx_data_queue;
|
|
|
|
cmd_rx.pOutput = (uint8_t *)&prop_radio.rx_stats;
|
|
|
|
|
|
|
|
/* Init RF params and specify non-default params */
|
|
|
|
RF_Params rf_params;
|
|
|
|
RF_Params_init(&rf_params);
|
2018-06-19 12:55:18 +00:00
|
|
|
rf_params.nInactivityTimeout = 2000; /* 2 ms */
|
2018-06-15 14:37:51 +00:00
|
|
|
|
|
|
|
/* Open RF Driver */
|
|
|
|
prop_radio.rf_handle = RF_open(&prop_radio.rf_object, &rf_prop_mode,
|
|
|
|
(RF_RadioSetup*)&cmd_radio_setup, &rf_params);
|
|
|
|
if (prop_radio.rf_handle == NULL) {
|
2018-06-19 12:55:18 +00:00
|
|
|
PRINTF("init: unable to open RF driver\n");
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_ERROR;
|
|
|
|
}
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
set_channel(PROP_MODE_CHANNEL);
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
/* Start RF process */
|
|
|
|
process_start(&rf_process, NULL);
|
2018-02-09 11:50:55 +00:00
|
|
|
|
2018-06-15 14:37:51 +00:00
|
|
|
return CMD_RESULT_OK;
|
2018-02-09 11:50:55 +00:00
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
2018-02-08 10:45:47 +00:00
|
|
|
const struct radio_driver prop_mode_driver = {
|
2018-06-15 14:37:51 +00:00
|
|
|
init,
|
2018-02-08 10:45:47 +00:00
|
|
|
prepare,
|
|
|
|
transmit,
|
|
|
|
send,
|
2018-06-15 14:37:51 +00:00
|
|
|
read,
|
2018-02-08 10:45:47 +00:00
|
|
|
channel_clear,
|
|
|
|
receiving_packet,
|
|
|
|
pending_packet,
|
2018-06-15 14:37:51 +00:00
|
|
|
on,
|
|
|
|
off,
|
2018-02-08 10:45:47 +00:00
|
|
|
get_value,
|
|
|
|
set_value,
|
|
|
|
get_object,
|
|
|
|
set_object,
|
|
|
|
};
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
|
|
* @}
|
|
|
|
*/
|