/** * \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 */ #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 #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); /*--------------------------------------------------------------------------*/ 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, }; /*---------------------------------------------------------------------------*/ 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 && RIMEADDR_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; } /** @} */