diff options
Diffstat (limited to 'contrib/sysmobts-calib/sysmobts-layer1.c')
-rw-r--r-- | contrib/sysmobts-calib/sysmobts-layer1.c | 686 |
1 files changed, 686 insertions, 0 deletions
diff --git a/contrib/sysmobts-calib/sysmobts-layer1.c b/contrib/sysmobts-calib/sysmobts-layer1.c new file mode 100644 index 00000000..d886997f --- /dev/null +++ b/contrib/sysmobts-calib/sysmobts-layer1.c @@ -0,0 +1,686 @@ +/* Layer1 handling for the DSP/FPGA */ +/* + * (C) 2012 Holger Hans Peter Freyther + * + * 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 Affero 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 <stdlib.h> +#include <inttypes.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include <sysmocom/femtobts/superfemto.h> +#include <sysmocom/femtobts/gsml1prim.h> + +#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0])) + +#define BTS_DSP2ARM "/dev/msgq/superfemto_dsp2arm" +#define BTS_ARM2DSP "/dev/msgq/superfemto_arm2dsp" +#define L1_SIG_ARM2DSP "/dev/msgq/gsml1_sig_arm2dsp" +#define L1_SIG_DSP2ARM "/dev/msgq/gsml1_sig_dsp2arm" + +int set_clock_cor(int clock_cor, int calib, int source); +static int wait_read_ignore(int seconds); + +static int sys_dsp2arm = -1, + sys_arm2dsp = -1, + sig_dsp2arm = -1, + sig_arm2dsp = -1; + +static int sync_indicated = 0; +static int time_indicated = 0; + +static int open_devices() +{ + sys_dsp2arm = open(BTS_DSP2ARM, O_RDONLY); + if (sys_dsp2arm == -1) { + perror("Failed to open dsp2arm system queue"); + return -1; + } + + sys_arm2dsp = open(BTS_ARM2DSP, O_WRONLY); + if (sys_arm2dsp == -1) { + perror("Failed to open arm2dsp system queue"); + return -2; + } + + sig_dsp2arm = open(L1_SIG_DSP2ARM, O_RDONLY); + if (sig_dsp2arm == -1) { + perror("Failed to open dsp2arm sig queue"); + return -3; + } + + sig_arm2dsp = open(L1_SIG_ARM2DSP, O_WRONLY); + if (sig_arm2dsp == -1) { + perror("Failed to open arm2dsp sig queue"); + return -4; + } + + return 0; +} + +/** + * Send a primitive to the system queue + */ +static int send_primitive(int primitive, SuperFemto_Prim_t *prim) +{ + prim->id = primitive; + return write(sys_arm2dsp, prim, sizeof(*prim)) != sizeof(*prim); +} + +/** + * Wait for a confirmation + */ +static int wait_primitive(int wait_for, SuperFemto_Prim_t *prim) +{ + memset(prim, 0, sizeof(*prim)); + int rc = read(sys_dsp2arm, prim, sizeof(*prim)); + if (rc != sizeof(*prim)) { + printf("Short read in %s: %d\n", __func__, rc); + return -1; + } + + if (prim->id != wait_for) { + printf("Got primitive %d but waited for %d\n", + prim->id, wait_for); + return -2; + } + + return 0; +} + +/* The Cnf for the Req, assume it is a +1 */ +static int answer_for(int primitive) +{ + return primitive + 1; +} + +static int send_recv_primitive(int p, SuperFemto_Prim_t *prim) +{ + int rc; + rc = send_primitive(p, prim); + if (rc != 0) + return -1; + + rc = wait_primitive(answer_for(p), prim); + if (rc != 0) + return -2; + return 0; +} + +static int answer_for_sig(int prim) +{ + static const GsmL1_PrimId_t cnf[] = { + [GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf, + [GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf, + [GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf, + [GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf, + [GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf, + [GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf, + }; + + if (prim < 0 || prim >= ARRAY_SIZE(cnf)) { + printf("Unknown primitive: %d\n", prim); + exit(-3); + } + + return cnf[prim]; +} + +static int is_indication(int prim) +{ + return + prim == GsmL1_PrimId_MphTimeInd || + prim == GsmL1_PrimId_MphSyncInd || + prim == GsmL1_PrimId_PhConnectInd || + prim == GsmL1_PrimId_PhReadyToSendInd || + prim == GsmL1_PrimId_PhDataInd || + prim == GsmL1_PrimId_PhRaInd; +} + + +static int send_recv_sig_prim(int p, GsmL1_Prim_t *prim) +{ + int rc; + prim->id = p; + rc = write(sig_arm2dsp, prim, sizeof(*prim)); + if (rc != sizeof(*prim)) { + printf("Failed to write: %d\n", rc); + return -1; + } + + do { + rc = read(sig_dsp2arm, prim, sizeof(*prim)); + if (rc != sizeof(*prim)) { + printf("Failed to read: %d\n", rc); + return -2; + } + } while (is_indication(prim->id)); + + if (prim->id != answer_for_sig(p)) { + printf("Wrong L1 result got %d wanted %d for prim: %d\n", + prim->id, answer_for_sig(p), p); + return -3; + } + + return 0; +} + +static int wait_for_indication(int p, GsmL1_Prim_t *prim) +{ + int rc; + memset(prim, 0, sizeof(*prim)); + + struct timespec start_time, now_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + + /* + * TODO: select.... with timeout. The below will work 99% as we will + * get time indications very soonish after the connect + */ + for (;;) { + clock_gettime(CLOCK_MONOTONIC, &now_time); + if (now_time.tv_sec - start_time.tv_sec > 10) { + printf("Timeout waiting for indication.\n"); + return -4; + } + + rc = read(sig_dsp2arm, prim, sizeof(*prim)); + if (rc != sizeof(*prim)) { + printf("Failed to read.\n"); + return -1; + } + + if (!is_indication(prim->id)) { + printf("No indication: %d\n", prim->id); + return -2; + } + + if (p != prim->id && prim->id == GsmL1_PrimId_MphSyncInd) { + printf("Got sync.\n"); + sync_indicated = 1; + continue; + } + if (p != prim->id && prim->id == GsmL1_PrimId_MphTimeInd) { + time_indicated = 1; + continue; + } + + if (p != prim->id) { + printf("Wrong indication got %d wanted %d\n", + prim->id, p); + return -3; + } + + break; + } + + return 0; +} + +static int set_trace_flags(uint32_t dsp) +{ + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + prim.u.setTraceFlagsReq.u32Tf = dsp; + return send_primitive(SuperFemto_PrimId_SetTraceFlagsReq, &prim); +} + +static int reset_and_wait() +{ + int rc; + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + rc = send_recv_primitive(SuperFemto_PrimId_Layer1ResetReq, &prim); + if (rc != 0) + return -1; + if (prim.u.layer1ResetCnf.status != GsmL1_Status_Success) + return -2; + return 0; +} + +/** + * Open the message queues and (re-)initialize the DSP and FPGA + */ +int initialize_layer1(uint32_t dsp_flags) +{ + if (open_devices() != 0) { + printf("Failed to open devices.\n"); + return -1; + } + + if (set_trace_flags(dsp_flags) != 0) { + printf("Failed to set dsp flags.\n"); + return -2; + } + if (reset_and_wait() != 0) { + printf("Failed to reset the firmware.\n"); + return -3; + } + return 0; +} + +/** + * Print systems infos + */ +int print_system_info() +{ + int rc; + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + rc = send_recv_primitive(SuperFemto_PrimId_SystemInfoReq, &prim); + if (rc != 0) { + printf("Failed to send SystemInfoRequest.\n"); + return -1; + } + + if (prim.u.systemInfoCnf.status != GsmL1_Status_Success) { + printf("Failed to request SystemInfoRequest.\n"); + return -2; + } + +#define INFO_DSP(x) x.u.systemInfoCnf.dspVersion +#define INFO_FPGA(x) x.u.systemInfoCnf.fpgaVersion + printf("DSP v%d.%d.%d FPGA v%d.%d.%d Rev: %d Option: %d\n", + INFO_DSP(prim).major, INFO_DSP(prim).minor, INFO_DSP(prim).build, + INFO_FPGA(prim).major, INFO_FPGA(prim).minor, INFO_FPGA(prim).build, + prim.u.systemInfoCnf.boardVersion.rev, + prim.u.systemInfoCnf.boardVersion.option); +#undef INFO_DSP +#undef INFO_FPGA + return 0; +} + +int activate_rf_frontend(int clock_source, int initial_cor) +{ + int rc; + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + prim.u.activateRfReq.timing.u8TimSrc = 1; + prim.u.activateRfReq.msgq.u8UseTchMsgq = 0; + prim.u.activateRfReq.msgq.u8UsePdtchMsgq = 0; + + prim.u.activateRfReq.rfTrx.iClkCor = initial_cor; + prim.u.activateRfReq.rfTrx.clkSrc = clock_source; + + prim.u.activateRfReq.rfRx.iClkCor = initial_cor; + prim.u.activateRfReq.rfRx.clkSrc = clock_source; + + rc = send_recv_primitive(SuperFemto_PrimId_ActivateRfReq, &prim); + return rc; +} + +static int mph_init(int band, int arfcn, HANDLE *layer1) +{ + int rc; + GsmL1_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + prim.u.mphInitReq.deviceParam.devType = GsmL1_DevType_Rxd; + prim.u.mphInitReq.deviceParam.freqBand = band; + prim.u.mphInitReq.deviceParam.u16Arfcn = arfcn; + prim.u.mphInitReq.deviceParam.u16BcchArfcn = arfcn; + prim.u.mphInitReq.deviceParam.fRxPowerLevel = -75.f; + prim.u.mphInitReq.deviceParam.u8AutoTA = 1; + + rc = send_recv_sig_prim(GsmL1_PrimId_MphInitReq, &prim); + if (rc != 0) { + printf("Failed to initialize the physical channel.\n"); + return -1; + } + + if (prim.u.mphInitCnf.status != GsmL1_Status_Success) { + printf("MPH Init failed.\n"); + return -2; + } + +#if 0 + if (prim.u.mphInitCnf.freqBand != band) { + printf("Layer1 ignored the band: %d\n", + prim.u.mphInitCnf.freqBand); + return -3; + } +#endif + + *layer1 = prim.u.mphInitCnf.hLayer1; + return 0; +} + +int mph_close(HANDLE layer1) +{ + int rc; + GsmL1_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + prim.u.mphCloseReq.hLayer1 = layer1; + rc = send_recv_sig_prim(GsmL1_PrimId_MphCloseReq, &prim); + if (rc != 0) { + printf("Failed to close the MPH\n"); + return -6; + } + if (prim.u.mphCloseCnf.status != GsmL1_Status_Success) { + printf("MPH Close failed.\n"); + return -7; + } + + return 0; +} + +int follow_sch(int band, int arfcn, int clock, int ref, HANDLE *layer1) +{ + int rc; + GsmL1_Prim_t prim; + + time_indicated = 0; + sync_indicated = 0; + + rc = mph_init(band, arfcn, layer1); + if (rc != 0) + return rc; + + /* 1.) Connect */ + memset(&prim, 0, sizeof(prim)); + prim.u.mphConnectReq.hLayer1 = *layer1; + prim.u.mphConnectReq.u8Tn = 0; + prim.u.mphConnectReq.logChComb = GsmL1_LogChComb_V; + rc = send_recv_sig_prim(GsmL1_PrimId_MphConnectReq, &prim); + if (rc != 0) { + printf("Failed to connect.\n"); + return -1; + } + if (prim.u.mphConnectCnf.status != GsmL1_Status_Success) { + printf("Connect failed.\n"); + return -2; + } + if (prim.u.mphConnectCnf.u8Tn != 0) { + printf("Wrong timeslot.\n"); + return -3; + } + + /* 2.) Activate */ + memset(&prim, 0, sizeof(prim)); + prim.u.mphActivateReq.hLayer1 = *layer1; + prim.u.mphActivateReq.u8Tn = 0; + prim.u.mphActivateReq.sapi = GsmL1_Sapi_Sch; + prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink; + rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim); + if (rc != 0) { + printf("Activation failed.\n"); + return -4; + } + if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) { + printf("Activation not successful.\n"); + return -5; + } + + /* 3.) Wait for indication... TODO: check... */ + printf("Waiting for connect indication.\n"); + rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim); + if (rc != 0) { + printf("Didn't get a connect indication.\n"); + return rc; + } + + /* 4.) Indication Syndication TODO: check... */ + if (!sync_indicated) { + printf("Waiting for sync indication.\n"); + rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim); + if (rc < 0) { + printf("Didn't get a sync indication.\n"); + return -23; + } else if (rc == 0) { + if (!prim.u.mphSyncInd.u8Synced) { + printf("Failed to get sync.\n"); + return -23; + } else { + printf("Synced.\n"); + } + } + } else { + printf("Already synced.\n"); + } + + return 0; +} + +int follow_bcch(HANDLE layer1) +{ + int rc; + GsmL1_Prim_t prim; + + /* 1.) Activate BCCH... */ + memset(&prim, 0, sizeof(prim)); + prim.u.mphActivateReq.hLayer1 = layer1; + prim.u.mphActivateReq.u8Tn = 0; + prim.u.mphActivateReq.sapi = GsmL1_Sapi_Bcch; + prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink; + + rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim); + if (rc != 0) { + printf("Activation failed.\n"); + return -4; + } + if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) { + printf("Activation not successful.\n"); + return -5; + } + + /* 2.) Wait for indication... */ + printf("Waiting for connect indication.\n"); + rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim); + if (rc != 0) { + printf("Didn't get a connect indication.\n"); + return rc; + } + + /* 3.) Wait for PhDataInd... */ + printf("Waiting for BCCH data.\n"); + rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim); + if (rc != 0) { + printf("Didn't get BCCH data.\n"); + return rc; + } + + return 0; +} + +int set_clock_cor(int clock_cor, int calib, int source) +{ + int rc; + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + prim.u.rfClockSetupReq.rfTrx.iClkCor = clock_cor; + prim.u.rfClockSetupReq.rfTrx.clkSrc = calib; + prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor; + prim.u.rfClockSetupReq.rfRx.clkSrc = calib; + prim.u.rfClockSetupReq.rfTrxClkCal.clkSrc = source; + + rc = send_recv_primitive(SuperFemto_PrimId_RfClockSetupReq, &prim); + if (rc != 0) { + printf("Failed to set the clock setup.\n"); + return -1; + } + if (prim.u.rfClockSetupCnf.status != GsmL1_Status_Success) { + printf("Clock setup was not successfull.\n"); + return -2; + } + + return 0; +} + +int rf_clock_info(HANDLE *layer1, int *clkErr, int *clkErrRes) +{ + SuperFemto_Prim_t prim; + memset(&prim, 0, sizeof(prim)); + + int rc; + + /* reset the counter */ + prim.u.rfClockInfoReq.u8RstClkCal = 1; + rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim); + if (rc != 0) { + printf("Failed to reset the clock info.\n"); + return -1; + } + + /* wait for a value */ + wait_read_ignore(15); + + /* ask for the current counter/error */ + memset(&prim, 0, sizeof(prim)); + prim.u.rfClockInfoReq.u8RstClkCal = 0; + rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim); + if (rc != 0) { + printf("Failed to get the clock info.\n"); + return -2; + } + + printf("Error: %d Res: %d\n", + prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr, + prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes); + *clkErr = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr; + *clkErrRes = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes; + return 0; +} + +int power_scan(int band, int arfcn, int duration, float *mean_rssi) +{ + int rc; + HANDLE layer1; + GsmL1_Prim_t prim; + + /* init */ + rc = mph_init(band, arfcn, &layer1); + if (rc != 0) + return rc; + + /* mph measure request */ + memset(&prim, 0, sizeof(prim)); + prim.u.mphMeasureReq.hLayer1 = layer1; + prim.u.mphMeasureReq.u32Duration = duration; + rc = send_recv_sig_prim(GsmL1_PrimId_MphMeasureReq, &prim); + if (rc != 0) { + printf("Failed to send measurement request.\n"); + return -4; + } + + if (prim.u.mphMeasureCnf.status != GsmL1_Status_Success) { + printf("MphMeasureReq was not confirmed.\n"); + return -5; + } + + *mean_rssi = prim.u.mphMeasureCnf.fMeanRssi; + + /* close */ + rc = mph_close(layer1); + return rc; +} + +/** + * Wait for indication... + */ +int wait_for_sync(HANDLE layer1, int cor, int calib, int source) +{ + GsmL1_Prim_t prim; + int rc; + + rc = set_clock_cor(cor, calib, source); + if (rc != 0) { + printf("Failed to set the clock correction.\n"); + return -1; + } + + sync_indicated = 0; + rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim); + if (rc < 0 && rc != -4) { + return rc; + } else if (rc == 0) { + if (!prim.u.mphSyncInd.u8Synced) { + printf("Failed to get sync.\n"); + return 0; + } + printf("Synced.\n"); + return 1; + } + + return 0; +} + +/** + * Make sure the pipe is not running full. + * + */ +static int wait_read_ignore(int seconds) +{ + int max, rc; + fd_set fds; + struct timeval timeout; + + max = sys_dsp2arm > sig_dsp2arm ? sys_dsp2arm : sig_dsp2arm; + + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + + while (1) { + FD_ZERO(&fds); + FD_SET(sys_dsp2arm, &fds); + FD_SET(sig_dsp2arm, &fds); + + + rc = select(max + 1, &fds, NULL, NULL, &timeout); + if (rc == -1) { + printf("Failed to select.\n"); + return -1; + } else if (rc) { + if (FD_ISSET(sys_dsp2arm, &fds)) { + SuperFemto_Prim_t prim; + rc = read(sys_dsp2arm, &prim, sizeof(prim)); + if (rc != sizeof(prim)) { + perror("Failed to read system primitive"); + return -2; + } + } + if (FD_ISSET(sig_dsp2arm, &fds)) { + GsmL1_Prim_t prim; + rc = read(sig_dsp2arm, &prim, sizeof(prim)); + if (rc != sizeof(prim)) { + perror("Failed to read signal primitiven"); + return -3; + } + } + } else if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0) { + break; + } + +#ifndef __linux__ +#error "Non portable code" +#endif + } + return 0; +} |