f0c48c55ca
set a transmission power outside the valid range instead of using closest valid value.
868 lines
24 KiB
C
868 lines
24 KiB
C
/**
|
|
* \addtogroup stm32w-cpu
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2010, STMicroelectronics.
|
|
* 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. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* Machine dependent STM32W radio code.
|
|
* \author
|
|
* Salvatore Pitrulli
|
|
* Chi-Anh La la@imag.fr
|
|
* Simon Duquennoy <simonduq@sics.se>
|
|
*/
|
|
|
|
#include PLATFORM_HEADER
|
|
#include "hal/error.h"
|
|
#include "hal/hal.h"
|
|
|
|
#include "contiki.h"
|
|
|
|
#include "net/mac/frame802154.h"
|
|
|
|
#include "dev/stm32w-radio.h"
|
|
#include "net/netstack.h"
|
|
|
|
#include "net/packetbuf.h"
|
|
#include "net/rime/rimestats.h"
|
|
#include "sys/rtimer.h"
|
|
|
|
#define DEBUG 0
|
|
|
|
#include "dev/leds.h"
|
|
#define LED_ACTIVITY 0
|
|
|
|
#ifdef ST_CONF_RADIO_AUTOACK
|
|
#define ST_RADIO_AUTOACK ST_CONF_RADIO_AUTOACK
|
|
#else
|
|
#define ST_RADIO_AUTOACK 0
|
|
#endif /* ST_CONF_RADIO_AUTOACK */
|
|
|
|
#if RDC_CONF_DEBUG_LED
|
|
#define LED_RDC RDC_CONF_DEBUG_LED
|
|
#define LED_ACTIVITY 1
|
|
#else
|
|
#define LED_RDC 0
|
|
#endif /* RDC_CONF_DEBUG_LED */
|
|
|
|
#if DEBUG > 0
|
|
#include <stdio.h>
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define PRINTF(...) do {} while (0)
|
|
#endif /* DEBUG > 0 */
|
|
|
|
#if LED_ACTIVITY
|
|
#define LED_TX_ON() leds_on(LEDS_GREEN)
|
|
#define LED_TX_OFF() leds_off(LEDS_GREEN)
|
|
#define LED_RX_ON() do { \
|
|
if(LED_RDC == 0){ \
|
|
leds_on(LEDS_RED); \
|
|
} \
|
|
} while (0)
|
|
#define LED_RX_OFF() do { \
|
|
if(LED_RDC == 0){ \
|
|
leds_off(LEDS_RED); \
|
|
} \
|
|
} while (0)
|
|
#define LED_RDC_ON() do { \
|
|
if(LED_RDC == 1){ \
|
|
leds_on(LEDS_RED); \
|
|
} \
|
|
} while (0)
|
|
#define LED_RDC_OFF() do { \
|
|
if(LED_RDC == 1){ \
|
|
leds_off(LEDS_RED); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define LED_TX_ON()
|
|
#define LED_TX_OFF()
|
|
#define LED_RX_ON()
|
|
#define LED_RX_OFF()
|
|
#define LED_RDC_ON()
|
|
#define LED_RDC_OFF()
|
|
#endif /* LED_ACTIVITY */
|
|
|
|
#if RDC_CONF_HARDWARE_CSMA
|
|
#define MAC_RETRIES 0
|
|
#endif /* RDC_CONF_HARDWARE_CSMA */
|
|
|
|
#ifndef MAC_RETRIES
|
|
#define MAC_RETRIES 1
|
|
#endif /* MAC_RETRIES */
|
|
|
|
#if MAC_RETRIES
|
|
int8_t mac_retries_left;
|
|
#define INIT_RETRY_CNT() (mac_retries_left = packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS))
|
|
#define DEC_RETRY_CNT() (mac_retries_left--)
|
|
#define RETRY_CNT_GTZ() (mac_retries_left > 0)
|
|
#else
|
|
#define INIT_RETRY_CNT()
|
|
#define DEC_RETRY_CNT()
|
|
#define RETRY_CNT_GTZ() 0
|
|
#endif /* MAC_RETRIES */
|
|
|
|
|
|
/* If set to 1, a send() returns only after the packet has been transmitted.
|
|
This is necessary if you use the x-mac module, for example. */
|
|
#ifndef RADIO_WAIT_FOR_PACKET_SENT
|
|
#define RADIO_WAIT_FOR_PACKET_SENT 1
|
|
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
|
|
#define TO_PREV_STATE() do { \
|
|
if(onoroff == OFF){ \
|
|
ST_RadioSleep(); \
|
|
ENERGEST_OFF(ENERGEST_TYPE_LISTEN); \
|
|
} \
|
|
} while(0)
|
|
#if RDC_CONF_HARDWARE_CSMA
|
|
#define ST_RADIO_CHECK_CCA FALSE
|
|
#define ST_RADIO_CCA_ATTEMPT_MAX 0
|
|
#define ST_BACKOFF_EXP_MIN 0
|
|
#define ST_BACKOFF_EXP_MAX 0
|
|
#else /* RDC_CONF_HARDWARE_CSMA */
|
|
#define ST_RADIO_CHECK_CCA TRUE
|
|
#define ST_RADIO_CCA_ATTEMPT_MAX 4
|
|
#define ST_BACKOFF_EXP_MIN 2
|
|
#define ST_BACKOFF_EXP_MAX 6
|
|
#endif /* RDC_CONF_HARDWARE_CSMA */
|
|
|
|
const RadioTransmitConfig radioTransmitConfig = {
|
|
TRUE, /* waitForAck; */
|
|
ST_RADIO_CHECK_CCA, /* checkCca;Set to FALSE with low-power MACs. */
|
|
ST_RADIO_CCA_ATTEMPT_MAX, /* ccaAttemptMax; */
|
|
ST_BACKOFF_EXP_MIN, /* backoffExponentMin; */
|
|
ST_BACKOFF_EXP_MAX, /* backoffExponentMax; */
|
|
TRUE /* appendCrc; */
|
|
};
|
|
|
|
#define MAC_RETRIES 0
|
|
|
|
/*
|
|
* The buffers which hold incoming data.
|
|
*/
|
|
#ifndef RADIO_RXBUFS
|
|
#define RADIO_RXBUFS 1
|
|
#endif /* RADIO_RXBUFS */
|
|
|
|
/* +1 because of the first byte, which will contain the length of the packet. */
|
|
static uint8_t stm32w_rxbufs[RADIO_RXBUFS][STM32W_MAX_PACKET_LEN + 1];
|
|
|
|
#if RADIO_RXBUFS > 1
|
|
static volatile int8_t first = -1, last = 0;
|
|
#else /* RADIO_RXBUFS > 1 */
|
|
static const int8_t first = 0, last = 0;
|
|
#endif /* RADIO_RXBUFS > 1 */
|
|
|
|
#if RADIO_RXBUFS > 1
|
|
#define CLEAN_RXBUFS() do{first = -1; last = 0;}while(0)
|
|
#define RXBUFS_EMPTY() (first == -1)
|
|
int
|
|
RXBUFS_FULL()
|
|
{
|
|
int8_t first_tmp = first;
|
|
return first_tmp == last;
|
|
}
|
|
#else /* RADIO_RXBUFS > 1 */
|
|
#define CLEAN_RXBUFS() (stm32w_rxbufs[0][0] = 0)
|
|
#define RXBUFS_EMPTY() (stm32w_rxbufs[0][0] == 0)
|
|
#define RXBUFS_FULL() (stm32w_rxbufs[0][0] != 0)
|
|
#endif /* RADIO_RXBUFS > 1 */
|
|
|
|
static uint8_t
|
|
__attribute__ ((aligned(2))) stm32w_txbuf[STM32W_MAX_PACKET_LEN + 1];
|
|
|
|
|
|
#define CLEAN_TXBUF() (stm32w_txbuf[0] = 0)
|
|
#define TXBUF_EMPTY() (stm32w_txbuf[0] == 0)
|
|
#define CHECKSUM_LEN 2
|
|
|
|
/*
|
|
* The transceiver state.
|
|
*/
|
|
#define ON 0
|
|
#define OFF 1
|
|
|
|
#define BUSYWAIT_UNTIL(cond, max_time) \
|
|
do { \
|
|
rtimer_clock_t t0; \
|
|
t0 = RTIMER_NOW(); \
|
|
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time))); \
|
|
} while(0)
|
|
|
|
#define GET_LOCK() locked++
|
|
|
|
static volatile uint8_t onoroff = OFF;
|
|
static uint8_t receiving_packet = 0;
|
|
static int8_t last_rssi;
|
|
static volatile StStatus last_tx_status;
|
|
static uint8_t locked;
|
|
static volatile uint8_t is_transmit_ack;
|
|
/*--------------------------------------------------------------------------*/
|
|
static void
|
|
RELEASE_LOCK(void)
|
|
{
|
|
if(locked > 0)
|
|
locked--;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
PROCESS(stm32w_radio_process, "STM32W radio driver");
|
|
/*---------------------------------------------------------------------------*/
|
|
static int stm32w_radio_init(void);
|
|
|
|
static int stm32w_radio_prepare(const void *payload,
|
|
unsigned short payload_len);
|
|
static int stm32w_radio_transmit(unsigned short payload_len);
|
|
|
|
static int stm32w_radio_send(const void *data, unsigned short len);
|
|
|
|
static int stm32w_radio_read(void *buf, unsigned short bufsize);
|
|
|
|
static int stm32w_radio_channel_clear(void);
|
|
|
|
static int stm32w_radio_receiving_packet(void);
|
|
|
|
static int stm32w_radio_pending_packet(void);
|
|
|
|
static int stm32w_radio_on(void);
|
|
|
|
static int stm32w_radio_off(void);
|
|
|
|
static int add_to_rxbuf(uint8_t * src);
|
|
|
|
static int read_from_rxbuf(void *dest, unsigned short len);
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static radio_result_t
|
|
get_value(radio_param_t param, radio_value_t *value)
|
|
{
|
|
if(!value) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
switch(param) {
|
|
case RADIO_PARAM_POWER_MODE:
|
|
*value = onoroff == ON ? RADIO_POWER_MODE_ON : RADIO_POWER_MODE_OFF;
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_CHANNEL:
|
|
*value = ST_RadioGetChannel();
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_PAN_ID:
|
|
*value = ST_RadioGetPanId();
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_16BIT_ADDR:
|
|
*value = ST_RadioGetNodeId();
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_RX_MODE:
|
|
*value = 0;
|
|
if(ST_RadioAddressFilteringEnabled()) {
|
|
*value |= RADIO_RX_MODE_ADDRESS_FILTER;
|
|
}
|
|
if(ST_RadioAutoAckEnabled()) {
|
|
*value |= RADIO_RX_MODE_AUTOACK;
|
|
}
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_TXPOWER:
|
|
*value = ST_RadioGetPower();
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_CCA_THRESHOLD:
|
|
*value = ST_RadioGetEdCcaThreshold();
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_RSSI:
|
|
*value = ST_RadioEnergyDetection();
|
|
return RADIO_RESULT_OK;
|
|
|
|
case RADIO_CONST_CHANNEL_MIN:
|
|
*value = ST_MIN_802_15_4_CHANNEL_NUMBER;
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_CONST_CHANNEL_MAX:
|
|
*value = ST_MAX_802_15_4_CHANNEL_NUMBER;
|
|
return RADIO_RESULT_OK;
|
|
|
|
case RADIO_CONST_TXPOWER_MIN:
|
|
*value = MIN_RADIO_POWER;
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_CONST_TXPOWER_MAX:
|
|
*value = MAX_RADIO_POWER;
|
|
return RADIO_RESULT_OK;
|
|
|
|
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) {
|
|
stm32w_radio_on();
|
|
return RADIO_RESULT_OK;
|
|
}
|
|
if(value == RADIO_POWER_MODE_OFF) {
|
|
stm32w_radio_off();
|
|
return RADIO_RESULT_OK;
|
|
}
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
case RADIO_PARAM_CHANNEL:
|
|
if(value < ST_MIN_802_15_4_CHANNEL_NUMBER ||
|
|
value > ST_MAX_802_15_4_CHANNEL_NUMBER) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
if(ST_RadioSetChannel(value) != ST_SUCCESS) {
|
|
return RADIO_RESULT_ERROR;
|
|
}
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_PAN_ID:
|
|
ST_RadioSetPanId(value & 0xffff);
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_16BIT_ADDR:
|
|
ST_RadioSetNodeId(value & 0xffff);
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_RX_MODE:
|
|
if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER |
|
|
RADIO_RX_MODE_AUTOACK)) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
ST_RadioEnableAddressFiltering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0);
|
|
ST_RadioEnableAutoAck((value & RADIO_RX_MODE_AUTOACK) != 0);
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_TXPOWER:
|
|
if(value < MIN_RADIO_POWER || value > MAX_RADIO_POWER) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
if(ST_RadioSetPower((int8_t)value) != ST_SUCCESS) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
return RADIO_RESULT_OK;
|
|
case RADIO_PARAM_CCA_THRESHOLD:
|
|
ST_RadioSetEdCcaThreshold((int8_t)value);
|
|
return RADIO_RESULT_OK;
|
|
default:
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static radio_result_t
|
|
get_object(radio_param_t param, void *dest, size_t size)
|
|
{
|
|
const uint8_t *eui64;
|
|
uint8_t *target;
|
|
int i;
|
|
|
|
if(param == RADIO_PARAM_64BIT_ADDR) {
|
|
if(size < 8 || !dest) {
|
|
return RADIO_RESULT_INVALID_VALUE;
|
|
}
|
|
eui64 = ST_RadioGetEui64();
|
|
if(!eui64) {
|
|
return RADIO_RESULT_ERROR;
|
|
}
|
|
target = dest;
|
|
for(i = 0; i < 8; i++) {
|
|
target[i] = eui64[7 - i];
|
|
}
|
|
return RADIO_RESULT_OK;
|
|
}
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static radio_result_t
|
|
set_object(radio_param_t param, const void *src, size_t size)
|
|
{
|
|
return RADIO_RESULT_NOT_SUPPORTED;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
const struct radio_driver stm32w_radio_driver = {
|
|
stm32w_radio_init,
|
|
stm32w_radio_prepare,
|
|
stm32w_radio_transmit,
|
|
stm32w_radio_send,
|
|
stm32w_radio_read,
|
|
stm32w_radio_channel_clear,
|
|
stm32w_radio_receiving_packet,
|
|
stm32w_radio_pending_packet,
|
|
stm32w_radio_on,
|
|
stm32w_radio_off,
|
|
get_value,
|
|
set_value,
|
|
get_object,
|
|
set_object
|
|
};
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_init(void)
|
|
{
|
|
/* A channel also needs to be set. */
|
|
ST_RadioSetChannel(RF_CHANNEL);
|
|
|
|
/* Initialize radio (analog section, digital baseband and MAC). */
|
|
/* Leave radio powered up in non-promiscuous rx mode. */
|
|
ST_RadioInit(ST_RADIO_POWER_MODE_OFF);
|
|
|
|
onoroff = OFF;
|
|
ST_RadioSetPanId(IEEE802154_PANID);
|
|
|
|
CLEAN_RXBUFS();
|
|
CLEAN_TXBUF();
|
|
|
|
#if ST_RADIO_AUTOACK && !(UIP_CONF_LL_802154 && LINKADDR_CONF_SIZE==8)
|
|
#error "Autoack and address filtering can only be used with EUI 64"
|
|
#endif
|
|
ST_RadioEnableAutoAck(ST_RADIO_AUTOACK);
|
|
ST_RadioEnableAddressFiltering(ST_RADIO_AUTOACK);
|
|
|
|
locked = 0;
|
|
process_start(&stm32w_radio_process, NULL);
|
|
|
|
return 0;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
stm32w_radio_set_promiscous(uint8_t on)
|
|
{
|
|
if(on) {
|
|
ST_RadioEnableAddressFiltering(0);
|
|
} else {
|
|
ST_RadioEnableAddressFiltering(ST_RADIO_AUTOACK);
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
stm32w_radio_set_channel(uint8_t channel)
|
|
{
|
|
if (ST_RadioSetChannel(channel) == ST_SUCCESS) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
wait_for_tx(void)
|
|
{
|
|
struct timer t;
|
|
timer_set(&t, CLOCK_SECOND / 10);
|
|
while(!TXBUF_EMPTY()) {
|
|
if(timer_expired(&t)) {
|
|
PRINTF("stm32w: tx buffer full.\r\n");
|
|
return 1;
|
|
}
|
|
/* Put CPU in sleep mode. */
|
|
halSleepWithOptions(SLEEPMODE_IDLE, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_prepare(const void *payload, unsigned short payload_len)
|
|
{
|
|
if(payload_len > STM32W_MAX_PACKET_LEN) {
|
|
PRINTF("stm32w: payload length=%d is too long.\r\n", payload_len);
|
|
return RADIO_TX_ERR;
|
|
}
|
|
#if !RADIO_WAIT_FOR_PACKET_SENT
|
|
/*
|
|
* Check if the txbuf is empty. Wait for a finite time.
|
|
* This should not occur if we wait for the end of transmission in
|
|
* stm32w_radio_transmit().
|
|
*/
|
|
if(wait_for_tx()) {
|
|
PRINTF("stm32w: tx buffer full.\r\n");
|
|
return RADIO_TX_ERR;
|
|
}
|
|
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
|
|
/*
|
|
* Copy to the txbuf.
|
|
* The first byte must be the packet length.
|
|
*/
|
|
CLEAN_TXBUF();
|
|
memcpy(stm32w_txbuf + 1, payload, payload_len);
|
|
|
|
return RADIO_TX_OK;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_transmit(unsigned short payload_len)
|
|
{
|
|
stm32w_txbuf[0] = payload_len + CHECKSUM_LEN;
|
|
INIT_RETRY_CNT();
|
|
|
|
if(onoroff == OFF) {
|
|
PRINTF("stm32w: Radio is off, turning it on.\r\n");
|
|
ST_RadioWake();
|
|
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
|
|
}
|
|
#if RADIO_WAIT_FOR_PACKET_SENT
|
|
GET_LOCK();
|
|
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
last_tx_status = -1;
|
|
LED_TX_ON();
|
|
if(ST_RadioTransmit(stm32w_txbuf) == ST_SUCCESS) {
|
|
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
|
|
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
|
|
PRINTF("stm32w: sending %d bytes\r\n", payload_len);
|
|
|
|
#if DEBUG > 1
|
|
for(uint8_t c = 1; c <= stm32w_txbuf[0] - 2; c++) {
|
|
PRINTF("%x:", stm32w_txbuf[c]);
|
|
}
|
|
PRINTF("\r\n");
|
|
#endif
|
|
|
|
#if RADIO_WAIT_FOR_PACKET_SENT
|
|
|
|
if(wait_for_tx()) {
|
|
PRINTF("stm32w: unknown tx error.\r\n");
|
|
TO_PREV_STATE();
|
|
LED_TX_OFF();
|
|
RELEASE_LOCK();
|
|
return RADIO_TX_ERR;
|
|
}
|
|
|
|
TO_PREV_STATE();
|
|
if(last_tx_status == ST_SUCCESS || last_tx_status == ST_PHY_ACK_RECEIVED ||
|
|
last_tx_status == ST_MAC_NO_ACK_RECEIVED) {
|
|
RELEASE_LOCK();
|
|
if(last_tx_status == ST_PHY_ACK_RECEIVED) {
|
|
return RADIO_TX_OK; /* ACK status */
|
|
} else if(last_tx_status == ST_MAC_NO_ACK_RECEIVED ||
|
|
last_tx_status == ST_SUCCESS) {
|
|
return RADIO_TX_NOACK;
|
|
}
|
|
}
|
|
LED_TX_OFF();
|
|
RELEASE_LOCK();
|
|
return RADIO_TX_ERR;
|
|
|
|
#else /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
TO_PREV_STATE();
|
|
LED_TX_OFF();
|
|
return RADIO_TX_OK;
|
|
|
|
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
}
|
|
|
|
#if RADIO_WAIT_FOR_PACKET_SENT
|
|
RELEASE_LOCK();
|
|
#endif /* RADIO_WAIT_FOR_PACKET_SENT */
|
|
TO_PREV_STATE();
|
|
|
|
PRINTF("stm32w: transmission never started.\r\n");
|
|
/* TODO: Do we have to retransmit? */
|
|
|
|
CLEAN_TXBUF();
|
|
LED_TX_OFF();
|
|
return RADIO_TX_ERR;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_send(const void *payload, unsigned short payload_len)
|
|
{
|
|
if (stm32w_radio_prepare(payload, payload_len) == RADIO_TX_ERR) {
|
|
return RADIO_TX_ERR;
|
|
}
|
|
return stm32w_radio_transmit(payload_len);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_channel_clear(void)
|
|
{
|
|
return ST_RadioChannelIsClear();
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_receiving_packet(void)
|
|
{
|
|
return receiving_packet;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_pending_packet(void)
|
|
{
|
|
return !RXBUFS_EMPTY();
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_off(void)
|
|
{
|
|
/* Any transmit or receive packets in progress are aborted.
|
|
* Waiting for end of transmission or reception have to be done.
|
|
*/
|
|
if(locked) {
|
|
PRINTF("stm32w: try to off while sending/receiving (lock=%u).\r\n",
|
|
locked);
|
|
return 0;
|
|
}
|
|
/* off only if there is no transmission or reception of packet. */
|
|
if(onoroff == ON && TXBUF_EMPTY() && !receiving_packet) {
|
|
LED_RDC_OFF();
|
|
ST_RadioSleep();
|
|
onoroff = OFF;
|
|
CLEAN_TXBUF();
|
|
CLEAN_RXBUFS();
|
|
|
|
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_on(void)
|
|
{
|
|
PRINTF("stm32w: turn radio on\n");
|
|
if(onoroff == OFF) {
|
|
LED_RDC_ON();
|
|
ST_RadioWake();
|
|
onoroff = ON;
|
|
|
|
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
stm32w_radio_is_on(void)
|
|
{
|
|
return onoroff == ON;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioReceiveIsrCallback(uint8_t *packet,
|
|
boolean ackFramePendingSet,
|
|
uint32_t time, uint16_t errors, int8_t rssi)
|
|
{
|
|
LED_RX_ON();
|
|
PRINTF("stm32w: incomming packet received\n");
|
|
receiving_packet = 0;
|
|
|
|
/* Copy packet into the buffer. It is better to do this here. */
|
|
if(add_to_rxbuf(packet)) {
|
|
process_poll(&stm32w_radio_process);
|
|
last_rssi = rssi;
|
|
}
|
|
LED_RX_OFF();
|
|
GET_LOCK();
|
|
is_transmit_ack = 1;
|
|
/* Wait for sending ACK */
|
|
BUSYWAIT_UNTIL(!is_transmit_ack, RTIMER_SECOND / 1500);
|
|
RELEASE_LOCK();
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioTxAckIsrCallback(void)
|
|
{
|
|
/*
|
|
* This callback is for simplemac 1.1.0.
|
|
* Till now we block (RTIMER_SECOND / 1500)
|
|
* to prevent radio off during ACK transmission.
|
|
*/
|
|
is_transmit_ack = 0;
|
|
/* RELEASE_LOCK(); */
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioTransmitCompleteIsrCallback(StStatus status,
|
|
uint32_t txSyncTime, boolean framePending)
|
|
{
|
|
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
|
|
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
|
|
LED_TX_OFF();
|
|
|
|
last_tx_status = status;
|
|
|
|
if(status == ST_SUCCESS || status == ST_PHY_ACK_RECEIVED) {
|
|
CLEAN_TXBUF();
|
|
} else {
|
|
if(RETRY_CNT_GTZ()) {
|
|
|
|
/* Retransmission */
|
|
LED_TX_ON();
|
|
if(ST_RadioTransmit(stm32w_txbuf) == ST_SUCCESS) {
|
|
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
|
|
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
|
|
PRINTF("stm32w: retransmission.\r\n");
|
|
DEC_RETRY_CNT();
|
|
} else {
|
|
CLEAN_TXBUF();
|
|
LED_TX_OFF();
|
|
PRINTF("stm32w: retransmission failed.\r\n");
|
|
}
|
|
} else {
|
|
CLEAN_TXBUF();
|
|
}
|
|
}
|
|
|
|
/* Debug outputs. */
|
|
if(status == ST_SUCCESS || status == ST_PHY_ACK_RECEIVED) {
|
|
PRINTF("stm32w: return status TX_END\r\n");
|
|
} else if(status == ST_MAC_NO_ACK_RECEIVED) {
|
|
PRINTF("stm32w: return status TX_END_NOACK\r\n");
|
|
} else if(status == ST_PHY_TX_CCA_FAIL) {
|
|
PRINTF("stm32w: return status TX_END_CCA_FAIL\r\n");
|
|
} else if(status == ST_PHY_TX_UNDERFLOW) {
|
|
PRINTF("stm32w: return status TX_END_UNDERFLOW\r\n");
|
|
} else {
|
|
PRINTF("stm32w: return status TX_END_INCOMPLETE\r\n");
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
boolean
|
|
ST_RadioDataPendingShortIdIsrCallback(uint16_t shortId)
|
|
{
|
|
receiving_packet = 1;
|
|
return FALSE;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
boolean
|
|
ST_RadioDataPendingLongIdIsrCallback(uint8_t * longId)
|
|
{
|
|
receiving_packet = 1;
|
|
return FALSE;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
PROCESS_THREAD(stm32w_radio_process, ev, data)
|
|
{
|
|
int len;
|
|
PROCESS_BEGIN();
|
|
PRINTF("stm32w_radio_process: started\r\n");
|
|
|
|
while(1) {
|
|
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
|
|
PRINTF("stm32w_radio_process: calling receiver callback\r\n");
|
|
|
|
#if DEBUG > 1
|
|
for(uint8_t c = 1; c <= RCVD_PACKET_LEN; c++) {
|
|
PRINTF("%x", stm32w_rxbuf[c]);
|
|
}
|
|
PRINTF("\r\n");
|
|
#endif
|
|
|
|
packetbuf_clear();
|
|
len = stm32w_radio_read(packetbuf_dataptr(), PACKETBUF_SIZE);
|
|
if(len > 0) {
|
|
packetbuf_set_datalen(len);
|
|
NETSTACK_RDC.input();
|
|
}
|
|
if(!RXBUFS_EMPTY()) {
|
|
/*
|
|
* Some data packet still in rx buffer (this happens because process_poll
|
|
* doesn't queue requests), so stm32w_radio_process needs to be called
|
|
* again.
|
|
*/
|
|
process_poll(&stm32w_radio_process);
|
|
}
|
|
}
|
|
PROCESS_END();
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
stm32w_radio_read(void *buf, unsigned short bufsize)
|
|
{
|
|
return read_from_rxbuf(buf, bufsize);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioOverflowIsrCallback(void)
|
|
{
|
|
PRINTF("stm32w: radio overflow\r\n");
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioSfdSentIsrCallback(uint32_t sfdSentTime)
|
|
{
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
ST_RadioMacTimerCompareIsrCallback(void)
|
|
{
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
add_to_rxbuf(uint8_t *src)
|
|
{
|
|
if(RXBUFS_FULL()) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(stm32w_rxbufs[last], src, src[0] + 1);
|
|
#if RADIO_RXBUFS > 1
|
|
last = (last + 1) % RADIO_RXBUFS;
|
|
if(first == -1) {
|
|
first = 0;
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
read_from_rxbuf(void *dest, unsigned short len)
|
|
{
|
|
if(RXBUFS_EMPTY()) { /* Buffers are all empty */
|
|
return 0;
|
|
}
|
|
|
|
if(stm32w_rxbufs[first][0] > len) { /* Too large packet for dest. */
|
|
len = 0;
|
|
} else {
|
|
len = stm32w_rxbufs[first][0];
|
|
memcpy(dest, stm32w_rxbufs[first] + 1, len);
|
|
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, last_rssi);
|
|
}
|
|
|
|
#if RADIO_RXBUFS > 1
|
|
ATOMIC(first = (first + 1) % RADIO_RXBUFS;
|
|
int first_tmp = first; if(first_tmp == last) {
|
|
CLEAN_RXBUFS();}
|
|
)
|
|
#else
|
|
CLEAN_RXBUFS();
|
|
#endif
|
|
|
|
return len;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
short
|
|
last_packet_rssi()
|
|
{
|
|
return last_rssi;
|
|
}
|
|
/** @} */
|