/* Layer 1 (PHY) interface of osmo-bts OCTPHY integration */ /* Copyright (c) 2015 Harald Welte * * 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 . * */ #include #include #include #include #include #include "l1_if.h" #include "l1_oml.h" #include "l1_utils.h" #include "octphy_hw_api.h" #include #include #include /* Chapter 12.1 */ static int get_pcb_info_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { tOCTVC1_HW_MSG_PCB_INFO_RSP *pir = (tOCTVC1_HW_MSG_PCB_INFO_RSP *) resp->l2h; mOCTVC1_HW_MSG_PCB_INFO_RSP_SWAP(pir); LOGP(DL1C, LOGL_INFO, "HW-PCB-INFO.resp: Name=%s %s, Serial=%s, " "FileName=%s, InfoSource=%u, InfoState=%u, GpsName=%s, " "WiFiName=%s\n", pir->szName, pir->ulDeviceId ? "SEC" : "PRI", pir->szSerial, pir->szFilename, pir->ulInfoSource, pir->ulInfoState, pir->szGpsName, pir->szWifiName); msgb_free(resp); return 0; } /* Chapter 12.1 */ int octphy_hw_get_pcb_info(struct octphy_hdl *fl1h) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_PCB_INFO_CMD *pic; pic = (tOCTVC1_HW_MSG_PCB_INFO_CMD *) msgb_put(msg, sizeof(*pic)); l1if_fill_msg_hdr(&pic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_PCB_INFO_CID); mOCTVC1_HW_MSG_PCB_INFO_CMD_SWAP(pic); return l1if_req_compl(fl1h, msg, get_pcb_info_compl_cb, NULL); } /* Chapter 12.9 */ static int rf_port_info_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { tOCTVC1_HW_MSG_RF_PORT_INFO_RSP *pir = (tOCTVC1_HW_MSG_RF_PORT_INFO_RSP *) resp->l2h; mOCTVC1_HW_MSG_RF_PORT_INFO_RSP_SWAP(pir); LOGP(DL1C, LOGL_INFO, "RF-PORT-INFO.resp Idx=%u, InService=%u, " "hOwner=0x%x, Id=%u, FreqMin=%u, FreqMax=%u\n", pir->ulPortIndex, pir->ulInService, pir->hOwner, pir->ulPortInterfaceId, pir->ulFrequencyMinKhz, pir->ulFrequencyMaxKhz); msgb_free(resp); return 0; } /* Chapter 12.9 */ int octphy_hw_get_rf_port_info(struct octphy_hdl *fl1h, uint32_t index) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_RF_PORT_INFO_CMD *pic; pic = (tOCTVC1_HW_MSG_RF_PORT_INFO_CMD *) msgb_put(msg, sizeof(*pic)); l1if_fill_msg_hdr(&pic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_RF_PORT_INFO_CID); pic->ulPortIndex = index; mOCTVC1_HW_MSG_RF_PORT_INFO_CMD_SWAP(pic); return l1if_req_compl(fl1h, msg, rf_port_info_compl_cb, NULL); } /* Chapter 12.10 */ static int rf_port_stats_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { struct octphy_hw_get_cb_data *get_cb_data; tOCTVC1_HW_MSG_RF_PORT_STATS_RSP *psr = (tOCTVC1_HW_MSG_RF_PORT_STATS_RSP *) resp->l2h; mOCTVC1_HW_MSG_RF_PORT_STATS_RSP_SWAP(psr); LOGP(DL1C, LOGL_INFO, "RF-PORT-STATS.resp Idx=%u RadioStandard=%s, " "Rx(Bytes=%u, Overflow=%u, AvgBps=%u, Period=%uus, Freq=%u) " "Tx(Bytes=%i, Underflow=%u, AvgBps=%u, Period=%uus, Freq=%u)\n", psr->ulPortIndex, get_value_string(radio_std_vals, psr->ulRadioStandard), psr->RxStats.ulRxByteCnt, psr->RxStats.ulRxOverflowCnt, psr->RxStats.ulRxAverageBytePerSecond, psr->RxStats.ulRxAveragePeriodUs, #if OCTPHY_USE_FREQUENCY == 1 psr->RxStats.Frequency.ulValue, #else psr->RxStats.ulFrequencyKhz, #endif psr->TxStats.ulTxByteCnt, psr->TxStats.ulTxUnderflowCnt, psr->TxStats.ulTxAverageBytePerSecond, psr->TxStats.ulTxAveragePeriodUs, #if OCTPHY_USE_FREQUENCY == 1 psr->TxStats.Frequency.ulValue); #else psr->TxStats.ulFrequencyKhz); #endif get_cb_data = (struct octphy_hw_get_cb_data*) data; get_cb_data->cb(resp,get_cb_data->data); msgb_free(resp); return 0; } /* Chapter 12.10 */ int octphy_hw_get_rf_port_stats(struct octphy_hdl *fl1h, uint32_t index, struct octphy_hw_get_cb_data *cb_data) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_RF_PORT_STATS_CMD *psc; psc = (tOCTVC1_HW_MSG_RF_PORT_STATS_CMD *) msgb_put(msg, sizeof(*psc)); l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_RF_PORT_STATS_CID); psc->ulPortIndex = index; psc->ulResetStatsFlag = cOCT_FALSE; mOCTVC1_HW_MSG_RF_PORT_STATS_CMD_SWAP(psc); return l1if_req_compl(fl1h, msg, rf_port_stats_compl_cb, cb_data); } static const struct value_string rx_gain_mode_vals[] = { { cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_MGC, "Manual" }, { cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_AGC_FAST_ATK, "Automatic (fast)" }, { cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_AGC_SLOW_ATK, "Automatic (slow)" }, { 0, NULL } }; /* Chapter 12.13 */ static int rf_ant_rx_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP *arc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP *) resp->l2h; mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP_SWAP(arc); LOGP(DL1C, LOGL_INFO, "ANT-RX-CONFIG.resp(Port=%u, Ant=%u): %s, " "Gain %d dB, GainCtrlMode=%s\n", arc->ulPortIndex, arc->ulAntennaIndex, #ifdef OCTPHY_USE_RX_CONFIG arc->RxConfig.ulEnableFlag ? "Enabled" : "Disabled", arc->RxConfig.lRxGaindB/512, get_value_string(rx_gain_mode_vals, arc->RxConfig.ulRxGainMode)); #else arc->ulEnableFlag ? "Enabled" : "Disabled", arc->lRxGaindB/512, get_value_string(rx_gain_mode_vals, arc->ulRxGainMode)); #endif msgb_free(resp); return 0; } /* Chapter 12.13 */ int octphy_hw_get_rf_ant_rx_config(struct octphy_hdl *fl1h, uint32_t port_idx, uint32_t ant_idx) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD *psc; psc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD *) msgb_put(msg, sizeof(*psc)); l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CID); psc->ulPortIndex = port_idx; psc->ulAntennaIndex = ant_idx; mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD_SWAP(psc); return l1if_req_compl(fl1h, msg, rf_ant_rx_compl_cb, NULL); } /* Chapter 12.14 */ static int rf_ant_tx_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP *atc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP *) resp->l2h; mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP_SWAP(atc); LOGP(DL1C, LOGL_INFO, "ANT-TX-CONFIG.resp(Port=%u, Ant=%u): %s, " "Gain %d dB\n", atc->ulPortIndex, atc->ulAntennaIndex, #ifdef OCTPHY_USE_TX_CONFIG atc->TxConfig.ulEnableFlag? "Enabled" : "Disabled", atc->TxConfig.lTxGaindB/512); #else atc->ulEnableFlag ? "Enabled" : "Disabled", atc->lTxGaindB/512); #endif msgb_free(resp); return 0; } /* Chapter 12.14 */ int octphy_hw_get_rf_ant_tx_config(struct octphy_hdl *fl1h, uint32_t port_idx, uint32_t ant_idx) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD *psc; psc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD *) msgb_put(msg, sizeof(*psc)); l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CID); psc->ulPortIndex = port_idx; psc->ulAntennaIndex = ant_idx; mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD_SWAP(psc); return l1if_req_compl(fl1h, msg, rf_ant_tx_compl_cb, NULL); } static const struct value_string clocksync_source_vals[] = { { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_1HZ, "1 Hz" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_10MHZ, "10 MHz" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_30_72MHZ, "30.72 MHz" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_1HZ_EXT, "1 Hz (ext)"}, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_NONE, "None" }, { 0, NULL } }; static const struct value_string clocksync_sel_vals[] = { { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_AUTOSELECT, "Autoselect" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_CONFIG_FILE, "Config File" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_HOST_APPLICATION, "Host Application" }, { 0, NULL } }; static const struct value_string clocksync_source_state_vals[] = { { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_INVALID, "Invalid" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_VALID, "Valid" }, { cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_UNSPECIFIED, "Unspecified" }, { 0, NULL } }; /* Chapter 12.15 */ static int get_clock_sync_compl_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP *cir = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP *) resp->l2h; mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP_SWAP(cir); LOGP(DL1C, LOGL_INFO, "CLOCK-SYNC-MGR-INFO.resp Reference=%s ", get_value_string(clocksync_source_vals, cir->ulClkSourceRef)); LOGPC(DL1C, LOGL_INFO, "Selection=%s)\n", get_value_string(clocksync_sel_vals, cir->ulClkSourceSelection)); msgb_free(resp); return 0; } /* Chapter 12.15 */ int octphy_hw_get_clock_sync_info(struct octphy_hdl *fl1h) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD *cic; cic = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD *) msgb_put(msg, sizeof(*cic)); l1if_fill_msg_hdr(&cic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CID); mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD_SWAP(cic); return l1if_req_compl(fl1h, msg, get_clock_sync_compl_cb, NULL); } /* Chapter 12.16 */ static int get_clock_sync_stats_cb(struct octphy_hdl *fl1, struct msgb *resp, void *data) { struct octphy_hw_get_cb_data *get_cb_data; tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP *csr = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP *) resp->l2h; mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP_SWAP(csr); LOGP(DL1C, LOGL_INFO, "CLOCK-SYNC-MGR-STATS.resp State=%s, " "ClockError=%d DroppedCycles=%d, PllFreqHz=%u, PllFract=%u, " "SlipCnt=%u SyncLosses=%u SourceState=%u, DacValue=%u\n", get_value_string(clocksync_state_vals, csr->ulState), csr->lClockError, csr->lDroppedCycles, csr->ulPllFreqHz, csr->ulPllFractionalFreqHz, csr->ulSlipCnt, #if OCTPHY_USE_SYNC_LOSS_CNT == 1 csr->ulSyncLossCnt, #else csr->ulSyncLosseCnt, #endif csr->ulSourceState, csr->ulDacValue); get_cb_data = (struct octphy_hw_get_cb_data*) data; get_cb_data->cb(resp,get_cb_data->data); msgb_free(resp); return 0; } /* Chapter 12.16 */ int octphy_hw_get_clock_sync_stats(struct octphy_hdl *fl1h, struct octphy_hw_get_cb_data *cb_data) { struct msgb *msg = l1p_msgb_alloc(); tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD *csc; csc = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD *) msgb_put(msg, sizeof(*csc)); l1if_fill_msg_hdr(&csc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND, cOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CID); mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD_SWAP(csc); return l1if_req_compl(fl1h, msg, get_clock_sync_stats_cb, cb_data); }