Host driver/HAL to build a LoRa Picocell Gateway which communicates through USB with a concentrator board based on Semtech SX1308 multi-channel modem and SX1257/SX1255 RF transceivers.
Diff: libloragw/src/loragw_mcu.c
- Revision:
- 0:102b50f941d0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libloragw/src/loragw_mcu.c Wed Apr 11 14:38:42 2018 +0000 @@ -0,0 +1,552 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2017 Semtech-Cycleo + +Description: + Wrapper to call MCU's HAL functions + +License: Revised BSD License, see LICENSE.TXT file include in the project +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdio.h> /* printf fprintf */ +#include <stdlib.h> /* malloc free */ +#include <unistd.h> /* lseek, close */ +#include <fcntl.h> /* open */ +#include <string.h> /* memset */ +#include <errno.h> /* Error number definitions */ +#include <termios.h> /* POSIX terminal control definitions */ +#include <sys/ioctl.h> +#include <pthread.h> +#include <time.h> +#include <sys/select.h> + +#include "loragw_com.h" +#include "loragw_mcu.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#if DEBUG_MCU == 1 +#define DEBUG_MSG(str) fprintf(stderr, str) +#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) +#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_COM_ERROR;} +#else +#define DEBUG_MSG(str) +#define DEBUG_PRINTF(fmt, args...) +#define CHECK_NULL(a) if(a==NULL){return LGW_COM_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE SHARED VARIABLES (GLOBAL) ------------------------------------ */ + +extern void *lgw_com_target; /*! generic pointer to the COM device */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lgw_mcu_board_setconf(struct lgw_conf_board_s conf) { + int i, x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + uint8_t PADDING = 0; + uint8_t data[4]; + uint16_t size; + + /* struct to byte array */ + data[0] = conf.lorawan_public; + data[1] = conf.clksrc; + data[2] = PADDING; + data[3] = PADDING; + size = sizeof(data) / sizeof(uint8_t); + + /* prepare command */ + cmd.id = 'i'; + cmd.len_msb = (uint8_t)((size >> 8) & 0xFF); + cmd.len_lsb = (uint8_t)((size >> 0) & 0xFF); + cmd.address = 0; + for (i = 0; i < size; i++) { + cmd.cmd_data[i] = data[i]; + } + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + printf("ERROR: failed to configure board\n"); + return LGW_MCU_ERROR; + } + + /* check command acknoledge */ + if (ans.status != ACK_OK) { + printf("ERROR: failed to configure board, ACK failed\n"); + return LGW_MCU_ERROR; + } + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_rxrf_setconf(uint8_t rfchain, struct lgw_conf_rxrf_s conf) { + int i, x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + uint8_t PADDING = 0; + uint8_t data[20]; + uint16_t size; + + /* struct to byte array */ + /* --- 64-bits start --- */ + data[0] = conf.enable; + data[1] = PADDING; + data[2] = PADDING; + data[3] = PADDING; + data[4] = *(((uint8_t *)(&conf.freq_hz))); + data[5] = *(((uint8_t *)(&conf.freq_hz)) + 1); + data[6] = *(((uint8_t *)(&conf.freq_hz)) + 2); + data[7] = *(((uint8_t *)(&conf.freq_hz)) + 3); + /* --- 64-bits start --- */ + data[8] = *(((uint8_t *)(&conf.rssi_offset))); + data[9] = *(((uint8_t *)(&conf.rssi_offset)) + 1); + data[10] = *(((uint8_t *)(&conf.rssi_offset)) + 2); + data[11] = *(((uint8_t *)(&conf.rssi_offset)) + 3); + data[12] = *(((uint8_t *)(&conf.type))); + data[13] = PADDING; + data[14] = PADDING; + data[15] = PADDING; + /* --- 64-bits start --- */ + data[16] = *(((uint8_t *)(&conf.tx_enable))); + data[17] = *(((uint8_t *)(&conf.tx_enable)) + 1); + data[18] = *(((uint8_t *)(&conf.tx_enable)) + 2); + data[19] = *(((uint8_t *)(&conf.tx_enable)) + 3); + size = sizeof(data) / sizeof(uint8_t); + + /* prepare command */ + cmd.id = 'c'; + cmd.len_msb = (uint8_t)((size >> 8) & 0xFF); + cmd.len_lsb = (uint8_t)((size >> 0) & 0xFF); + cmd.address = rfchain; + for (i = 0; i < size; i++) { + cmd.cmd_data[i] = data[i]; + } + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + printf("ERROR: failed to send rxrf configuration\n"); + return LGW_MCU_ERROR; + } + + /* check command acknoledge */ + if (ans.status != ACK_OK) { + printf("ERROR: rxrf configuration, ACK failed\n"); + return LGW_MCU_ERROR; + } + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_rxif_setconf(uint8_t ifchain, struct lgw_conf_rxif_s conf) { + int i, x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + uint8_t PADDING = 0; + uint8_t data[32]; + uint16_t size; + + /* struct to byte array */ + /* --- 64-bits start --- */ + data[0] = conf.enable; + data[1] = *(((uint8_t *)(&conf.rf_chain))); + data[2] = PADDING; + data[3] = PADDING; + data[4] = *(((uint8_t *)(&conf.freq_hz))); + data[5] = *(((uint8_t *)(&conf.freq_hz)) + 1); + data[6] = *(((uint8_t *)(&conf.freq_hz)) + 2); + data[7] = *(((uint8_t *)(&conf.freq_hz)) + 3); + /* --- 64-bits start --- */ + data[8] = *(((uint8_t *)(&conf.bandwidth))); + data[9] = PADDING; + data[10] = PADDING; + data[11] = PADDING; + data[12] = *(((uint8_t *)(&conf.datarate))); + data[13] = *(((uint8_t *)(&conf.datarate)) + 1); + data[14] = *(((uint8_t *)(&conf.datarate)) + 2); + data[15] = *(((uint8_t *)(&conf.datarate)) + 3); + /* --- 64-bits start --- */ + data[16] = *(((uint8_t *)(&conf.sync_word_size))); + data[17] = PADDING; + data[18] = PADDING; + data[19] = PADDING; + data[20] = PADDING; + data[21] = PADDING; + data[22] = PADDING; + data[23] = PADDING; + /* --- 64-bits start --- */ + data[24] = *(((uint8_t *)(&conf.sync_word))); + data[25] = *(((uint8_t *)(&conf.sync_word)) + 1); + data[26] = *(((uint8_t *)(&conf.sync_word)) + 2); + data[27] = *(((uint8_t *)(&conf.sync_word)) + 3); + data[28] = *(((uint8_t *)(&conf.sync_word)) + 4); + data[29] = *(((uint8_t *)(&conf.sync_word)) + 5); + data[30] = *(((uint8_t *)(&conf.sync_word)) + 6); + data[31] = *(((uint8_t *)(&conf.sync_word)) + 7); + size = sizeof(data) / sizeof(uint8_t); + + /* prepare command */ + cmd.id = 'd'; + cmd.len_msb = (uint8_t)((size >> 8) & 0xFF); + cmd.len_lsb = (uint8_t)((size >> 0) & 0xFF); + cmd.address = ifchain; + for (i = 0; i < size; i++) { + cmd.cmd_data[i] = data[i]; + } + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + printf("ERROR: failed to send rxif configuration\n"); + return LGW_MCU_ERROR; + } + + /* check command acknoledge */ + if (ans.status != ACK_OK) { + printf("ERROR: rxif configuration, ACK failed\n"); + return LGW_MCU_ERROR; + } + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_txgain_setconf(struct lgw_tx_gain_lut_s *conf) { + int i, x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + uint32_t u = 0; + uint8_t data[(LGW_MULTI_NB * TX_GAIN_LUT_SIZE_MAX) + 4]; + uint16_t size; + + /* struct to byte array */ + for (u = 0; u < TX_GAIN_LUT_SIZE_MAX; u++) { + data[0 + (5 * u)] = 0; + data[1 + (5 * u)] = 0; + data[2 + (5 * u)] = 0; + data[3 + (5 * u)] = 0; + data[4 + (5 * u)] = 0; + } + + for (u = 0; u < conf->size; u++) { + data[0 + (5 * u)] = conf->lut[u].dig_gain; + data[1 + (5 * u)] = conf->lut[u].pa_gain; + data[2 + (5 * u)] = conf->lut[u].dac_gain; + data[3 + (5 * u)] = conf->lut[u].mix_gain; + data[4 + (5 * u)] = conf->lut[u].rf_power; + } + data[(TX_GAIN_LUT_SIZE_MAX) * 5] = conf->size; + size = ((TX_GAIN_LUT_SIZE_MAX) * 5) + 1; + + /* prepare command */ + cmd.id = 'h'; + cmd.len_msb = (uint8_t)((size >> 8) & 0xFF); + cmd.len_lsb = (uint8_t)((size >> 0) & 0xFF); + cmd.address = 0; + for (i = 0; i < size; i++) { + cmd.cmd_data[i] = data[i]; + } + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + printf("ERROR: failed to send tx gain configuration\n"); + return LGW_MCU_ERROR; + } + + /* check command acknoledge */ + if (ans.status != ACK_OK) { + printf("ERROR: tx gain configuration, ACK failed\n"); + return LGW_MCU_ERROR; + } + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { + int i, j, x; + int cptalc = 0; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + int nb_packet ; + uint8_t data[LGW_PKT_RX_STRUCT_SIZE_ALIGNED * max_pkt]; + uint16_t pkt_size; + + /* check input variables */ + CHECK_NULL(pkt_data); + + /* Prepare command for fetching packets */ + cmd.id = 'b'; + cmd.len_msb = 0; + cmd.len_lsb = 1; + cmd.address = 0; + cmd.cmd_data[0] = max_pkt; + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if ((x != LGW_COM_SUCCESS) || (ans.status != ACK_OK)) { + DEBUG_MSG("ERROR: failed to receive packets from concentrator\n"); + return 0; + } + + /* check nb_packet variables */ + nb_packet = ans.ans_data[0]; + if ((nb_packet > LGW_PKT_FIFO_SIZE) || (nb_packet < 0)) { + DEBUG_PRINTF("ERROR: NOT A VALID NUMBER OF RECEIVED PACKET (%d)\n", nb_packet); + return 0; + } + + //DEBUG_PRINTF("NOTE: Available packet %d %d\n", nb_packet, (ans.len_msb << 8) + ans.len_lsb); + + /* over the number of packets */ + for (i = 0; i < nb_packet; i++) { + /* for each packet */ + pkt_size = (uint16_t)((uint8_t)(ans.ans_data[cptalc + 42] << 8) | (uint8_t)ans.ans_data[cptalc + 43]); + for (j = 0; j < (LGW_PKT_RX_METADATA_SIZE_ALIGNED + pkt_size); j++) { + data[(i * LGW_PKT_RX_STRUCT_SIZE_ALIGNED) + j] = ans.ans_data[j + cptalc + 1]; /* +1 because ans.ans_data[0] is nb_packet */ + } + cptalc += j; + } + + /* byte array to struct - the following code is done to work both with 32 or 64 bits host */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + for (i = 0; i < nb_packet; i++) { + /* --- 64-bits start --- */ + pkt_data[i].freq_hz = *((uint32_t*)(&data[0 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].if_chain = *((uint8_t*)(&data[4 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].status = *((uint8_t*)(&data[5 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* 1 BYTE PADDING FOR 64-bits ALIGNMENT */ + /* 1 BYTE PADDING FOR 64-bits ALIGNMENT */ + /* --- 64-bits start --- */ + pkt_data[i].count_us = *((uint32_t*)(&data[8 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].rf_chain = *((uint8_t*)(&data[12 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].modulation = *((uint8_t*)(&data[13 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].bandwidth = *((uint8_t*)(&data[14 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* 1 BYTE PADDING FOR 64-bits ALIGNMENT */ + /* --- 64-bits start --- */ + pkt_data[i].datarate = *((uint32_t*)(&data[16 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].coderate = *((uint8_t*)(&data[20 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* --- 64-bits start --- */ + pkt_data[i].rssi = *((float*)(&data[24 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].snr = *((float*)(&data[28 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* --- 64-bits start --- */ + pkt_data[i].snr_min = *((float*)(&data[32 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].snr_max = *((float*)(&data[36 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* --- 64-bits start --- */ + pkt_data[i].crc = *((uint16_t*)(&data[40 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + pkt_data[i].size = *((uint16_t*)(&data[42 + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + /* NO PADDING NEEDED HERE, END OF ARRAY */ + for (j = 0; j < 256; j++) { + (pkt_data[i].payload[j]) = *((uint8_t*)(&data[LGW_PKT_RX_METADATA_SIZE_ALIGNED + j + LGW_PKT_RX_STRUCT_SIZE_ALIGNED * i])); + } + } +#pragma GCC diagnostic pop + + return nb_packet; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_send(struct lgw_pkt_tx_s pkt_data) { + int i, x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + uint8_t PADDING = 0; + uint8_t data[LGW_PKT_TX_STRUCT_SIZE_ALIGNED]; + uint16_t size; + + /* struct to byte array */ + /* --- 64-bits start --- */ + data[0] = *(((uint8_t *)(&pkt_data.freq_hz))); + data[1] = *(((uint8_t *)(&pkt_data.freq_hz)) + 1); + data[2] = *(((uint8_t *)(&pkt_data.freq_hz)) + 2); + data[3] = *(((uint8_t *)(&pkt_data.freq_hz)) + 3); + data[4] = *(((uint8_t *)(&pkt_data.tx_mode))); + data[5] = PADDING; + data[6] = PADDING; + data[7] = PADDING; + /* --- 64-bits start --- */ + data[8] = *(((uint8_t *)(&pkt_data.count_us))); + data[9] = *(((uint8_t *)(&pkt_data.count_us)) + 1); + data[10] = *(((uint8_t *)(&pkt_data.count_us)) + 2); + data[11] = *(((uint8_t *)(&pkt_data.count_us)) + 3); + data[12] = *(((uint8_t *)(&pkt_data.rf_chain))); + data[13] = *(((uint8_t *)(&pkt_data.rf_power))); + data[14] = *(((uint8_t *)(&pkt_data.modulation))); + data[15] = *(((uint8_t *)(&pkt_data.bandwidth))); + /* --- 64-bits start --- */ + data[16] = *(((uint8_t *)(&pkt_data.datarate))); + data[17] = *(((uint8_t *)(&pkt_data.datarate)) + 1); + data[18] = *(((uint8_t *)(&pkt_data.datarate)) + 2); + data[19] = *(((uint8_t *)(&pkt_data.datarate)) + 3); + data[20] = *(((uint8_t *)(&pkt_data.coderate))); + data[21] = *(((uint8_t *)(&pkt_data.invert_pol))); + data[22] = *(((uint8_t *)(&pkt_data.f_dev))); + data[23] = PADDING; + /* --- 64-bits start --- */ + data[24] = *(((uint8_t *)(&pkt_data.preamble))); + data[25] = *(((uint8_t *)(&pkt_data.preamble)) + 1); + data[26] = *(((uint8_t *)(&pkt_data.no_crc))); + data[27] = *(((uint8_t *)(&pkt_data.no_header))); + data[28] = *(((uint8_t *)(&pkt_data.size))); + data[29] = *(((uint8_t *)(&pkt_data.size)) + 1); + /* NO PADDING NEEDED HERE, END OF ARRAY */ + for (i = 0; i < 256; i++) { + data[i + LGW_PKT_TX_METADATA_SIZE_ALIGNED] = *(((uint8_t *)(&pkt_data.payload)) + i); + } + size = sizeof(data) / sizeof(uint8_t); + + /* prepare command */ + cmd.id = 'f'; + cmd.len_msb = (uint8_t)((size >> 8) & 0xFF); + cmd.len_lsb = (uint8_t)((size >> 0) & 0xFF); + cmd.address = 0; + for (i = 0; i < size; i++) { + cmd.cmd_data[i] = data[i]; + } + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + printf("ERROR: failed to send packet\n"); + return LGW_MCU_ERROR; + } + + /* check command acknoledge */ + if (ans.status != ACK_OK) { + printf("ERROR: failed to send packet, ACK failed\n"); + return LGW_MCU_ERROR; + } + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_get_trigcnt(uint32_t *data) { + int x; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + + /* check input variables */ + CHECK_NULL(data); + + /* prepare command */ + cmd.id = 'q'; + cmd.len_msb = 0; + cmd.len_lsb = 0; + cmd.address = 0; + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if ((x != LGW_COM_SUCCESS) || (ans.status != ACK_OK)) { + DEBUG_MSG("ERROR: failed to get concentrator internal counter\n"); + return LGW_MCU_ERROR; + } + + *data = (ans.ans_data[0] << 24) + (ans.ans_data[1] << 16) + (ans.ans_data[2] << 8) + (ans.ans_data[3]); + DEBUG_PRINTF("Note: sx1301 counter %u\n", *data); + + return LGW_MCU_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_commit_radio_calibration(uint8_t idx_start, uint8_t idx_nb) { + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + + /* prepare command */ + cmd.id = 'j'; + cmd.len_msb = 0; + cmd.len_lsb = 2; + cmd.address = 0; + cmd.cmd_data[0] = idx_start; + cmd.cmd_data[1] = idx_nb; + + /* send command to MCU */ + return lgw_com_send_command(lgw_com_target, cmd, &ans); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_reset(void) { + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + + /* prepare command */ + cmd.id = 'm'; + cmd.len_msb = 0; + cmd.len_lsb = 0; + cmd.address = 0; + + /* send command to MCU */ + return lgw_com_send_command(lgw_com_target, cmd, &ans); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_mcu_get_unique_id(uint8_t *uid) { + int i, x; + int fwversion = STM32FWVERSION; + lgw_com_cmd_t cmd; + lgw_com_ans_t ans; + + /* prepare command */ + cmd.id = 'l'; + cmd.len_msb = 0; + cmd.len_lsb = 4; + cmd.address = 0; + cmd.cmd_data[0] = (uint8_t)((fwversion >> 24) & (0x000000ff)); + cmd.cmd_data[1] = (uint8_t)((fwversion >> 16) & (0x000000ff)); + cmd.cmd_data[2] = (uint8_t)((fwversion >> 8) & (0x000000ff)); + cmd.cmd_data[3] = (uint8_t)((fwversion) & (0x000000ff)); + + /* send command to MCU */ + x = lgw_com_send_command(lgw_com_target, cmd, &ans); + if (x != LGW_COM_SUCCESS) { + DEBUG_MSG("ERROR: Failed to get MCU unique ID\n"); + return LGW_MCU_ERROR; + } + + /* Check MCU FW version */ + if (ans.status == ACK_KO) { + DEBUG_MSG("ERROR: Invalid MCU firmware version\n"); + return LGW_MCU_ERROR; + } + + /* Get MCU unique ID */ + for (i = 0; i <= 7; i++) { + uid[i] = ans.ans_data[i]; + } + + return LGW_MCU_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */