diff options
Diffstat (limited to 'src/osmo-bts-sysmo/calib_file.c')
-rw-r--r-- | src/osmo-bts-sysmo/calib_file.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/src/osmo-bts-sysmo/calib_file.c b/src/osmo-bts-sysmo/calib_file.c new file mode 100644 index 00000000..2f723dd0 --- /dev/null +++ b/src/osmo-bts-sysmo/calib_file.c @@ -0,0 +1,475 @@ +/* sysmocom femtobts L1 calibration file routines*/ + +/* (C) 2012 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> + +#include <osmocom/core/utils.h> + +#include <osmo-bts/gsm_data.h> +#include <osmo-bts/logging.h> + +#include <sysmocom/femtobts/superfemto.h> +#include <sysmocom/femtobts/gsml1const.h> + +#include "l1_if.h" +#include "femtobts.h" +#include "eeprom.h" +#include "utils.h" + +struct calib_file_desc { + const char *fname; + GsmL1_FreqBand_t band; + int uplink; + int rx; +}; + +static const struct calib_file_desc calib_files[] = { + { + .fname = "calib_rxu_850.cfg", + .band = GsmL1_FreqBand_850, + .uplink = 1, + .rx = 1, + }, { + .fname = "calib_rxu_900.cfg", + .band = GsmL1_FreqBand_900, + .uplink = 1, + .rx = 1, + }, { + .fname = "calib_rxu_1800.cfg", + .band = GsmL1_FreqBand_1800, + .uplink = 1, + .rx = 1, + }, { + .fname = "calib_rxu_1900.cfg", + .band = GsmL1_FreqBand_1900, + .uplink = 1, + .rx = 1, + }, { + .fname = "calib_rxd_850.cfg", + .band = GsmL1_FreqBand_850, + .uplink = 0, + .rx = 1, + }, { + .fname = "calib_rxd_900.cfg", + .band = GsmL1_FreqBand_900, + .uplink = 0, + .rx = 1, + }, { + .fname = "calib_rxd_1800.cfg", + .band = GsmL1_FreqBand_1800, + .uplink = 0, + .rx = 1, + }, { + .fname = "calib_rxd_1900.cfg", + .band = GsmL1_FreqBand_1900, + .uplink = 0, + .rx = 1, + }, { + .fname = "calib_tx_850.cfg", + .band = GsmL1_FreqBand_850, + .uplink = 0, + .rx = 0, + }, { + .fname = "calib_tx_900.cfg", + .band = GsmL1_FreqBand_900, + .uplink = 0, + .rx = 0, + }, { + .fname = "calib_tx_1800.cfg", + .band = GsmL1_FreqBand_1800, + .uplink = 0, + .rx = 0, + }, { + .fname = "calib_tx_1900.cfg", + .band = GsmL1_FreqBand_1900, + .uplink = 0, + .rx = 0, + + }, +}; + +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0) +static const unsigned int arrsize_by_band[] = { + [GsmL1_FreqBand_850] = 124, + [GsmL1_FreqBand_900] = 194, + [GsmL1_FreqBand_1800] = 374, + [GsmL1_FreqBand_1900] = 299 +}; + +static float read_float(FILE *in) +{ + int rc; + float f = 0.0f; + + rc = fscanf(in, "%f\n", &f); + if (rc != 1) + LOGP(DL1C, LOGL_ERROR, + "Reading a float from calib data failed.\n"); + return f; +} + +static int read_int(FILE *in) +{ + int rc; + int i = 0; + + rc = fscanf(in, "%d\n", &i); + if (rc != 1) + LOGP(DL1C, LOGL_ERROR, + "Reading an int from calib data failed.\n"); + return i; +} + +/* some particular units have calibration data that is incompatible with + * firmware >= 3.3, so we need to alter it as follows: */ +static const float delta_by_band[Num_GsmL1_FreqBand] = { + [GsmL1_FreqBand_850] = -2.5f, + [GsmL1_FreqBand_900] = -2.0f, + [GsmL1_FreqBand_1800] = -8.0f, + [GsmL1_FreqBand_1900] = -12.0f, +}; + +extern const uint8_t fixup_macs[95][6]; + +static void determine_fixup(struct femtol1_hdl *fl1h) +{ + uint8_t macaddr[6]; + int rc, i; + + rc = eeprom_ReadEthAddr(macaddr); + if (rc != EEPROM_SUCCESS) { + LOGP(DL1C, LOGL_ERROR, + "Unable to read Ethenet MAC from EEPROM\n"); + return; + } + + /* assume no fixup is needed */ + fl1h->fixup_needed = FIXUP_NOT_NEEDED; + + if (fl1h->hw_info.dsp_version[0] < 3 || + (fl1h->hw_info.dsp_version[0] == 3 && + fl1h->hw_info.dsp_version[1] < 3)) { + LOGP(DL1C, LOGL_NOTICE, "No calibration table fix-up needed, " + "firmware < 3.3\n"); + return; + } + + for (i = 0; i < sizeof(fixup_macs)/6; i++) { + if (!memcmp(fixup_macs[i], macaddr, 6)) { + fl1h->fixup_needed = FIXUP_NEEDED; + break; + } + } + + LOGP(DL1C, LOGL_NOTICE, "MAC Address is %02x:%02x:%02x:%02x:%02x:%02x -> %s\n", + macaddr[0], macaddr[1], macaddr[2], macaddr[3], + macaddr[4], macaddr[5], + fl1h->fixup_needed == FIXUP_NEEDED ? "FIXUP" : "NO FIXUP"); +} + +static int fixup_needed(struct femtol1_hdl *fl1h) +{ + if (fl1h->fixup_needed == FIXUP_UNITILIAZED) + determine_fixup(fl1h); + + return fl1h->fixup_needed == FIXUP_NEEDED; +} +#endif /* API 2.4.0 */ + +static void calib_fixup_rx(struct femtol1_hdl *fl1h, SuperFemto_Prim_t *prim) +{ +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0) + SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq; + + if (fixup_needed(fl1h)) + rx->fExtRxGain += delta_by_band[rx->freqBand]; +#endif +} + +static int calib_file_read(const char *path, const struct calib_file_desc *desc, + SuperFemto_Prim_t *prim) +{ + FILE *in; + char fname[PATH_MAX]; + + fname[0] = '\0'; + snprintf(fname, sizeof(fname)-1, "%s/%s", path, desc->fname); + fname[sizeof(fname)-1] = '\0'; + + in = fopen(fname, "r"); + if (!in) { + LOGP(DL1C, LOGL_ERROR, + "Failed to open '%s' for calibration data.\n", fname); + return -1; + } + +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0) + int i; + if (desc->rx) { + SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq; + memset(rx, 0, sizeof(*rx)); + + prim->id = SuperFemto_PrimId_SetRxCalibTblReq; + + rx->freqBand = desc->band; + rx->bUplink = desc->uplink; + + rx->fExtRxGain = read_float(in); + rx->fRxMixGainCorr = read_float(in); + + for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++) + rx->fRxLnaGainCorr[i] = read_float(in); + + for (i = 0; i < arrsize_by_band[desc->band]; i++) + rx->fRxRollOffCorr[i] = read_float(in); + + if (desc->uplink) { + rx->u8IqImbalMode = read_int(in); + printf("%s: u8IqImbalMode=%d\n", desc->fname, rx->u8IqImbalMode); + + for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++) + rx->u16IqImbalCorr[i] = read_int(in); + } + } else { + SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq; + memset(tx, 0, sizeof(*tx)); + + prim->id = SuperFemto_PrimId_SetTxCalibTblReq; + + tx->freqBand = desc->band; + + for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++) + tx->fTxGainGmsk[i] = read_float(in); + + tx->fTx8PskCorr = read_float(in); + + for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++) + tx->fTxExtAttCorr[i] = read_float(in); + + for (i = 0; i < arrsize_by_band[desc->band]; i++) + tx->fTxRollOffCorr[i] = read_float(in); + } +#else +#warning Format of calibration tables before API version 2.4.0 not supported +#endif + fclose(in); + + return 0; +} + +static int calib_eeprom_read(const struct calib_file_desc *desc, SuperFemto_Prim_t *prim) +{ +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0) + eeprom_Error_t eerr; + int i; + if (desc->rx) { + SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq; + eeprom_RxCal_t rx_cal; + + memset(rx, 0, sizeof(*rx)); + + prim->id = SuperFemto_PrimId_SetRxCalibTblReq; + + rx->freqBand = desc->band; + rx->bUplink = desc->uplink; + + eerr = eeprom_ReadRxCal(desc->band, desc->uplink, &rx_cal); + if (eerr != EEPROM_SUCCESS) { + LOGP(DL1C, LOGL_ERROR, "Error reading RxCalibration " + "from EEPROM, band=%d, ul=%d, err=%d\n", + desc->band, desc->uplink, eerr); + return -EIO; + } + + rx->fExtRxGain = rx_cal.fExtRxGain; + rx->fRxMixGainCorr = rx_cal.fRxMixGainCorr; + + for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++) + rx->fRxLnaGainCorr[i] = rx_cal.fRxLnaGainCorr[i]; + + for (i = 0; i < arrsize_by_band[desc->band]; i++) + rx->fRxRollOffCorr[i] = rx_cal.fRxRollOffCorr[i]; + + if (desc->uplink) { + rx->u8IqImbalMode = rx_cal.u8IqImbalMode; + + for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++) + rx->u16IqImbalCorr[i] = rx_cal.u16IqImbalCorr[i]; + } +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(5,1,0) + rx->u8DspMajVer = rx_cal.u8DspMajVer; + rx->u8DspMinVer = rx_cal.u8DspMinVer; + rx->u8FpgaMajVer = rx_cal.u8FpgaMajVer; + rx->u8FpgaMinVer = rx_cal.u8FpgaMinVer; +#endif + } else { + SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq; + eeprom_TxCal_t tx_cal; + + memset(tx, 0, sizeof(*tx)); + + prim->id = SuperFemto_PrimId_SetTxCalibTblReq; + tx->freqBand = desc->band; + + eerr = eeprom_ReadTxCal(desc->band, &tx_cal); + if (eerr != EEPROM_SUCCESS) { + LOGP(DL1C, LOGL_ERROR, "Error reading TxCalibration " + "from EEPROM, band=%d, err=%d\n", + desc->band, eerr); + return -EIO; + } + + for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++) + tx->fTxGainGmsk[i] = tx_cal.fTxGainGmsk[i]; + + tx->fTx8PskCorr = tx_cal.fTx8PskCorr; + + for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++) + tx->fTxExtAttCorr[i] = tx_cal.fTxExtAttCorr[i]; + + for (i = 0; i < arrsize_by_band[desc->band]; i++) + tx->fTxRollOffCorr[i] = tx_cal.fTxRollOffCorr[i]; +#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(5,1,0) + tx->u8DspMajVer = tx_cal.u8DspMajVer; + tx->u8DspMinVer = tx_cal.u8DspMinVer; + tx->u8FpgaMajVer = tx_cal.u8FpgaMajVer; + tx->u8FpgaMinVer = tx_cal.u8FpgaMinVer; +#endif + } +#endif + + return 0; +} + +/* determine next calibration file index based on supported bands */ +static int next_calib_file_idx(uint32_t band_mask, int last_idx) +{ + int i; + + for (i = last_idx+1; i < ARRAY_SIZE(calib_files); i++) { + int band = band_femto2osmo(calib_files[i].band); + if (band < 0) + continue; + if (band_mask & band) + return i; + } + return -1; +} + +/* iteratively download the calibration data into the L1 */ + +static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, + void *data); + +/* send the calibration table for a single specified file */ +static int calib_file_send(struct femtol1_hdl *fl1h, + const struct calib_file_desc *desc) +{ + struct calib_send_state *st = &fl1h->st; + struct msgb *msg; + char *calib_path = fl1h->phy_inst->u.sysmobts.calib_path; + int rc; + + msg = sysp_msgb_alloc(); + + if (calib_path) + rc = calib_file_read(calib_path, desc, msgb_sysprim(msg)); + else + rc = calib_eeprom_read(desc, msgb_sysprim(msg)); + if (rc < 0) { + msgb_free(msg); + + /* still, we'd like to continue trying to load + * calibration for all other bands */ + st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support, + st->last_file_idx); + if (st->last_file_idx >= 0) + return calib_file_send(fl1h, + &calib_files[st->last_file_idx]); + else + return rc; + } + calib_fixup_rx(fl1h, msgb_sysprim(msg)); + + return l1if_req_compl(fl1h, msg, calib_send_compl_cb, NULL); +} + +/* completion callback after every SetCalibTbl is confirmed */ +static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, + void *data) +{ + struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx); + struct calib_send_state *st = &fl1h->st; + char *calib_path = fl1h->phy_inst->u.sysmobts.calib_path; + + LOGP(DL1C, LOGL_NOTICE, "L1 calibration table %s loaded (src: %s)\n", + calib_files[st->last_file_idx].fname, + calib_path ? "file" : "eeprom"); + + msgb_free(l1_msg); + + st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support, + st->last_file_idx); + if (st->last_file_idx >= 0) + return calib_file_send(fl1h, + &calib_files[st->last_file_idx]); + + LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n"); + eeprom_free_resources(); + + return 0; +} + + +int calib_load(struct femtol1_hdl *fl1h) +{ +#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0) + LOGP(DL1C, LOGL_ERROR, "L1 calibration is not supported on pre 2.4.0 firmware.\n"); + return -1; +#else + int idx = next_calib_file_idx(fl1h->hw_info.band_support, -1); + if (idx < 0) { + LOGP(DL1C, LOGL_ERROR, "No band_support?!?\n"); + return -1; + } + return calib_file_send(fl1h, &calib_files[idx]); +#endif +} + + +#if 0 +int main(int argc, char **argv) +{ + SuperFemto_Prim_t p; + int i; + + for (i = 0; i < ARRAY_SIZE(calib_files); i++) { + memset(&p, 0, sizeof(p)); + calib_read_file(argv[1], &calib_files[i], &p); + } + exit(0); +} +#endif |