/** * @brief Gets the OQPSK Chiprate * * * @param[in] trx_id Transceiver identifier * @param[in] freq_band Frequency band * * @return oqpsk chiprate */ oqpsk_chip_rate_t get_oqpsk_chip_rate(trx_id_t trx_id, sun_freq_band_t freq_band) { uint16_t rate = 0; uint8_t rate_mode = 0; for (uint8_t i = 0; i < OQPSK_CHIP_RATE_FREQ_TABLE_ROW_SIZE; i++) { if (freq_band == (uint16_t)PGM_READ_WORD(& oqpsk_chip_rate_freq_table [i][0])) { rate = (uint16_t)PGM_READ_WORD( &oqpsk_chip_rate_freq_table[i][1]); break; } } if (rate == 2000) { rate_mode = CHIP_RATE_2000; } else if (rate == 1000) { rate_mode = CHIP_RATE_1000; } else if (rate == 200) { rate_mode = CHIP_RATE_200; } else if (rate == 100) { rate_mode = CHIP_RATE_100; } return rate_mode; }
// General Functions uint8_t xboot_get_version(uint16_t *ver) { uint8_t ret = init_api(); uint16_t ptr; #ifdef NEED_EIND uint8_t saved_eind; #endif // NEED_EIND if (ret != XB_SUCCESS) return ret; if (api_version == 1) { ptr = PGM_READ_WORD(JUMP_TABLE_INDEX(0)); if (ptr == 0 || ptr == 0xffff) return XB_ERR_NOT_FOUND; #ifdef NEED_EIND saved_eind = EIND; EIND = PROGMEM_SIZE >> 17; #endif // NEED_EIND ret = ( (uint8_t(*)(uint16_t *)) ptr )(ver); #ifdef NEED_EIND EIND = saved_eind; #endif // NEED_EIND return ret; }
/** * @brief Obtains the message type from the buffer and calls the respective handler * * This function decodes all events/messages and calls the appropriate handler. * * @param event Pointer to the buffer header whose body part holds the message * type and message elemnets */ void dispatch_event(uint8_t *event) { /* * A pointer to the body of the buffer is obtained from the pointer to the * received header. */ uint8_t *buffer_body = BMM_BUFFER_POINTER((buffer_t *)event); /* Check is done to see if the message type is valid */ /* Please note: * The macro PGM_READ_WORD is only relevant for AVR-GCC builds and * reads a DWord from flash, which is the start address of the function. * * How does this work for builds that are larger than 128K? * * For IAR builds this statement is fine, since PGM_READ_WORD just * turns to "*". The size of the function pointer is automatically * 3 byte for MCUs which have more than 128K flash. The the address * of the callback is properly derived from this location. * * AVR-GCC currently does not support function pointers larger than * 16 bit. This implies that functions residing in the upper 128K * cannot be addressed properly. Therefore this code does not work * in these cases. * In regular cases, where code is not larger than 128K, the size * of a function pointer is 16 bit and properly read via PGM_READ_WORD. * * Update: In order for this to work, the option -mrelax must be given * on the compiler command-line that is used to link the final ELF file. * (Older compilers did not implement this option for the AVR, * use -Wl,--relax instead.) */ /* Check for regular MAC requests. */ if (buffer_body[CMD_ID_OCTET] <= LAST_MESSAGE) { /* * The following statement reads the address from the dispatch table * of the function to be called by utilizing function pointers. */ handler_t handler = (handler_t)PGM_READ_WORD(&dispatch_table[buffer_body[CMD_ID_OCTET]]); if (handler != NULL) { handler(event); } else { bmm_buffer_free((buffer_t *)event); #if (DEBUG > 0) ASSERT("Dispatch handler unavailable" == 0); #endif } } #ifdef ENABLE_RTB else { dispatch_rtb_event(event); } #endif /* ENABLE_RTB */ }
/** * @brief Post processing of the vendor data response. * * @param PairingRef Pairing reference * @param VendorId Vendor ID. * @param nsduLength Length of the payload. * @param nsdu Actual payload * @param RxLinkQuality Link quality of received packet. * @param RxFlags Rx Flags. */ static void vendor_data_ind(uint8_t PairingRef, uint16_t VendorId, uint8_t nsduLength, uint8_t *nsdu, uint8_t RxLinkQuality, uint8_t RxFlags) { uint16_t v_id = PGM_READ_WORD(&VendorIdentifier); if (v_id == VendorId) { if (node_status == POWER_SAVE) { printf("Leaving power save mode.\r\n"); nlme_rx_enable_request(RX_DURATION_INFINITY, (FUNC_PTR)nlme_rx_enable_confirm ); node_status = IDLE; } printf("Vendor data received from pairing ref %d - ", PairingRef); switch (nsdu[0]) { case BATTERY_STATUS_RESP: { uint16_t volt = (uint16_t)nsdu[1] | ((uint16_t)nsdu[2] << 8); printf("battery status "); printf("%d.", volt / 1000); printf("%d V\r\n", volt % 1000); } break; case ALIVE_RESP: printf("Remote controller is alive\r\n"); break; case FW_VERSION_RESP: printf("Firmware version response: %d.%d.%d\r\n", nsdu[1], nsdu[2], nsdu[3]); break; case RX_ON_RESP: printf("RX on response\r\n"); break; default: printf("unknown command: 0x"); for (uint8_t i = 0; i < nsduLength; i++) { printf("%.02X ", nsdu[i]); } printf("\r\n"); break; } } /* Keep compiler happy */ PairingRef = PairingRef; nsduLength = nsduLength; RxLinkQuality = RxLinkQuality; RxFlags = RxFlags; }
/** * @brief Gets the symbol duration in us * * @param trx_id Transceiver identifier * * @return Duration of a symbol in us */ uint16_t tal_get_symbol_duration_us(trx_id_t trx_id) { uint16_t ret_val = 0; /* 0: indicator for wrong parameter */ switch (tal_pib[trx_id].phy.modulation) { #ifdef SUPPORT_FSK case FSK: ret_val = 20; /* table 0, pg. 7 */ break; #endif #ifdef SUPPORT_OFDM case OFDM: ret_val = 120; break; #endif #ifdef SUPPORT_OQPSK case OQPSK: /* table 183, pg. 118 */ ret_val = (uint16_t)PGM_READ_WORD(&oqpsk_sym_duration_table[ tal_pib [trx_id].phy.phy_mode.oqpsk.chip_rate]); break; #endif #ifdef SUPPORT_LEGACY_OQPSK case LEG_OQPSK: ret_val = 16; break; #endif default: break; } return ret_val; }
void vendor_data_ind(uint8_t PairingRef, uint16_t VendorId, uint8_t nsduLength, uint8_t *nsdu, uint8_t RxLinkQuality, uint8_t RxFlags) { /* Check if vendor id matches. * Handle here only vendor data from same vendor */ uint16_t v_id = PGM_READ_WORD(&VendorIdentifier); uint8_t nsduHandle = 1; if ((VendorId == v_id) && (RxFlags & RX_FLAG_WITH_SEC)) { switch (nsdu[0]) { /* vendor-specific command id */ case BATTERY_STATUS_REQ: { uint16_t voltage = get_batmon_voltage(); nsdu[0] = BATTERY_STATUS_RESP; nsdu[1] = (uint8_t)voltage; /* LSB */ nsdu[2] = (uint8_t)(voltage >> 8); /* MSB */ nsduLength = 3; } break; case ALIVE_REQ: /* Alive request */ vendor_app_alive_req(); /* Send alive response */ nsdu[0] = ALIVE_RESP; nsduLength = 1; break; case FW_VERSION_REQ: { /* Send alive response */ nsdu[0] = FW_VERSION_RESP; nsdu[1] = FW_VERSION_MAJOR; /* major version number */ nsdu[2] = FW_VERSION_MINOR; /* minor version number */ nsdu[3] = FW_VERSION_REV; /* revision version number */ nsduLength = 4; } break; case RX_ON_REQ: { uint32_t duration = 0; memcpy(&duration, &nsdu[1], 3); if (!nlme_rx_enable_request(duration, (FUNC_PTR)nlme_rx_enable_confirm )) { /* * RX enable could not been added to the queue. * Therefore do not send response message. */ return; } /* Send response */ nsdu[0] = RX_ON_RESP; nsduLength = 1; } break; default: { /* Send response */ nsdu[0] = FW_DATA_RESP; nsdu[1] = VD_NOT_SUPPORTED_ATTRIBUTE; nsduLength = 2; } break; } /* Transmit response message */ nlde_data_request(PairingRef, PROFILE_ID_ZRC, VendorId, nsduLength, nsdu, TXO_UNICAST | TXO_DST_ADDR_NET | TXO_ACK_REQ | TXO_SEC_REQ | TXO_MULTI_CH | TXO_CH_NOT_SPEC | TXO_VEND_SPEC, nsduHandle, (FUNC_PTR)vendor_data_confirm ); /* Keep compiler happy */ RxLinkQuality = RxLinkQuality; RxFlags = RxFlags; } }
void vendor_data_ind(uint8_t PairingRef, profile_id_t ProfileId, uint16_t VendorId, uint8_t nsduLength, uint8_t *nsdu, uint8_t RxLinkQuality, uint8_t RxFlags) { /* Check if vendor id matches. Handle here only vendor data from same vendor */ uint16_t v_id = PGM_READ_WORD(&VendorIdentifier); if ((VendorId == v_id) && (RxFlags & RX_FLAG_WITH_SEC)) { switch (nsdu[0]) // vendor-specific command id { #ifdef _DEBUG_INTERFACE_ case 0x1B: { int i; for (i = 0; i < nsduLength;) { RxHandler(nsdu[i++]); } RX_index = 4; // Next GetChar() will get the command id /* Call QDebug_Process */ QDebug_ProcessCommands(); return; } break; #endif #ifdef TFA_BAT_MON case BATTERY_STATUS_REQ: { uint16_t voltage = tfa_get_batmon_voltage(); nsdu[0] = BATTERY_STATUS_RESP; nsdu[1] = (uint8_t)voltage; // LSB nsdu[2] = (uint8_t)(voltage >> 8); // MSB nsduLength = 3; } break; #endif case ALIVE_REQ: /* Alive request */ vendor_app_alive_req(); /* Send alive response */ nsdu[0] = ALIVE_RESP; nsduLength = 1; break; case FW_VERSION_REQ: { /* Send alive response */ nsdu[0] = FW_VERSION_RESP; nsdu[1] = FW_VERSION_MAJOR; // major version number nsdu[2] = FW_VERSION_MINOR; // minor version number nsdu[3] = FW_VERSION_REV; // revision version number nsduLength = 4; } break; case RX_ON_REQ: { uint32_t duration = 0; memcpy(&duration, &nsdu[1], 3); if (!nlme_rx_enable_request(duration)) { /* * RX enable could not been added to the queue. * Therefore do not send response message. */ return; } /* Send response */ nsdu[0] = RX_ON_RESP; nsduLength = 1; } break; #ifdef FLASH_SUPPORT case FW_DATA_REQ: { fw_data_frame_t *fw_frame; vendor_status_t status = VD_SUCCESS; fw_frame = (fw_data_frame_t *)nsdu; /* Verify data chunk size */ uint8_t fw_data_size = nsduLength - 5; // 5 = header len if (fw_data_size > 64) { status = VD_UNSUPPORTED_SIZE; } else { /* Fill temporary page buffer */ uint16_t start_addr = (fw_frame->frame_cnt - 1) % 4; flash_fill_page_buffer(start_addr * (SPM_PAGESIZE / 4), fw_data_size, &fw_frame->fw_data[0]); /* Write flash page */ if ((fw_frame->frame_cnt % 4) == 0) { uint32_t page_start_addr; page_start_addr = IMAGE_START_ADDR + ((uint32_t)SPM_PAGESIZE * ((fw_frame->frame_cnt / 4) - 1)); flash_program_page(page_start_addr); } else if (fw_frame->frame_cnt == fw_frame->total_num_frames) { uint32_t page_start_addr; page_start_addr = IMAGE_START_ADDR + ((uint32_t)SPM_PAGESIZE * (fw_frame->frame_cnt / 4)); flash_program_page(page_start_addr); } } /* Send response */ nsdu[0] = FW_DATA_RESP; nsdu[1] = status; nsduLength = 2; } break; #endif /* #ifdef FLASH_SUPPORT */ #ifdef FLASH_SUPPORT case FW_SWAP_REQ: flash_swap(IMAGE_START_ADDR, IMAGE_SIZE); /* Do not send response message */ return; #endif /* #ifdef FLASH_SUPPORT */ default: { /* Send response */ nsdu[0] = FW_DATA_RESP; nsdu[1] = VD_NOT_SUPPORTED_ATTRIBUTE; nsduLength = 2; } break; } /* Transmit response message */ nlde_data_request(PairingRef, PROFILE_ID_VENDOR_DATA, VendorId, nsduLength, nsdu, TXO_UNICAST | TXO_DST_ADDR_NET | TXO_ACK_REQ | TXO_SEC_REQ | TXO_MULTI_CH | TXO_CH_NOT_SPEC | TXO_VEND_SPEC); /* Keep compiler happy */ UNUSED(ProfileId); UNUSED(RxLinkQuality); UNUSED(RxFlags); } }
/** * @brief Gets PSDU data rate * * @param trx_id Transceiver identifier * * @return data rate in kbit/s */ float get_data_rate(trx_id_t trx_id) { float rate = 0; switch (tal_pib[trx_id].phy.modulation) { #ifdef SUPPORT_FSK case FSK: rate = 10 * (uint8_t)PGM_READ_BYTE(&fsk_data_rate_table[ tal_pib[trx_id ].phy.phy_mode.fsk.data_rate]); if (tal_pib[trx_id].phy.phy_mode.fsk.mod_type == F4FSK) { rate *= 2; } if (tal_pib[trx_id].FSKFECEnabled) { rate /= 2; } break; #endif #ifdef SUPPORT_OFDM case OFDM: rate = (uint16_t)PGM_READ_WORD(&ofdm_data_rate_table[tal_pib[ trx_id ].OFDMMCS] \ [tal_pib[trx_id].phy.phy_mode.ofdm.option]); /* optoin * - * 1 */ break; #endif #ifdef SUPPORT_OQPSK case OQPSK: { uint16_t chip_rate = oqpsk_get_chip_rate(trx_id); uint8_t spread = oqpsk_spreading( tal_pib[trx_id].phy.phy_mode.oqpsk.chip_rate, tal_pib[trx_id].OQPSKRateMode); rate = (float)chip_rate / (float)spread / (float)2; } break; #endif #ifdef SUPPORT_LEGACY_OQPSK case LEG_OQPSK: rate = 250; if (tal_pib[trx_id].HighRateEnabled) { if (tal_pib[trx_id].phy.phy_mode.leg_oqpsk.chip_rate == CHIP_RATE_1000) { rate *= 2; } else { rate *= 4; } } break; #endif default: break; } return rate; }