/* * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/ * 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 simplelink * @{ * * \file * Implementation of common CC13xx/CC26xx RF functionality */ /*---------------------------------------------------------------------------*/ #include "contiki.h" #include "dev/watchdog.h" #include "sys/cc.h" #include "sys/process.h" #include "sys/energest.h" #include "net/netstack.h" #include "net/packetbuf.h" #include "net/mac/mac.h" #include "rf-core.h" /*---------------------------------------------------------------------------*/ #include #include DeviceFamily_constructPath(driverlib/rf_common_cmd.h) #include DeviceFamily_constructPath(driverlib/rf_mailbox.h) #include /*---------------------------------------------------------------------------*/ #include "rf-data-queue.h" #include "netstack-settings.h" /*---------------------------------------------------------------------------*/ #include #include #include /*---------------------------------------------------------------------------*/ /* Log configuration */ #include "sys/log.h" #define LOG_MODULE "RF Core" #define LOG_LEVEL LOG_LEVEL_DBG /*---------------------------------------------------------------------------*/ #ifdef NDEBUG # define PRINTF(...) #else # define PRINTF(...) printf(__VA_ARGS__) #endif /*---------------------------------------------------------------------------*/ #define CMD_FS_RETRIES 3 #define RF_EVENTS_CMD_DONE (RF_EventCmdDone | RF_EventLastCmdDone | \ RF_EventFGCmdDone | RF_EventLastFGCmdDone) #define CMD_STATUS(cmd) (CC_ACCESS_NOW(RF_Op, cmd).status) #define CMD_HANDLE_OK(handle) (((handle) != RF_ALLOC_ERROR) && \ ((handle) != RF_SCHEDULE_CMD_ERROR)) #define EVENTS_CMD_DONE(events) (((events) & RF_EVENTS_CMD_DONE) != 0) /*---------------------------------------------------------------------------*/ static RF_Object rf_netstack; static RF_Object rf_ble; static RF_CmdHandle cmd_rx_handle; /*---------------------------------------------------------------------------*/ static void cmd_rx_cb(RF_Handle client, RF_CmdHandle command, RF_EventMask events) { /* Unused arguments */ (void)client; (void)command; if (events & RF_EventRxEntryDone) { process_poll(&rf_core_process); } if (events & RF_EventRxBufFull) { data_queue_reset(); /* TODO: Check status of RX to verify RX was running before rescheduling */ netstack_sched_rx(); process_poll(&rf_core_process); } } /*---------------------------------------------------------------------------*/ static inline bool cmd_rx_is_active(void) { /* * Active in this case means RX command is either running to be running, * that is ACTIVE for running or PENDING for to be running. */ const uint16_t status = CMD_STATUS(netstack_cmd_rx); return (status == ACTIVE) || (status == PENDING); } /*---------------------------------------------------------------------------*/ static uint_fast8_t cmd_rx_disable(void) { const bool is_active = cmd_rx_is_active(); if (is_active) { CMD_STATUS(netstack_cmd_rx) = DONE_STOPPED; RF_cancelCmd(&rf_netstack, cmd_rx_handle, RF_ABORT_GRACEFULLY); cmd_rx_handle = 0; } return (uint_fast8_t)is_active; } /*---------------------------------------------------------------------------*/ static rf_result_t cmd_rx_restore(uint_fast8_t rx_key) { const bool was_active = (rx_key != 0) ? true : false; if (!was_active) { return RF_RESULT_OK; } RF_ScheduleCmdParams sched_params; RF_ScheduleCmdParams_init(&sched_params); sched_params.priority = RF_PriorityNormal; sched_params.endTime = 0; sched_params.allowDelay = RF_AllowDelayAny; CMD_STATUS(netstack_cmd_rx) = PENDING; cmd_rx_handle = RF_scheduleCmd(&rf_netstack, (RF_Op*)&netstack_cmd_rx, &sched_params, cmd_rx_cb, RF_EventRxEntryDone | RF_EventRxBufFull ); if (!CMD_HANDLE_OK(cmd_rx_handle)) { PRINTF("cmd_rx_restore: unable to schedule RX command handle=%d status=0x%04x", cmd_rx_handle, CMD_STATUS(netstack_cmd_rx)); return RF_RESULT_ERROR; } return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ rf_result_t rf_yield(void) { /* Force abort of any ongoing RF operation */ RF_flushCmd(&rf_netstack, RF_CMDHANDLE_FLUSH_ALL, RF_ABORT_GRACEFULLY); RF_flushCmd(&rf_ble, RF_CMDHANDLE_FLUSH_ALL, RF_ABORT_GRACEFULLY); /* Trigger a manual power-down */ RF_yield(&rf_netstack); RF_yield(&rf_ble); ENERGEST_OFF(ENERGEST_TYPE_LISTEN); return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ rf_result_t rf_set_tx_power(RF_Handle handle, RF_TxPowerTable_Entry *table, int8_t dbm) { const RF_Stat stat = RF_setTxPower(handle, RF_TxPowerTable_findValue(table, dbm) ); return (stat == RF_StatSuccess) ? RF_RESULT_OK : RF_RESULT_ERROR; } /*---------------------------------------------------------------------------*/ rf_result_t rf_get_tx_power(RF_Handle handle, RF_TxPowerTable_Entry *table, int8_t *dbm) { *dbm = RF_TxPowerTable_findPowerLevel(table, RF_getTxPower(handle) ); return (*dbm != RF_TxPowerTable_INVALID_DBM) ? RF_RESULT_OK : RF_RESULT_ERROR; } /*---------------------------------------------------------------------------*/ RF_Handle netstack_open(RF_Params *params) { return RF_open(&rf_netstack, &netstack_mode, (RF_RadioSetup*)&netstack_cmd_radio_setup, params ); } /*---------------------------------------------------------------------------*/ rf_result_t netstack_sched_fs(void) { const uint_fast8_t rx_key = cmd_rx_disable(); /* * For IEEE-mode, restarting IEEE_RX recalibrates the synth by using the * channel field in the IEEE_RX commando. It is assumed this field is * already configured before this functions is called. * However, if IEEE_RX wasn't active, manually calibrate the synth * with CMD_FS. * * For Prop-mode, the synth is always manually calibrated with CMD_FS. */ #if (RF_CORE_CONF_MODE == RF_CORE_MODE_2_4_GHZ) if (rx_key) { cmd_rx_restore(rx_key); return RF_RESULT_OK; } #endif /* RF_CORE_CONF_MODE == RF_CORE_MODE_2_4_GHZ */ RF_EventMask events; bool synth_error = false; uint8_t num_tries = 0; do { CMD_STATUS(netstack_cmd_fs) = PENDING; events = RF_runCmd(&rf_netstack, (RF_Op*)&netstack_cmd_fs, RF_PriorityNormal, NULL, 0 ); synth_error = (EVENTS_CMD_DONE(events)) && (CMD_STATUS(netstack_cmd_fs) == ERROR_SYNTH_PROG); } while (synth_error && (num_tries++ < CMD_FS_RETRIES)); cmd_rx_restore(rx_key); return (CMD_STATUS(netstack_cmd_fs) == DONE_OK) ? RF_RESULT_OK : RF_RESULT_ERROR; } /*---------------------------------------------------------------------------*/ rf_result_t netstack_sched_ieee_tx(bool recieve_ack) { rf_result_t res; RF_ScheduleCmdParams sched_params; RF_ScheduleCmdParams_init(&sched_params); sched_params.priority = RF_PriorityNormal; sched_params.endTime = 0; sched_params.allowDelay = RF_AllowDelayAny; const bool is_active = cmd_rx_is_active(); const bool rx_ack_required = (recieve_ack && !is_active); /* * If we expect ACK after transmission, RX must be running to be able to * run the RX_ACK command. Therefore, turn on RX before starting the * chained TX command. */ if (rx_ack_required) { res = netstack_sched_rx(); if (res != RF_RESULT_OK) { return res; } } CMD_STATUS(netstack_cmd_tx) = PENDING; RF_CmdHandle tx_handle = RF_scheduleCmd(&rf_netstack, (RF_Op*)&netstack_cmd_tx, &sched_params, NULL, 0 ); if (!CMD_HANDLE_OK(tx_handle)) { PRINTF("netstack_sched_tx: unable to schedule TX command handle=%d status=0x%04x\n", tx_handle, CMD_STATUS(netstack_cmd_tx)); return RF_RESULT_ERROR; } if (is_active) { ENERGEST_SWITCH(ENERGEST_TYPE_LISTEN, ENERGEST_TYPE_TRANSMIT); } else { ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); } /* Wait until TX operation finishes */ RF_EventMask tx_events = RF_pendCmd(&rf_netstack, tx_handle, 0); /* Stop RX if it was turned on only for ACK */ if (rx_ack_required) { netstack_stop_rx(); } if (is_active) { ENERGEST_SWITCH(ENERGEST_TYPE_TRANSMIT, ENERGEST_TYPE_LISTEN); } else { ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); } if (!EVENTS_CMD_DONE(tx_events)) { PRINTF("netstack_sched_tx: TX command pend error events=0x%08llx status=0x%04x\n", tx_events, CMD_STATUS(netstack_cmd_tx)); return RF_RESULT_ERROR; } return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ rf_result_t netstack_sched_prop_tx(void) { RF_ScheduleCmdParams sched_params; RF_ScheduleCmdParams_init(&sched_params); sched_params.priority = RF_PriorityNormal; sched_params.endTime = 0; sched_params.allowDelay = RF_AllowDelayAny; CMD_STATUS(netstack_cmd_tx) = PENDING; RF_CmdHandle tx_handle = RF_scheduleCmd(&rf_netstack, (RF_Op*)&netstack_cmd_tx, &sched_params, NULL, 0 ); if (!CMD_HANDLE_OK(tx_handle)) { PRINTF("netstack_sched_tx: unable to schedule TX command handle=%d status=0x%04x\n", tx_handle, CMD_STATUS(netstack_cmd_tx)); return RF_RESULT_ERROR; } /* * Prop TX requires any on-going RX operation to be stopped to be * able to transmit. Therefore, disable RX if running. */ const bool rx_key = cmd_rx_disable(); if (rx_key) { ENERGEST_SWITCH(ENERGEST_TYPE_LISTEN, ENERGEST_TYPE_TRANSMIT); } else { ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); } /* Wait until TX operation finishes */ RF_EventMask tx_events = RF_pendCmd(&rf_netstack, tx_handle, 0); cmd_rx_restore(rx_key); if (rx_key) { ENERGEST_SWITCH(ENERGEST_TYPE_TRANSMIT, ENERGEST_TYPE_LISTEN); } else { ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); } if (!EVENTS_CMD_DONE(tx_events)) { PRINTF("netstack_sched_tx: TX command pend error events=0x%08llx status=0x%04x\n", tx_events, CMD_STATUS(netstack_cmd_tx)); return RF_RESULT_ERROR; } return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ rf_result_t netstack_sched_rx(void) { if (cmd_rx_is_active()) { PRINTF("netstack_sched_rx: already in RX\n"); return RF_RESULT_OK; } RF_ScheduleCmdParams sched_params; RF_ScheduleCmdParams_init(&sched_params); sched_params.priority = RF_PriorityNormal; sched_params.endTime = 0; sched_params.allowDelay = RF_AllowDelayAny; CMD_STATUS(netstack_cmd_rx) = PENDING; cmd_rx_handle = RF_scheduleCmd(&rf_netstack, (RF_Op*)&netstack_cmd_rx, &sched_params, cmd_rx_cb, RF_EventRxEntryDone | RF_EventRxBufFull ); if (!CMD_HANDLE_OK(cmd_rx_handle)) { PRINTF("netstack_sched_rx: unable to schedule RX command handle=%d status=0x%04x\n", cmd_rx_handle, CMD_STATUS(netstack_cmd_rx)); return RF_RESULT_ERROR; } ENERGEST_ON(ENERGEST_TYPE_LISTEN); return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ rf_result_t netstack_stop_rx(void) { if (!cmd_rx_is_active()) { PRINTF("netstack_stop_rx: RX not active\n"); return RF_RESULT_OK; } CMD_STATUS(netstack_cmd_rx) = DONE_STOPPED; const RF_Stat stat = RF_cancelCmd(&rf_netstack, cmd_rx_handle, RF_ABORT_GRACEFULLY); cmd_rx_handle = 0; ENERGEST_OFF(ENERGEST_TYPE_LISTEN); return (stat == RF_StatSuccess) ? RF_RESULT_OK : RF_RESULT_ERROR; } /*---------------------------------------------------------------------------*/ RF_Handle ble_open(RF_Params *params) { return RF_open(&rf_ble, &ble_mode, (RF_RadioSetup*)&ble_cmd_radio_setup, params); } /*---------------------------------------------------------------------------*/ rf_result_t ble_sched_beacon(RF_Callback cb, RF_EventMask bm_event) { RF_ScheduleCmdParams sched_params; RF_ScheduleCmdParams_init(&sched_params); sched_params.priority = RF_PriorityNormal; sched_params.endTime = 0; sched_params.allowDelay = RF_AllowDelayAny; CMD_STATUS(ble_cmd_beacon) = PENDING; RF_CmdHandle beacon_handle = RF_scheduleCmd(&rf_ble, (RF_Op*)&ble_cmd_beacon, &sched_params, cb, bm_event ); if (!CMD_HANDLE_OK(beacon_handle)) { PRINTF("ble_sched_beacon: unable to schedule BLE Beacon command handle=%d status=0x%04x\n", beacon_handle, CMD_STATUS(ble_cmd_beacon)); return RF_RESULT_ERROR; } const uint_fast8_t rx_key = cmd_rx_disable(); /* Wait until Beacon operation finishes */ RF_EventMask beacon_events = RF_pendCmd(&rf_ble, beacon_handle, 0); if (!EVENTS_CMD_DONE(beacon_events)) { PRINTF("ble_sched_beacon: Beacon command pend error events=0x%08llx status=0x%04x\n", beacon_events, CMD_STATUS(ble_cmd_beacon)); cmd_rx_restore(rx_key); return RF_RESULT_ERROR; } cmd_rx_restore(rx_key); return RF_RESULT_OK; } /*---------------------------------------------------------------------------*/ PROCESS(rf_core_process, "RF Core Process"); /*---------------------------------------------------------------------------*/ PROCESS_THREAD(rf_core_process, ev, data) { int len; PROCESS_BEGIN(); while(1) { PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); do { //watchdog_periodic(); packetbuf_clear(); len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); if(len > 0) { packetbuf_set_datalen(len); NETSTACK_MAC.input(); } } while(len > 0); } PROCESS_END(); } /*---------------------------------------------------------------------------*/ /** @} */