/* * \brief Function controls the ACK pending, channel setting and energy detection. * * \param extension_type Type of control * \param data_ptr Data from NET library * * \return 0 Success */ static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) { switch (extension_type) { /* Control MAC pending bit for Indirect data transmission */ case PHY_EXTENSION_CTRL_PENDING_BIT: if(*data_ptr) { data_pending = true; } else { data_pending = false; } break; /* Return frame pending bit from last received ACK */ case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: if(last_ack_pending_bit) { *data_ptr = 0xFF; } else { *data_ptr = 0; } break; /* Set channel */ case PHY_EXTENSION_SET_CHANNEL: channel = *data_ptr; break; /* Read energy on the channel */ case PHY_EXTENSION_READ_CHANNEL_ENERGY: // TODO: implement energy detection *data_ptr = 0; break; /* Read status of the link */ case PHY_EXTENSION_READ_LINK_STATUS: // TODO: return accurate value here SL_DEBUG_PRINT("rf_extension: Trying to read link status\n"); break; /* Convert between LQI and RSSI */ case PHY_EXTENSION_CONVERT_SIGNAL_INFO: // TODO: return accurate value here SL_DEBUG_PRINT("rf_extension: Trying to read signal info\n"); break; case PHY_EXTENSION_ACCEPT_ANY_BEACON: SL_DEBUG_PRINT("rf_extension: Trying to accept any beacon\n"); break; } return 0; }
/* * \brief Function sets the addresses to RF address filters. * * \param address_type Type of address * \param address_ptr Pointer to given address * * \return 0 Success */ static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) { int8_t ret_val = 0; switch (address_type) { /*Set 48-bit address*/ case PHY_MAC_48BIT: // 15.4 does not support 48-bit addressing ret_val = -1; break; /*Set 64-bit MAC address*/ case PHY_MAC_64BIT: /* Store MAC in MSB order */ memcpy(MAC_address, address_ptr, 8); SL_DEBUG_PRINT("rf_address_write: MACw "); for(unsigned int i = 0; i < sizeof(MAC_address); i ++) { SL_DEBUG_PRINT("%02x:", MAC_address[i]); } SL_DEBUG_PRINT("\n"); /* Pass MAC to the RF driver in LSB order */ uint8_t MAC_reversed[8]; for(unsigned int i = 0; i < sizeof(MAC_address); i ++) { MAC_reversed[i] = MAC_address[sizeof(MAC_address) - 1 - i]; } RAIL_IEEE802154_SetLongAddress(MAC_reversed); break; /*Set 16-bit address*/ case PHY_MAC_16BIT: short_address = address_ptr[0] << 8 | address_ptr[1]; RAIL_IEEE802154_SetShortAddress(short_address); break; /*Set PAN Id*/ case PHY_MAC_PANID: PAN_address = address_ptr[0] << 8 | address_ptr[1]; RAIL_IEEE802154_SetPanId(PAN_address); break; } return ret_val; }
/* * \brief Function gives the control of RF states to MAC. * * \param new_state RF state * \param rf_channel RF channel * * \return 0 Success */ static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) { int8_t ret_val = 0; switch (new_state) { /* Reset PHY driver and set to idle */ case PHY_INTERFACE_RESET: RAIL_RfIdle(); radio_state = RADIO_IDLE; break; /* Disable PHY Interface driver */ case PHY_INTERFACE_DOWN: RAIL_RfIdle(); radio_state = RADIO_IDLE; break; /* Enable RX */ case PHY_INTERFACE_UP: if(rail_checkAndSwitchChannel(rf_channel)) { RAIL_IEEE802154_SetPromiscuousMode(false); RAIL_RxStart(channel); radio_state = RADIO_RX; } else { ret_val = -1; } break; /* Enable wireless interface ED scan mode */ case PHY_INTERFACE_RX_ENERGY_STATE: SL_DEBUG_PRINT("rf_interface_state_control: Energy det req\n"); // TODO: implement energy detection break; /* Enable RX in promiscuous mode (aka no address filtering) */ case PHY_INTERFACE_SNIFFER_STATE: if(rail_checkAndSwitchChannel(rf_channel)) { RAIL_IEEE802154_SetPromiscuousMode(true); RAIL_RxStart(channel); radio_state = RADIO_RX; } else { ret_val = -1; } break; } return ret_val; }
/** * Interrupt level callback * Allows the user finer granularity in tx radio events. * * Radio Statuses: * RAIL_TX_CONFIG_BUFFER_UNDERFLOW * RAIL_TX_CONFIG_CHANNEL_BUSY * * @param[in] status A bit field that defines what event caused the callback */ void RAILCb_TxRadioStatus(uint8_t status) { if(device_driver.phy_tx_done_cb != NULL) { if(status == RAIL_TX_CONFIG_BUFFER_UNDERFLOW || status == RAIL_TX_CONFIG_CHANNEL_BUSY || status == RAIL_TX_CONFIG_TX_ABORTED || status == RAIL_TX_CONFIG_TX_BLOCKED) { waiting_for_ack = false; #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_ERR); } #else device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); } else { SL_DEBUG_PRINT("Packet TX error %d\n", status); } #endif }
/* * \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO. * * \param data_ptr Pointer to TX data * \param data_length Length of the TX data * \param tx_handle Handle to transmission * \return 0 Success * \return -1 Busy */ static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol ) { RAIL_TxData_t txData = { data_ptr, data_length + 3 }; switch(radio_state) { case RADIO_UNINIT: SL_DEBUG_PRINT("rf_start_cca: Radio uninit\n"); return -1; case RADIO_INITING: SL_DEBUG_PRINT("rf_start_cca: Radio initing\n"); return -1; case RADIO_CALIBRATION: SL_DEBUG_PRINT("rf_start_cca: Radio calibrating\n"); return -1; case RADIO_TX: SL_DEBUG_PRINT("rf_start_cca: Radio in TX mode\n"); return -1; case RADIO_IDLE: case RADIO_RX: // If we're still waiting for an ACK, don't mess up the internal state if(waiting_for_ack || RAIL_RfStateGet() == RAIL_RF_STATE_TX) { if((RAIL_GetTime() - last_tx) < 30000) { SL_DEBUG_PRINT("rf_start_cca: Still waiting on previous ACK\n"); return -1; } else { SL_DEBUG_PRINT("rf_start_cca: TXerr\n"); } } platform_enter_critical(); data_ptr[0] = data_length + 2; RAIL_RfIdleExt(RAIL_IDLE_ABORT , true); RAIL_TxDataLoad(&txData); radio_state = RADIO_TX; RAIL_TxOptions_t txOpt; //Check to see whether we'll be waiting for an ACK if(data_ptr[1] & (1 << 5)) { txOpt.waitForAck = true; waiting_for_ack = true; } else { txOpt.waitForAck = false; } SL_DEBUG_PRINT("rf_start_cca: Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0); if(RAIL_TxStartWithOptions(channel, &txOpt, &RAIL_CcaCsma, (RAIL_CsmaConfig_t*) &csma_config) == 0) { //Save packet number and sequence current_tx_handle = tx_handle; current_tx_sequence = data_ptr[3]; platform_exit_critical(); return 0; } else { RAIL_RfIdle(); RAIL_RxStart(channel); radio_state = RADIO_RX; platform_exit_critical(); return -1; } } //Should never get here... platform_exit_critical(); return -1; }
static void rf_thread_loop(const void *arg) { SL_DEBUG_PRINT("rf_thread_loop: starting (id: %d)\n", rf_thread_id); for (;;) { osEvent event = osSignalWait(0, osWaitForever); if (event.status != osEventSignal) { continue; } platform_enter_critical(); if (event.value.signals & SL_RX_DONE) { while(rx_queue_tail != rx_queue_head) { void* handle = (void*) rx_queue[rx_queue_tail]; RAIL_RxPacketInfo_t* info = (RAIL_RxPacketInfo_t*) memoryPtrFromHandle(handle); device_driver.phy_rx_cb( info->dataPtr + 1, info->dataLength - 1, info->appendedInfo.lqi, info->appendedInfo.rssiLatch, rf_radio_driver_id); memoryFree(handle); rx_queue[rx_queue_tail] = NULL; rx_queue_tail = (rx_queue_tail + 1) % RF_QUEUE_SIZE; } } else if (event.value.signals & SL_TX_DONE) { device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_TX_SUCCESS, 1, 1); } else if (event.value.signals & SL_ACK_RECV) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, (event.value.signals & SL_ACK_PEND) ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, 1, 1); } else if (event.value.signals & SL_ACK_TIMEOUT) { waiting_for_ack = false; device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_TX_FAIL, 1, 1); } else if(event.value.signals & SL_TX_ERR) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); } else if(event.value.signals & SL_CAL_REQ) { SL_DEBUG_PRINT("rf_thread_loop: SL_CAL_REQ signal received (unhandled)\n"); } else if(event.value.signals & SL_RXFIFO_ERR) { SL_DEBUG_PRINT("rf_thread_loop: SL_RXFIFO_ERR signal received (unhandled)\n"); } else if(event.value.signals & SL_TXFIFO_ERR) { SL_DEBUG_PRINT("rf_thread_loop: SL_TXFIFO_ERR signal received (unhandled)\n"); } else if(event.value.signals & SL_QUEUE_FULL) { SL_DEBUG_PRINT("rf_thread_loop: SL_QUEUE_FULL signal received (packet dropped)\n"); } else { SL_DEBUG_PRINT("rf_thread_loop unhandled event status: %d value: %d\n", event.status, event.value.signals); } platform_exit_critical(); } }
/* * \brief Function initialises and registers the RF driver. * * \param none * * \return rf_radio_driver_id Driver ID given by NET library */ static int8_t rf_device_register(void) { // If we already exist, bail. if(radio_state != RADIO_UNINIT) { return -1; } SL_DEBUG_PRINT("rf_device_register: entry\n"); #if MBED_CONF_SL_RAIL_BAND == 2400 RADIO_PA_Init((RADIO_PAInit_t*)&paInit2p4); #elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) RADIO_PA_Init((RADIO_PAInit_t*)&paInitSubGhz); #endif // Set up PTI since it makes life so much easier #if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1) RADIO_PTIInit_t ptiInit = { MBED_CONF_SL_RAIL_PTI_MODE, MBED_CONF_SL_RAIL_PTI_BAUDRATE, MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION, MBED_CONF_SL_RAIL_PTI_DOUT_PORT, MBED_CONF_SL_RAIL_PTI_DOUT_PIN, MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION, MBED_CONF_SL_RAIL_PTI_DCLK_PORT, MBED_CONF_SL_RAIL_PTI_DCLK_PIN, MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION, MBED_CONF_SL_RAIL_PTI_DFRAME_PORT, MBED_CONF_SL_RAIL_PTI_DFRAME_PIN }; RADIO_PTI_Init(&ptiInit); #endif // Set up RAIL RAIL_RfInit(&railInitParams); RAIL_ChannelConfig(&channels); #if (MBED_CONF_SL_RAIL_BAND == 2400) RAIL_RadioConfig((void*) ieee802154_config_base); channel = 11; #elif (MBED_CONF_SL_RAIL_BAND == 915) RAIL_RadioConfig((void*) ieee802154_config_915); channel = 1; #elif (MBED_CONF_SL_RAIL_BAND == 868) RAIL_RadioConfig((void*) ieee802154_config_863); channel = 0; #endif RAIL_IEEE802154_Init((RAIL_IEEE802154_Config_t*)&config); /* Get real MAC address */ /* MAC is stored MSB first */ memcpy(MAC_address, (const void*)&DEVINFO->UNIQUEH, 4); memcpy(&MAC_address[4], (const void*)&DEVINFO->UNIQUEL, 4); /*Set pointer to MAC address*/ device_driver.PHY_MAC = MAC_address; device_driver.driver_description = (char*)"EFR32_154"; /*Type of RF PHY*/ #if MBED_CONF_SL_RAIL_BAND == 2400 device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE; #elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE; #endif device_driver.phy_channel_pages = phy_channel_pages; /*Maximum size of payload is 127*/ device_driver.phy_MTU = 127; /*1 byte header in PHY layer (length)*/ device_driver.phy_header_length = 1; /*No tail in PHY layer*/ device_driver.phy_tail_length = 0; /*Set address write function*/ device_driver.address_write = &rf_address_write; /*Set RF extension function*/ device_driver.extension = &rf_extension; /*Set RF state control function*/ device_driver.state_control = &rf_interface_state_control; /*Set transmit function*/ device_driver.tx = &rf_start_cca; /*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/ device_driver.phy_rx_cb = NULL; device_driver.phy_tx_done_cb = NULL; /*Virtual upper data callback init to NULL*/ device_driver.arm_net_virtual_rx_cb = NULL; device_driver.arm_net_virtual_tx_cb = NULL; /*Register device driver*/ rf_radio_driver_id = arm_net_phy_register(&device_driver); // If the radio hasn't called the ready callback by now, place it in the initing state if(radio_state == RADIO_UNINIT) { radio_state = RADIO_INITING; } #ifdef MBED_CONF_RTOS_PRESENT rx_queue_head = 0; rx_queue_tail = 0; rf_thread_id = osThreadCreate(osThread(rf_thread_loop), NULL); #endif return rf_radio_driver_id; }
/** * Event handler for RAIL-fired events. Usually gets called from IRQ context. * Due to IRQ latency and tailchaining, multiple event flags might be set simultaneously, * so we have to check all of them */ static void radioEventHandler(RAIL_Handle_t railHandle, RAIL_Events_t events) { /* RAIL_Events_t is a 64-bit event mask, but a thread only supports 32 * signal bits. This means we have to convert from a RAIL event mask to * our own custom event mask. */ if (railHandle != gRailHandle) return; #ifdef MBED_CONF_RTOS_PRESENT if(rf_thread_id == 0) { return; } #endif size_t index = 0; do { if (events & 1ull) { switch(index) { /* * Occurs when the AGC averaged RSSI is done. * It occurs in response to RAIL_StartAverageRssi() to indicate that the * hardware has completed averaging. Call \ref RAIL_GetAverageRssi to get the * result. */ case RAIL_EVENT_RSSI_AVERAGE_DONE_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_RSSI_DONE); #else SL_DEBUG_PRINT("RSSI done (%d)\n", RAIL_GetAverageRssi(gRailHandle)); #endif break; /* * Notifies the application when searching for an ACK has timed * out. This event occurs whenever the timeout for searching for an * ACK is exceeded. */ case RAIL_EVENT_RX_ACK_TIMEOUT_SHIFT: if(waiting_for_ack) { waiting_for_ack = false; #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_ACK_TIMEOUT); #else device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, PHY_LINK_TX_FAIL, 1, 1); #endif } break; /* * Occurs when the receive FIFO exceeds the configured threshold * value. Call \ref RAIL_GetRxFifoBytesAvailable to get the number of bytes * available. */ case RAIL_EVENT_RX_FIFO_ALMOST_FULL_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_RXFIFO_ERR); #else SL_DEBUG_PRINT("RX near full (%d)\n", RAIL_GetRxFifoBytesAvailable(gRailHandle)); #endif break; /* * Occurs whenever a packet is received. * Call RAIL_GetRxPacketInfo() to get basic packet information along * with a handle to this packet for subsequent use with * RAIL_PeekRxPacket(), RAIL_GetRxPacketDetails(), * RAIL_HoldRxPacket(), and RAIL_ReleaseRxPacket() as needed. * * If \ref RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, this event also occurs * for packets with CRC errors. */ case RAIL_EVENT_RX_PACKET_RECEIVED_SHIFT: { /* Get RX packet that got signaled */ RAIL_RxPacketInfo_t rxPacketInfo; RAIL_RxPacketHandle_t rxHandle = RAIL_GetRxPacketInfo(gRailHandle, RAIL_RX_PACKET_HANDLE_NEWEST, &rxPacketInfo ); /* Only process the packet if it had a correct CRC */ if(rxPacketInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS) { uint8_t header[4]; RAIL_PeekRxPacket(gRailHandle, rxHandle, header, 4, 0); /* If this is an ACK, deal with it early */ if( (header[0] == 5) && (header[3] == current_tx_sequence) && waiting_for_ack) { /* Tell the radio to not ACK an ACK */ RAIL_CancelAutoAck(gRailHandle); waiting_for_ack = false; /* Save the pending bit */ last_ack_pending_bit = (header[1] & (1 << 4)) != 0; /* Release packet */ RAIL_ReleaseRxPacket(gRailHandle, rxHandle); /* Tell the stack we got an ACK */ #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_ACK_RECV | (last_ack_pending_bit ? SL_ACK_PEND : 0)); #else SL_DEBUG_PRINT("rACK\n"); device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, last_ack_pending_bit ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, 1, 1); #endif } else { /* Get RSSI and LQI information about this packet */ RAIL_RxPacketDetails_t rxPacketDetails; rxPacketDetails.timeReceived.timePosition = RAIL_PACKET_TIME_DEFAULT; rxPacketDetails.timeReceived.totalPacketBytes = 0; RAIL_GetRxPacketDetails(gRailHandle, rxHandle, &rxPacketDetails); #ifdef MBED_CONF_RTOS_PRESENT /* Drop this packet if we're out of space */ if (((rx_queue_head + 1) % RF_QUEUE_SIZE) == rx_queue_tail) { osSignalSet(rf_thread_id, SL_QUEUE_FULL); RAIL_ReleaseRxPacket(gRailHandle, rxHandle); break; } /* Copy into queue */ uint8_t* packetBuffer = (uint8_t*)rx_queue[rx_queue_head]; #else /* Packet going temporarily onto stack for bare-metal apps */ uint8_t packetBuffer[MAC_PACKET_MAX_LENGTH + MAC_PACKET_INFO_LENGTH]; #endif /* First two bytes are RSSI and LQI, respecitvely */ packetBuffer[MAC_PACKET_OFFSET_RSSI] = (uint8_t)rxPacketDetails.rssi; packetBuffer[MAC_PACKET_OFFSET_LQI] = (uint8_t)rxPacketDetails.lqi; /* Copy packet payload from circular FIFO into contiguous memory */ RAIL_CopyRxPacket(&packetBuffer[MAC_PACKET_INFO_LENGTH], &rxPacketInfo); /* Release RAIL resources early */ RAIL_ReleaseRxPacket(gRailHandle, rxHandle); /* Figure out whether we want to not ACK this packet */ /* * packetBuffer[0] = length * dataLength = length w/o length byte * packetBuffer[1:2] = 0x61C9 -> 0b01100001 0b1100 1001 (version 1, dest 3, src 2, ACKreq, type = 1) * [1] => b[0:2] frame type, b[3] = security enabled, b[4] = frame pending, b[5] = ACKreq, b[6] = intrapan * [2] => b[2:3] destmode, b[4:5] version, b[6:7] srcmode */ if( (packetBuffer[MAC_PACKET_INFO_LENGTH + 1] & (1 << 5)) == 0 ) { /* Cancel the ACK if the sender did not request one */ RAIL_CancelAutoAck(gRailHandle); } #ifdef MBED_CONF_RTOS_PRESENT rx_queue_head = (rx_queue_head + 1) % RF_QUEUE_SIZE; osSignalSet(rf_thread_id, SL_RX_DONE); #else SL_DEBUG_PRINT("rPKT %d\n", packetBuffer[MAC_PACKET_INFO_LENGTH] - 2); device_driver.phy_rx_cb(&packetBuffer[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ packetBuffer[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ packetBuffer[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ packetBuffer[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ rf_radio_driver_id); #endif } } } break; /* Event for preamble detection */ case RAIL_EVENT_RX_PREAMBLE_DETECT_SHIFT: break; /* Event for detection of the first sync word */ case RAIL_EVENT_RX_SYNC1_DETECT_SHIFT: break; /** Event for detection of the second sync word */ case RAIL_EVENT_RX_SYNC2_DETECT_SHIFT: break; /* Event for detection of frame errors * * For efr32xg1x parts, frame errors include violations of variable length * minimum/maximum limits, frame coding errors, and CRC errors. If \ref * RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, \ref RAIL_EVENT_RX_FRAME_ERROR * will not occur for CRC errors. */ case RAIL_EVENT_RX_FRAME_ERROR_SHIFT: break; /* Occurs when RX buffer is full */ case RAIL_EVENT_RX_FIFO_OVERFLOW_SHIFT: break; /* Occurs when a packet is address filtered */ case RAIL_EVENT_RX_ADDRESS_FILTERED_SHIFT: break; /* Occurs when an RX event times out */ case RAIL_EVENT_RX_TIMEOUT_SHIFT: break; /* Occurs when the scheduled RX window ends */ case RAIL_EVENT_RX_SCHEDULED_RX_END_SHIFT: break; /* An event for an aborted packet. It is triggered when a more specific * reason isn't known for why the packet was aborted (e.g. * \ref RAIL_EVENT_RX_ADDRESS_FILTERED). */ case RAIL_EVENT_RX_PACKET_ABORTED_SHIFT: break; /* * Occurs when the packet has passed any configured address and frame * filtering options. */ case RAIL_EVENT_RX_FILTER_PASSED_SHIFT: break; /* Occurs when modem timing is lost */ case RAIL_EVENT_RX_TIMING_LOST_SHIFT: break; /* Occurs when modem timing is detected */ case RAIL_EVENT_RX_TIMING_DETECT_SHIFT: break; /* * Indicates a Data Request is being received when using IEEE 802.15.4 * functionality. This occurs when the command byte of an incoming frame is * for a data request, which requests an ACK. This callback is called before * the packet is fully received to allow the node to have more time to decide * whether to set the frame pending in the outgoing ACK. This event only ever * occurs if the RAIL IEEE 802.15.4 functionality is enabled. * * Call \ref RAIL_IEEE802154_GetAddress to get the source address of the * packet. */ case RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND_SHIFT: if(data_pending) { RAIL_IEEE802154_SetFramePending(gRailHandle); } break; // TX Event Bitmasks /* * Occurs when the transmit FIFO falls under the configured * threshold value. It only occurs if a rising edge occurs across this * threshold. This event does not occur on initailization or after resetting * the transmit FIFO with RAIL_ResetFifo(). * Call \ref RAIL_GetTxFifoSpaceAvailable to get the number of bytes * available in the transmit FIFO at the time of the callback dispatch. */ case RAIL_EVENT_TX_FIFO_ALMOST_EMPTY_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TXFIFO_ERR); #else SL_DEBUG_PRINT("TX near empty (%d)\n", spaceAvailable); #endif break; /* * Interrupt level event to signify when the packet was sent. * Call RAIL_GetTxPacketDetails() to get information about the packet * that was transmitted. * @note that this structure is only valid during the timeframe of the * \ref RAIL_Config_t::eventsCallback. */ case RAIL_EVENT_TX_PACKET_SENT_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_DONE); #else if(device_driver.phy_tx_done_cb != NULL) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, // Normally we'd switch on ACK requested here, but Nanostack does that for us. PHY_LINK_TX_SUCCESS, // Succeeded, so how many times we tried is really not relevant. 1, 1); } #endif last_tx = RAIL_GetTime(); radio_state = RADIO_RX; break; /* * An interrupt level event to signify when the packet was sent. * Call RAIL_GetTxPacketDetails() to get information about the packet * that was transmitted. * @note This structure is only valid during the timeframe of the * \ref RAIL_Config_t::eventsCallback. */ case RAIL_EVENT_TXACK_PACKET_SENT_SHIFT: break; /* Occurs when a TX is aborted by the user */ case RAIL_EVENT_TX_ABORTED_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_TIMEOUT); #else device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); #endif waiting_for_ack = false; radio_state = RADIO_RX; break; /* Occurs when a TX ACK is aborted by the user */ case RAIL_EVENT_TXACK_ABORTED_SHIFT: break; /* Occurs when a TX is blocked by something like PTA or RHO */ case RAIL_EVENT_TX_BLOCKED_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_TIMEOUT); #else device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); #endif waiting_for_ack = false; radio_state = RADIO_RX; break; /* Occurs when a TX ACK is blocked by something like PTA or RHO */ case RAIL_EVENT_TXACK_BLOCKED_SHIFT: break; /* Occurs when the TX buffer underflows */ case RAIL_EVENT_TX_UNDERFLOW_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_TIMEOUT); #else device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); #endif waiting_for_ack = false; radio_state = RADIO_RX; break; /* Occurs when the buffer used for TX acking underflows */ case RAIL_EVENT_TXACK_UNDERFLOW_SHIFT: break; /* Occurs when CCA/CSMA/LBT succeeds */ case RAIL_EVENT_TX_CHANNEL_CLEAR_SHIFT: break; /* Occurs when CCA/CSMA/LBT fails */ case RAIL_EVENT_TX_CHANNEL_BUSY_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_TX_TIMEOUT); #else device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); #endif waiting_for_ack = false; radio_state = RADIO_RX; break; /* Occurs when a CCA check is being retried */ case RAIL_EVENT_TX_CCA_RETRY_SHIFT: break; /** Occurs when a clear channel assessment (CCA) is begun */ case RAIL_EVENT_TX_START_CCA_SHIFT: break; // Scheduler Event Bitmasks: Not used /* Event for when the scheduler switches away from this configuration */ case RAIL_EVENT_CONFIG_UNSCHEDULED_SHIFT: break; /* Event for when the scheduler switches to this configuration */ case RAIL_EVENT_CONFIG_SCHEDULED_SHIFT: break; /* Event for when the status of the scheduler changes */ case RAIL_EVENT_SCHEDULER_STATUS_SHIFT: break; // Other Event Bitmasks /* * Notifies the application that a calibration is needed. * It occurs whenever the RAIL library detects that a * calibration is needed. An application determines a valid * window to call \ref RAIL_Calibrate(). */ case RAIL_EVENT_CAL_NEEDED_SHIFT: #ifdef MBED_CONF_RTOS_PRESENT osSignalSet(rf_thread_id, SL_CAL_REQ); #else SL_DEBUG_PRINT("!!!! Calling for calibration\n"); #endif break; default: break; } } events = events >> 1; index += 1; } while (events != 0); }
/* * \brief Function gives the control of RF states to MAC. * * \param new_state RF state * \param rf_channel RF channel * * \return 0 Success */ static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) { int8_t ret_val = 0; switch (new_state) { /* Reset PHY driver and set to idle */ case PHY_INTERFACE_RESET: RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); radio_state = RADIO_IDLE; if(sleep_blocked) { sleep_manager_unlock_deep_sleep(); sleep_blocked = false; } break; /* Disable PHY Interface driver */ case PHY_INTERFACE_DOWN: RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); radio_state = RADIO_IDLE; if(sleep_blocked) { sleep_manager_unlock_deep_sleep(); sleep_blocked = false; } break; /* Enable RX */ case PHY_INTERFACE_UP: if(rail_checkAndSwitchChannel(rf_channel)) { RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, false); RAIL_StartRx(gRailHandle, channel, NULL); radio_state = RADIO_RX; if(!sleep_blocked) { /* RX can only happen in EM0/1*/ sleep_manager_lock_deep_sleep(); sleep_blocked = true; } } else { ret_val = -1; } break; /* Enable wireless interface ED scan mode */ case PHY_INTERFACE_RX_ENERGY_STATE: SL_DEBUG_PRINT("rf_interface_state_control: Energy det req\n"); // TODO: implement energy detection break; /* Enable RX in promiscuous mode (aka no address filtering) */ case PHY_INTERFACE_SNIFFER_STATE: if(rail_checkAndSwitchChannel(rf_channel)) { RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, true); RAIL_StartRx(gRailHandle, channel, NULL); radio_state = RADIO_RX; if(!sleep_blocked) { /* RX can only happen in EM0/1*/ sleep_manager_lock_deep_sleep(); sleep_blocked = true; } } else { ret_val = -1; } break; } return ret_val; }
/* * \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO. * * \param data_ptr Pointer to TX data * \param data_length Length of the TX data * \param tx_handle Handle to transmission * \return 0 Success * \return -1 Busy */ static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol ) { switch(radio_state) { case RADIO_UNINIT: SL_DEBUG_PRINT("rf_start_cca: Radio uninit\n"); return -1; case RADIO_INITING: SL_DEBUG_PRINT("rf_start_cca: Radio initing\n"); return -1; case RADIO_CALIBRATION: SL_DEBUG_PRINT("rf_start_cca: Radio calibrating\n"); return -1; case RADIO_TX: SL_DEBUG_PRINT("rf_start_cca: Radio in TX mode\n"); return -1; case RADIO_IDLE: case RADIO_RX: // If we're still waiting for an ACK, don't mess up the internal state if(waiting_for_ack || RAIL_GetRadioState(gRailHandle) == RAIL_RF_STATE_TX) { if((RAIL_GetTime() - last_tx) < 30000) { SL_DEBUG_PRINT("rf_start_cca: Still waiting on previous ACK\n"); return -1; } else { SL_DEBUG_PRINT("rf_start_cca: TXerr\n"); } } platform_enter_critical(); /* Since we set up Nanostack to give us a 1-byte PHY header, we get the one extra byte at the start of data_ptr * and need to populate it with the MAC-frame length byte (including the 2-byte hardware-inserted CRC) */ data_ptr[0] = data_length + 2; RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); RAIL_WriteTxFifo(gRailHandle, data_ptr, data_length + 1, true); radio_state = RADIO_TX; RAIL_TxOptions_t txOpt = RAIL_TX_OPTIONS_DEFAULT; //Check to see whether we'll be waiting for an ACK if(data_ptr[1] & (1 << 5)) { txOpt |= RAIL_TX_OPTION_WAIT_FOR_ACK; waiting_for_ack = true; } else { waiting_for_ack = false; } SL_DEBUG_PRINT("rf_start_cca: Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0); if(RAIL_StartCcaCsmaTx(gRailHandle, channel, txOpt, &csma_config, NULL) == 0) { //Save packet number and sequence current_tx_handle = tx_handle; current_tx_sequence = data_ptr[3]; platform_exit_critical(); return 0; } else { RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); RAIL_StartRx(gRailHandle, channel, NULL); radio_state = RADIO_RX; platform_exit_critical(); return -1; } } //Should never get here... return -1; }
/* * \brief Function initialises and registers the RF driver. * * \param none * * \return rf_radio_driver_id Driver ID given by NET library */ static int8_t rf_device_register(void) { // If we already exist, bail. if(radio_state != RADIO_UNINIT) { return -1; } SL_DEBUG_PRINT("rf_device_register: entry\n"); // Set up RAIL // Initialize the RAIL library and any internal state it requires gRailHandle = RAIL_Init(&railCfg, &RAILCb_RfReady); // Configure calibration settings RAIL_ConfigCal(gRailHandle, RAIL_CAL_ALL); // Set up library for IEEE802.15.4 PHY operation #if (MBED_CONF_SL_RAIL_BAND == 2400) RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle); channel = 11; #elif (MBED_CONF_SL_RAIL_BAND == 915) RAIL_ConfigChannels(gRailHandle, &channels, NULL); channel = 1; #elif (MBED_CONF_SL_RAIL_BAND == 868) RAIL_ConfigChannels(gRailHandle, &channels, NULL); channel = 0; #endif // Enable 802.15.4 acceleration features RAIL_IEEE802154_Init(gRailHandle, &config); // Fire all events by default RAIL_ConfigEvents(gRailHandle, RAIL_EVENTS_ALL, RAIL_EVENTS_ALL); // Setup the transmit buffer RAIL_SetTxFifo(gRailHandle, txFifo, 0, sizeof(txFifo)); #if MBED_CONF_SL_RAIL_BAND == 2400 if (RAIL_ConfigTxPower(gRailHandle, &paInit2p4) != RAIL_STATUS_NO_ERROR) { // Error: The PA could not be initialized due to an improper configuration. // Please ensure your configuration is valid for the selected part. while (1) ; } #elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) if (RAIL_ConfigTxPower(gRailHandle, &paInitSubGhz) != RAIL_STATUS_NO_ERROR) { // Error: The PA could not be initialized due to an improper configuration. // Please ensure your configuration is valid for the selected part. while (1) ; } #endif // Set the output power to the maximum supported by this chip RAIL_SetTxPower(gRailHandle, 255); // Set up PTI since it makes life so much easier #if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1) RAIL_PtiConfig_t ptiConfig = { MBED_CONF_SL_RAIL_PTI_MODE, MBED_CONF_SL_RAIL_PTI_BAUDRATE, MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION, MBED_CONF_SL_RAIL_PTI_DOUT_PORT, MBED_CONF_SL_RAIL_PTI_DOUT_PIN, MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION, MBED_CONF_SL_RAIL_PTI_DCLK_PORT, MBED_CONF_SL_RAIL_PTI_DCLK_PIN, MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION, MBED_CONF_SL_RAIL_PTI_DFRAME_PORT, MBED_CONF_SL_RAIL_PTI_DFRAME_PIN }; // Initialize the Packet Trace Interface (PTI) for the EFR32 RAIL_ConfigPti(RAIL_EFR32_HANDLE, &ptiConfig); // Enable Packet Trace (PTI) RAIL_EnablePti(RAIL_EFR32_HANDLE, true); #endif /* Get real MAC address */ /* MAC is stored MSB first */ memcpy(MAC_address, (const void*)&DEVINFO->UNIQUEH, 4); memcpy(&MAC_address[4], (const void*)&DEVINFO->UNIQUEL, 4); /*Set pointer to MAC address*/ device_driver.PHY_MAC = MAC_address; device_driver.driver_description = (char*)"EFR32_154"; /*Type of RF PHY*/ #if MBED_CONF_SL_RAIL_BAND == 2400 device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE; #elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE; #endif device_driver.phy_channel_pages = phy_channel_pages; /*Maximum size of payload is 127*/ device_driver.phy_MTU = 127; /*1 byte header in PHY layer (length)*/ device_driver.phy_header_length = 1; /*No tail in PHY layer*/ device_driver.phy_tail_length = 0; /*Set address write function*/ device_driver.address_write = &rf_address_write; /*Set RF extension function*/ device_driver.extension = &rf_extension; /*Set RF state control function*/ device_driver.state_control = &rf_interface_state_control; /*Set transmit function*/ device_driver.tx = &rf_start_cca; /*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/ device_driver.phy_rx_cb = NULL; device_driver.phy_tx_done_cb = NULL; /*Virtual upper data callback init to NULL*/ device_driver.arm_net_virtual_rx_cb = NULL; device_driver.arm_net_virtual_tx_cb = NULL; /*Register device driver*/ rf_radio_driver_id = arm_net_phy_register(&device_driver); // If the radio hasn't called the ready callback by now, place it in the initing state if(radio_state == RADIO_UNINIT) { radio_state = RADIO_INITING; } #ifdef MBED_CONF_RTOS_PRESENT rx_queue_head = 0; rx_queue_tail = 0; rf_thread_id = osThreadCreate(osThread(rf_thread_loop), NULL); #endif return rf_radio_driver_id; }
static void rf_thread_loop(const void *arg) { SL_DEBUG_PRINT("rf_thread_loop: starting (id: %d)\n", (int)rf_thread_id); for (;;) { osEvent event = osSignalWait(0, osWaitForever); if (event.status != osEventSignal) { continue; } platform_enter_critical(); if (event.value.signals & SL_RX_DONE) { while(rx_queue_tail != rx_queue_head) { uint8_t* packet = (uint8_t*) rx_queue[rx_queue_tail]; SL_DEBUG_PRINT("rPKT %d\n", packet[MAC_PACKET_INFO_LENGTH] - 2); device_driver.phy_rx_cb( &packet[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ packet[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ packet[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ packet[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ rf_radio_driver_id); rx_queue_tail = (rx_queue_tail + 1) % RF_QUEUE_SIZE; } } else if (event.value.signals & SL_TX_DONE) { device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_TX_SUCCESS, 1, 1); } else if (event.value.signals & SL_ACK_RECV) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, (event.value.signals & SL_ACK_PEND) ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, 1, 1); } else if (event.value.signals & SL_ACK_TIMEOUT) { waiting_for_ack = false; device_driver.phy_tx_done_cb(rf_radio_driver_id, current_tx_handle, PHY_LINK_TX_FAIL, 1, 1); } else if(event.value.signals & SL_TX_ERR) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); } else if(event.value.signals & SL_TX_TIMEOUT) { device_driver.phy_tx_done_cb( rf_radio_driver_id, current_tx_handle, PHY_LINK_CCA_FAIL, 8, 1); } else if(event.value.signals & SL_CAL_REQ) { SL_DEBUG_PRINT("rf_thread_loop: SL_CAL_REQ signal received (unhandled)\n"); } else if(event.value.signals & SL_RXFIFO_ERR) { SL_DEBUG_PRINT("rf_thread_loop: SL_RXFIFO_ERR signal received (unhandled)\n"); } else if(event.value.signals & SL_TXFIFO_ERR) { SL_DEBUG_PRINT("rf_thread_loop: SL_TXFIFO_ERR signal received (unhandled)\n"); } else if(event.value.signals & SL_QUEUE_FULL) { SL_DEBUG_PRINT("rf_thread_loop: SL_QUEUE_FULL signal received (packet dropped)\n"); } else { SL_DEBUG_PRINT("rf_thread_loop unhandled event status: %d value: %d\n", event.status, (int)event.value.signals); } platform_exit_critical(); } }