| /* Nokia XXXsite family specific code */ |
| |
| /* (C) 2011 by Dieter Spaar <spaar@mirider.augusta.de> |
| * |
| * 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/>. |
| * |
| */ |
| |
| /* |
| TODO: Attention: There are some static variables used for states during |
| configuration. Those variables have to be moved to a BTS specific context, |
| otherwise there will most certainly be problems if more than one Nokia BTS |
| is used. |
| */ |
| |
| #include <time.h> |
| |
| #include <osmocom/gsm/tlv.h> |
| |
| #include <openbsc/debug.h> |
| #include <openbsc/gsm_data.h> |
| #include <openbsc/abis_nm.h> |
| #include <osmocom/abis/e1_input.h> |
| #include <openbsc/signal.h> |
| |
| #include <osmocom/core/timer.h> |
| |
| #include <osmocom/abis/lapd.h> |
| |
| /* TODO: put in a separate file ? */ |
| |
| #define RESET_INTERVAL 15, 0 /* 15 seconds */ |
| |
| extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); |
| /* was static in system_information.c */ |
| extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts); |
| |
| static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts); |
| static void reset_timer_cb(void *_bts); |
| static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref); |
| static int dump_elements(uint8_t * data, int len) __attribute__((unused)); |
| |
| static void bootstrap_om_bts(struct gsm_bts *bts) |
| { |
| LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); |
| |
| if (!bts->nokia.skip_reset) { |
| if (!bts->nokia.did_reset) |
| abis_nm_reset(bts, 1); |
| } else |
| bts->nokia.did_reset = 1; |
| } |
| |
| static void bootstrap_om_trx(struct gsm_bts_trx *trx) |
| { |
| LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", |
| trx->bts->nr, trx->nr); |
| } |
| |
| static int shutdown_om(struct gsm_bts *bts) |
| { |
| /* TODO !? */ |
| return 0; |
| } |
| |
| #define SAPI_OML 62 |
| #define SAPI_RSL 0 |
| |
| /* |
| |
| Tell LAPD to start start the SAP (send SABM requests) for all signalling |
| timeslots in this line |
| |
| Attention: this has to be adapted for mISDN |
| */ |
| |
| static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi) |
| { |
| struct e1inp_sign_link *link; |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(line->ts); i++) { |
| struct e1inp_ts *ts = &line->ts[i]; |
| |
| if (ts->type != E1INP_TS_TYPE_SIGN) |
| continue; |
| |
| llist_for_each_entry(link, &ts->sign.sign_links, list) { |
| if (sapi != -1 && link->sapi != sapi) |
| continue; |
| |
| #if 0 /* debugging */ |
| printf("sap start/stop (%d): %d tei=%d sapi=%d\n", |
| start, i + 1, link->tei, link->sapi); |
| #endif |
| |
| if (start) { |
| ts->lapd->profile.t200_sec = 1; |
| ts->lapd->profile.t200_usec = 0; |
| lapd_sap_start(ts->lapd, link->tei, |
| link->sapi); |
| } else |
| lapd_sap_stop(ts->lapd, link->tei, |
| link->sapi); |
| } |
| } |
| } |
| |
| /* Callback function to be called every time we receive a signal from INPUT */ |
| static int gbl_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| struct gsm_bts *bts; |
| |
| if (subsys != SS_L_GLOBAL) |
| return 0; |
| |
| switch (signal) { |
| case S_GLOBAL_BTS_CLOSE_OM: |
| bts = signal_data; |
| if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) |
| shutdown_om(signal_data); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* Callback function to be called every time we receive a signal from INPUT */ |
| static int inp_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| struct input_signal_data *isd = signal_data; |
| |
| if (subsys != SS_L_INPUT) |
| return 0; |
| |
| switch (signal) { |
| case S_L_INP_LINE_INIT: |
| start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */ |
| break; |
| case S_L_INP_TEI_DN: |
| break; |
| case S_L_INP_TEI_UP: |
| switch (isd->link_type) { |
| case E1INP_SIGN_OML: |
| if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE) |
| break; |
| |
| if (isd->tei == isd->trx->bts->oml_tei) |
| bootstrap_om_bts(isd->trx->bts); |
| else |
| bootstrap_om_trx(isd->trx); |
| break; |
| } |
| break; |
| case S_L_INP_TEI_UNKNOWN: |
| /* We are receiving LAPD frames with one TEI that we do not |
| * seem to know, likely that we (the BSC) stopped working |
| * and lost our local states. However, the BTS is already |
| * configured, we try to take over the RSL links. */ |
| start_sabm_in_line(isd->line, 1, SAPI_RSL); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void nm_statechg_evt(unsigned int signal, |
| struct nm_statechg_signal_data *nsd) |
| { |
| if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE) |
| return; |
| } |
| |
| static int nm_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| if (subsys != SS_NM) |
| return 0; |
| |
| switch (signal) { |
| case S_NM_STATECHG_OPER: |
| case S_NM_STATECHG_ADM: |
| nm_statechg_evt(signal, signal_data); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static const struct value_string nokia_msgt_name[] = { |
| { 0x80, "NOKIA_BTS_CONF_DATA" }, |
| { 0x81, "NOKIA_BTS_ACK" }, |
| { 0x82, "NOKIA_BTS_OMU_STARTED" }, |
| { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" }, |
| { 0x84, "NOKIA_BTS_MF_REQ" }, |
| { 0x85, "NOKIA_BTS_AF_REQ" }, |
| { 0x86, "NOKIA_BTS_RESET_REQ" }, |
| { 0x87, "NOKIA_reserved" }, |
| { 0x88, "NOKIA_BTS_CONF_REQ" }, |
| { 0x89, "NOKIA_BTS_TEST_REQ" }, |
| { 0x8A, "NOKIA_BTS_TEST_REPORT" }, |
| { 0x8B, "NOKIA_reserved" }, |
| { 0x8C, "NOKIA_reserved" }, |
| { 0x8D, "NOKIA_reserved" }, |
| { 0x8E, "NOKIA_BTS_CONF_COMPL" }, |
| { 0x8F, "NOKIA_reserved" }, |
| { 0x90, "NOKIA_BTS_STM_TEST_REQ" }, |
| { 0x91, "NOKIA_BTS_STM_TEST_REPORT" }, |
| { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" }, |
| { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" }, |
| { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" }, |
| { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" }, |
| { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" }, |
| { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" }, |
| { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" }, |
| { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" }, |
| { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" }, |
| { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" }, |
| { 0x9C, "NOKIA_BTS_HW_REQ" }, |
| { 0x9D, "NOKIA_BTS_HW_REPORT" }, |
| { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" }, |
| { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" }, |
| { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" }, |
| { 0xA1, "NOKIA_BTS_CLOCK_REQ" }, |
| { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" }, |
| { 0xA3, "NOKIA_AC_INTERRUPTED" }, |
| { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" }, |
| { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" }, |
| { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" }, |
| { 0xA7, "NOKIA_AC_CIRCUIT_REQ" }, |
| { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" }, |
| { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" }, |
| { 0xAA, "NOKIA_BTS_GSM_TIME" }, |
| { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" }, |
| { 0xAC, "NOKIA_BTS_STATE_CHANGED" }, |
| { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" }, |
| { 0xAE, "NOKIA_BTS_ALARM" }, |
| { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" }, |
| { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" }, |
| { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" }, |
| { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" }, |
| { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" }, |
| { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" }, |
| { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" }, |
| { 0xB6, "NOKIA_BTS_LCS_COMMAND" }, |
| { 0xB7, "NOKIA_BTS_LCS_ANSWER" }, |
| { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" }, |
| { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" }, |
| { 0, NULL } |
| }; |
| |
| static const char *get_msg_type_name_string(uint8_t msg_type) |
| { |
| return get_value_string(nokia_msgt_name, msg_type); |
| } |
| |
| static const struct value_string nokia_element_name[] = { |
| { 0x01, "Ny1" }, |
| { 0x02, "T3105_F" }, |
| { 0x03, "Interference band limits" }, |
| { 0x04, "Interference report timer in secs" }, |
| { 0x05, "Channel configuration per TS" }, |
| { 0x06, "BSIC" }, |
| { 0x07, "RACH report timer in secs" }, |
| { 0x08, "Hardware database status" }, |
| { 0x09, "BTS RX level" }, |
| { 0x0A, "ARFN" }, |
| { 0x0B, "STM antenna attenuation" }, |
| { 0x0C, "Cell allocation bitmap" }, |
| { 0x0D, "Radio definition per TS" }, |
| { 0x0E, "Frame number" }, |
| { 0x0F, "Antenna diversity" }, |
| { 0x10, "T3105_D" }, |
| { 0x11, "File format" }, |
| { 0x12, "Last File" }, |
| { 0x13, "BTS type" }, |
| { 0x14, "Erasure mode" }, |
| { 0x15, "Hopping mode" }, |
| { 0x16, "Floating TRX" }, |
| { 0x17, "Power supplies" }, |
| { 0x18, "Reset type" }, |
| { 0x19, "Averaging period" }, |
| { 0x1A, "RBER2" }, |
| { 0x1B, "LAC" }, |
| { 0x1C, "CI" }, |
| { 0x1D, "Failure parameters" }, |
| { 0x1E, "(RF max power reduction)" }, |
| { 0x1F, "Measured RX_SENS" }, |
| { 0x20, "Extended cell radius" }, |
| { 0x21, "reserved" }, |
| { 0x22, "Success-Failure" }, |
| { 0x23, "Ack-Nack" }, |
| { 0x24, "OMU test results" }, |
| { 0x25, "File identity" }, |
| { 0x26, "Generation and version code" }, |
| { 0x27, "SW description" }, |
| { 0x28, "BCCH LEV" }, |
| { 0x29, "Test type" }, |
| { 0x2A, "Subscriber number" }, |
| { 0x2B, "reserved" }, |
| { 0x2C, "HSN" }, |
| { 0x2D, "reserved" }, |
| { 0x2E, "MS RXLEV" }, |
| { 0x2F, "MS TXLEV" }, |
| { 0x30, "RXQUAL" }, |
| { 0x31, "RX SENS" }, |
| { 0x32, "Alarm block" }, |
| { 0x33, "Neighbouring BCCH levels" }, |
| { 0x34, "STM report type" }, |
| { 0x35, "MA" }, |
| { 0x36, "MAIO" }, |
| { 0x37, "H_FLAG" }, |
| { 0x38, "TCH_ARFN" }, |
| { 0x39, "Clock output" }, |
| { 0x3A, "Transmitted power" }, |
| { 0x3B, "Clock sync" }, |
| { 0x3C, "TMS protocol discriminator" }, |
| { 0x3D, "TMS protocol data" }, |
| { 0x3E, "FER" }, |
| { 0x3F, "SWR result" }, |
| { 0x40, "Object identity" }, |
| { 0x41, "STM RX Antenna Test" }, |
| { 0x42, "reserved" }, |
| { 0x43, "reserved" }, |
| { 0x44, "Object current state" }, |
| { 0x45, "reserved" }, |
| { 0x46, "FU channel configuration" }, |
| { 0x47, "reserved" }, |
| { 0x48, "ARFN of a CU" }, |
| { 0x49, "FU radio definition" }, |
| { 0x4A, "reserved" }, |
| { 0x4B, "Severity" }, |
| { 0x4C, "Diversity selection" }, |
| { 0x4D, "RX antenna test" }, |
| { 0x4E, "RX antenna supervision period" }, |
| { 0x4F, "RX antenna state" }, |
| { 0x50, "Sector configuration" }, |
| { 0x51, "Additional info" }, |
| { 0x52, "SWR parameters" }, |
| { 0x53, "HW inquiry mode" }, |
| { 0x54, "reserved" }, |
| { 0x55, "Availability status" }, |
| { 0x56, "reserved" }, |
| { 0x57, "EAC inputs" }, |
| { 0x58, "EAC outputs" }, |
| { 0x59, "reserved" }, |
| { 0x5A, "Position" }, |
| { 0x5B, "HW unit identity" }, |
| { 0x5C, "RF test signal attenuation" }, |
| { 0x5D, "Operational state" }, |
| { 0x5E, "Logical object identity" }, |
| { 0x5F, "reserved" }, |
| { 0x60, "BS_TXPWR_OM" }, |
| { 0x61, "Loop_Duration" }, |
| { 0x62, "LNA_Path_Selection" }, |
| { 0x63, "Serial number" }, |
| { 0x64, "HW version" }, |
| { 0x65, "Obj. identity and obj. state" }, |
| { 0x66, "reserved" }, |
| { 0x67, "EAC input definition" }, |
| { 0x68, "EAC id and text" }, |
| { 0x69, "HW unit status" }, |
| { 0x6A, "SW release version" }, |
| { 0x6B, "FW version" }, |
| { 0x6C, "Bit_Error_Ratio" }, |
| { 0x6D, "RXLEV_with_Attenuation" }, |
| { 0x6E, "RXLEV_without_Attenuation" }, |
| { 0x6F, "reserved" }, |
| { 0x70, "CU_Results" }, |
| { 0x71, "reserved" }, |
| { 0x72, "LNA_Path_Results" }, |
| { 0x73, "RTE Results" }, |
| { 0x74, "Real Time" }, |
| { 0x75, "RX diversity selection" }, |
| { 0x76, "EAC input config" }, |
| { 0x77, "Feature support" }, |
| { 0x78, "File version" }, |
| { 0x79, "Outputs" }, |
| { 0x7A, "FU parameters" }, |
| { 0x7B, "Diagnostic info" }, |
| { 0x7C, "FU BSIC" }, |
| { 0x7D, "TRX Configuration" }, |
| { 0x7E, "Download status" }, |
| { 0x7F, "RX difference limit" }, |
| { 0x80, "TRX HW capability" }, |
| { 0x81, "Common HW config" }, |
| { 0x82, "Autoconfiguration pool size" }, |
| { 0x83, "TRE diagnostic info" }, |
| { 0x84, "TRE object identity" }, |
| { 0x85, "New TRE Info" }, |
| { 0x86, "Acknowledgement period" }, |
| { 0x87, "Synchronization mode" }, |
| { 0x88, "reserved" }, |
| { 0x89, "Block Control Data" }, |
| { 0x8A, "SW load mode" }, |
| { 0x8B, "Recommended recovery action" }, |
| { 0x8C, "BSC BCF id" }, |
| { 0x8D, "Q1 baud rate" }, |
| { 0x8E, "Allocation status" }, |
| { 0x8F, "Functional entity number" }, |
| { 0x90, "Transmission delay" }, |
| { 0x91, "Loop Duration ms" }, |
| { 0x92, "Logical channel" }, |
| { 0x93, "Q1 address" }, |
| { 0x94, "Alarm detail" }, |
| { 0x95, "Cabinet type" }, |
| { 0x96, "HW unit existence" }, |
| { 0x97, "RF power parameters" }, |
| { 0x98, "Message scenario" }, |
| { 0x99, "HW unit max amount" }, |
| { 0x9A, "Master TRX" }, |
| { 0x9B, "Transparent data" }, |
| { 0x9C, "BSC topology info" }, |
| { 0x9D, "Air i/f modulation" }, |
| { 0x9E, "LCS Q1 command data" }, |
| { 0x9F, "Frame number offset" }, |
| { 0xA0, "Abis TSL" }, |
| { 0xA1, "Dynamic pool info" }, |
| { 0xA2, "LCS LLP data" }, |
| { 0xA3, "LCS Q1 answer data" }, |
| { 0xA4, "DFCA FU Radio Definition" }, |
| { 0xA5, "Antenna hopping" }, |
| { 0xA6, "Field record sequence number" }, |
| { 0xA7, "Timeslot offslot" }, |
| { 0xA8, "EPCR capability" }, |
| { 0xA9, "Connectsite optional element" }, |
| { 0xAA, "TSC" }, |
| { 0xAB, "Special TX Power Setting" }, |
| { 0xAC, "Optional sync settings" }, |
| { 0xFA, "Abis If parameters" }, |
| { 0, NULL } |
| }; |
| |
| static const char *get_element_name_string(uint16_t element) |
| { |
| return get_value_string(nokia_element_name, element); |
| } |
| |
| static const struct value_string nokia_bts_types[] = { |
| { 0x0a, "MetroSite GSM 900" }, |
| { 0x0b, "MetroSite GSM 1800" }, |
| { 0x0c, "MetroSite GSM 1900 (PCS)" }, |
| { 0x0d, "MetroSite GSM 900 & 1800" }, |
| { 0x0e, "InSite GSM 900" }, |
| { 0x0f, "InSite GSM 1800" }, |
| { 0x10, "InSite GSM 1900" }, |
| { 0x11, "UltraSite GSM 900" }, |
| { 0x12, "UltraSite GSM 1800" }, |
| { 0x13, "UltraSite GSM/US-TDMA 1900" }, |
| { 0x14, "UltraSite GSM 900 & 1800" }, |
| { 0x16, "UltraSite GSM/US-TDMA 850" }, |
| { 0x18, "MetroSite GSM/US-TDMA 850" }, |
| { 0x19, "UltraSite GSM 800/1900" }, |
| { 0, NULL } |
| }; |
| |
| static const char *get_bts_type_string(uint8_t type) |
| { |
| return get_value_string(nokia_bts_types, type); |
| } |
| |
| static const struct value_string nokia_severity[] = { |
| { 0, "indeterminate" }, |
| { 1, "critical" }, |
| { 2, "major" }, |
| { 3, "minor" }, |
| { 4, "warning" }, |
| { 0, NULL } |
| }; |
| |
| static const char *get_severity_string(uint8_t severity) |
| { |
| return get_value_string(nokia_severity, severity); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* some message IDs */ |
| |
| #define NOKIA_MSG_CONF_DATA 128 |
| #define NOKIA_MSG_ACK 129 |
| #define NOKIA_MSG_OMU_STARTED 130 |
| #define NOKIA_MSG_START_DOWNLOAD_REQ 131 |
| #define NOKIA_MSG_MF_REQ 132 |
| #define NOKIA_MSG_RESET_REQ 134 |
| #define NOKIA_MSG_CONF_REQ 136 |
| #define NOKIA_MSG_CONF_COMPLETE 142 |
| #define NOKIA_MSG_BLOCK_CTRL_REQ 168 |
| #define NOKIA_MSG_STATE_CHANGED 172 |
| #define NOKIA_MSG_ALARM 174 |
| |
| /* some element IDs */ |
| |
| #define NOKIA_EI_BTS_TYPE 0x13 |
| #define NOKIA_EI_ACK 0x23 |
| #define NOKIA_EI_ADD_INFO 0x51 |
| #define NOKIA_EI_SEVERITY 0x4B |
| #define NOKIA_EI_ALARM_DETAIL 0x94 |
| |
| #define OM_ALLOC_SIZE 1024 |
| #define OM_HEADROOM_SIZE 128 |
| |
| static uint8_t fu_config_template[] = { |
| 0x7F, 0x7A, 0x39, |
| /* ID = 0x7A (FU parameters) ## constructed ## */ |
| /* length = 57 */ |
| /* [3] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [6] */ |
| 0x00, 0x07, 0x01, 0xFF, |
| |
| 0x41, 0x02, |
| /* ID = 0x01 (Ny1) */ |
| /* length = 2 */ |
| /* [12] */ |
| 0x00, 0x05, |
| |
| 0x42, 0x02, |
| /* ID = 0x02 (T3105_F) */ |
| /* length = 2 */ |
| /* [16] */ |
| 0x00, 0x28, /* FIXME: use net->T3105 */ |
| |
| 0x50, 0x02, |
| /* ID = 0x10 (T3105_D) */ |
| /* length = 2 */ |
| /* [20] */ |
| 0x00, 0x28, /* FIXME: use net->T3105 */ |
| |
| 0x43, 0x05, |
| /* ID = 0x03 (Interference band limits) */ |
| /* length = 5 */ |
| /* [24] */ |
| 0x0F, 0x1B, 0x27, 0x33, 0x3F, |
| |
| 0x44, 0x02, |
| /* ID = 0x04 (Interference report timer in secs) */ |
| /* length = 2 */ |
| /* [31] */ |
| 0x00, 0x10, |
| |
| 0x47, 0x01, |
| /* ID = 0x07 (RACH report timer in secs) */ |
| /* length = 1 */ |
| /* [35] */ |
| 0x1E, |
| |
| 0x4C, 0x10, |
| /* ID = 0x0C (Cell allocation bitmap) ####### */ |
| /* length = 16 */ |
| /* [38] */ |
| 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| 0x59, 0x01, |
| /* ID = 0x19 (Averaging period) */ |
| /* length = 1 */ |
| /* [56] */ |
| 0x01, |
| |
| 0x5E, 0x01, |
| /* ID = 0x1E ((RF max power reduction)) */ |
| /* length = 1 */ |
| /* [59] */ |
| 0x00, |
| |
| 0x7F, 0x46, 0x11, |
| /* ID = 0x46 (FU channel configuration) ## constructed ## */ |
| /* length = 17 */ |
| /* [63] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [66] */ |
| 0x00, 0x07, 0x01, 0xFF, |
| |
| 0x45, 0x08, |
| /* ID = 0x05 (Channel configuration per TS) */ |
| /* length = 8 */ |
| /* [72] */ |
| 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, |
| |
| 0x7F, 0x65, 0x0B, |
| /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */ |
| /* length = 11 */ |
| /* [83] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [86] */ |
| 0x00, 0x04, 0x01, 0xFF, |
| |
| 0x5F, 0x44, 0x01, |
| /* ID = 0x44 (Object current state) */ |
| /* length = 1 */ |
| /* [93] */ |
| 0x03, |
| |
| 0x7F, 0x7C, 0x0A, |
| /* ID = 0x7C (FU BSIC) ## constructed ## */ |
| /* length = 10 */ |
| /* [97] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [100] */ |
| 0x00, 0x07, 0x01, 0xFF, |
| |
| 0x46, 0x01, |
| /* ID = 0x06 (BSIC) */ |
| /* length = 1 */ |
| /* [106] */ |
| 0x00, |
| |
| 0x7F, 0x48, 0x0B, |
| /* ID = 0x48 (ARFN of a CU) ## constructed ## */ |
| /* length = 11 */ |
| /* [110] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [113] */ |
| 0x00, 0x08, 0x01, 0xFF, |
| |
| 0x4A, 0x02, |
| /* ID = 0x0A (ARFN) ####### */ |
| /* length = 2 */ |
| /* [119] */ |
| 0x03, 0x62, |
| |
| 0x7F, 0x49, 0x59, |
| /* ID = 0x49 (FU radio definition) ## constructed ## */ |
| /* length = 89 */ |
| /* [124] */ |
| |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [127] */ |
| 0x00, 0x07, 0x01, 0xFF, |
| |
| 0x4D, 0x50, |
| /* ID = 0x0D (Radio definition per TS) ####### */ |
| /* length = 80 */ |
| /* [133] */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */ |
| 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x62, |
| }; |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* |
| build the configuration for each TRX |
| */ |
| |
| static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, |
| uint8_t * fu_config, int *hopping) |
| { |
| int i; |
| |
| *hopping = 0; |
| |
| memcpy(fu_config, fu_config_template, sizeof(fu_config_template)); |
| |
| /* set ID */ |
| |
| fu_config[6 + 2] = id; |
| fu_config[66 + 2] = id; |
| fu_config[86 + 2] = id; |
| fu_config[100 + 2] = id; |
| fu_config[113 + 2] = id; |
| fu_config[127 + 2] = id; |
| |
| /* set ARFCN */ |
| |
| uint16_t arfcn = trx->arfcn; |
| |
| fu_config[119] = arfcn >> 8; |
| fu_config[119 + 1] = arfcn & 0xFF; |
| |
| for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { |
| struct gsm_bts_trx_ts *ts = &trx->ts[i]; |
| |
| if (ts->hopping.enabled) { |
| /* reverse order */ |
| int j; |
| for (j = 0; j < ts->hopping.ma_len; j++) |
| fu_config[133 + (i * 10) + (7 - j)] = |
| ts->hopping.ma_data[j]; |
| fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn; |
| fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio; |
| *hopping = 1; |
| } else { |
| fu_config[133 + 8 + (i * 10)] = arfcn >> 8; |
| fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF; |
| } |
| } |
| |
| /* set BSIC */ |
| |
| /* |
| Attention: all TRX except the first one seem to get the TSC |
| from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION, |
| GSM 04.08 CHANNEL DESCRIPTION). |
| There was a bug in rsl_chan_activate_lchan() setting this parameter. |
| */ |
| |
| uint8_t bsic = trx->bts->bsic; |
| |
| fu_config[106] = bsic; |
| |
| /* set CA */ |
| |
| if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) { |
| fprintf(stderr, "generate_cell_chan_list failed\n"); |
| return 0; |
| } |
| |
| /* set channel configuration */ |
| |
| for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { |
| struct gsm_bts_trx_ts *ts = &trx->ts[i]; |
| uint8_t chan_config; |
| |
| /* |
| 0 = FCCH + SCH + BCCH + CCCH |
| 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4 |
| 2 = BCCH + CCCH (This combination is not used in any BTS) |
| 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH |
| 4 = SDCCH/8 + SACCH/8 |
| 5 = SDCCH/8 with SDCCH2 used as CBCH |
| 6 = TCH/F + FACCH/F + SACCH/F |
| 7 = E-RACH (Talk family) |
| 9 = Dual rate (capability for TCH/F and TCH/H) |
| 10 = reserved for BTS internal use |
| 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2). |
| 0xFF = spare TS |
| */ |
| |
| if (ts->pchan == GSM_PCHAN_NONE) |
| chan_config = 0xFF; |
| else if (ts->pchan == GSM_PCHAN_CCCH) |
| chan_config = 0; |
| else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4) |
| chan_config = 1; |
| else if (ts->pchan == GSM_PCHAN_TCH_F) |
| chan_config = 6; /* 9 should work too */ |
| else if (ts->pchan == GSM_PCHAN_TCH_H) |
| chan_config = 9; |
| else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C) |
| chan_config = 4; |
| else if (ts->pchan == GSM_PCHAN_PDCH) |
| chan_config = 11; |
| else { |
| fprintf(stderr, |
| "unsupported channel config %d for timeslot %d\n", |
| ts->pchan, i); |
| return 0; |
| } |
| |
| fu_config[72 + i] = chan_config; |
| } |
| return sizeof(fu_config_template); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static uint8_t bts_config_1[] = { |
| 0x4E, 0x02, |
| /* ID = 0x0E (Frame number) */ |
| /* length = 2 */ |
| /* [2] */ |
| 0xFF, 0xFF, |
| |
| 0x5F, 0x4E, 0x02, |
| /* ID = 0x4E (RX antenna supervision period) */ |
| /* length = 2 */ |
| /* [7] */ |
| 0xFF, 0xFF, |
| |
| 0x5F, 0x50, 0x02, |
| /* ID = 0x50 (Sector configuration) */ |
| /* length = 2 */ |
| /* [12] */ |
| 0x01, 0x01, |
| }; |
| |
| static uint8_t bts_config_2[] = { |
| 0x55, 0x02, |
| /* ID = 0x15 (Hopping mode) */ |
| /* length = 2 */ |
| /* [2] */ |
| 0x01, 0x00, |
| |
| 0x5F, 0x75, 0x02, |
| /* ID = 0x75 (RX diversity selection) */ |
| /* length = 2 */ |
| /* [7] */ |
| 0x01, 0x01, |
| }; |
| |
| static uint8_t bts_config_3[] = { |
| 0x5F, 0x20, 0x02, |
| /* ID = 0x20 (Extended cell radius) */ |
| /* length = 2 */ |
| /* [3] */ |
| 0x01, 0x00, |
| }; |
| |
| static uint8_t bts_config_4[] = { |
| 0x5F, 0x74, 0x09, |
| /* ID = 0x74 (Real Time) */ |
| /* length = 9 */ |
| /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ |
| 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, |
| 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [15] */ |
| 0x01, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [21] */ |
| 0x02, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [27] */ |
| 0x03, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [33] */ |
| 0x04, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [39] */ |
| 0x05, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [45] */ |
| 0x06, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [51] */ |
| 0x07, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [57] */ |
| 0x08, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [63] */ |
| 0x09, 0x01, 0x00, |
| |
| 0x5F, 0x76, 0x03, |
| /* ID = 0x76 (EAC input config) */ |
| /* length = 3 */ |
| /* [69] */ |
| 0x0A, 0x01, 0x00, |
| }; |
| |
| static uint8_t bts_config_insite[] = { |
| 0x4E, 0x02, |
| /* ID = 0x0E (Frame number) */ |
| /* length = 2 */ |
| /* [2] */ |
| 0xFF, 0xFF, |
| |
| 0x5F, 0x4E, 0x02, |
| /* ID = 0x4E (RX antenna supervision period) */ |
| /* length = 2 */ |
| /* [7] */ |
| 0xFF, 0xFF, |
| |
| 0x5F, 0x50, 0x02, |
| /* ID = 0x50 (Sector configuration) */ |
| /* length = 2 */ |
| /* [12] */ |
| 0x01, 0x01, |
| |
| 0x55, 0x02, |
| /* ID = 0x15 (Hopping mode) */ |
| /* length = 2 */ |
| /* [16] */ |
| 0x01, 0x00, |
| |
| 0x5F, 0x20, 0x02, |
| /* ID = 0x20 (Extended cell radius) */ |
| /* length = 2 */ |
| /* [21] */ |
| 0x01, 0x00, |
| |
| 0x5F, 0x74, 0x09, |
| /* ID = 0x74 (Real Time) */ |
| /* length = 9 */ |
| /* [26] */ |
| 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, |
| 0x00, |
| }; |
| |
| void set_real_time(uint8_t * real_time) |
| { |
| time_t t; |
| struct tm *tm; |
| |
| t = time(NULL); |
| tm = localtime(&t); |
| |
| /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ |
| |
| real_time[0] = (1900 + tm->tm_year) >> 8; |
| real_time[1] = (1900 + tm->tm_year) & 0xFF; |
| real_time[2] = tm->tm_mon + 1; |
| real_time[3] = tm->tm_mday; |
| real_time[4] = tm->tm_hour; |
| real_time[5] = tm->tm_min; |
| real_time[6] = tm->tm_sec; |
| real_time[7] = 0; |
| real_time[8] = 0; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* |
| build the configuration data |
| */ |
| |
| static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config, |
| int need_hopping) |
| { |
| /* is it an InSite BTS ? */ |
| if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */ |
| if (n_trx != 1) { |
| fprintf(stderr, "InSite has only one TRX\n"); |
| return 0; |
| } |
| if (need_hopping != 0) { |
| fprintf(stderr, "InSite does not support hopping\n"); |
| return 0; |
| } |
| memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite)); |
| set_real_time(&fu_config[26]); |
| return sizeof(bts_config_insite); |
| } |
| |
| int len = 0; |
| int i; |
| |
| memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1)); |
| |
| /* set sector configuration */ |
| fu_config[len + 12 - 1] = 1 + n_trx; /* len */ |
| for (i = 0; i < n_trx; i++) |
| fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF); |
| |
| len += (sizeof(bts_config_1) + (n_trx - 1)); |
| |
| memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2)); |
| /* set hopping mode (Baseband and RF hopping work for the MetroSite) */ |
| if (need_hopping) |
| fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */ |
| len += sizeof(bts_config_2); |
| |
| /* set extended cell radius for each TRX */ |
| for (i = 0; i < n_trx; i++) { |
| memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3)); |
| fu_config[len + 3] = ((i + 1) & 0xFF); |
| len += sizeof(bts_config_3); |
| } |
| |
| memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4)); |
| set_real_time(&fu_config[len + 3]); |
| len += sizeof(bts_config_4); |
| |
| return len; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static struct msgb *nm_msgb_alloc(void) |
| { |
| return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| struct abis_om_nokia_hdr { |
| uint8_t msg_type; |
| uint8_t spare; |
| uint16_t reference; |
| uint8_t data[0]; |
| } __attribute__ ((packed)); |
| |
| #define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr)) |
| |
| static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, |
| uint8_t * data, int len_data) |
| { |
| struct abis_om_hdr *oh; |
| struct abis_om_nokia_hdr *noh; |
| struct msgb *msg = nm_msgb_alloc(); |
| |
| oh = (struct abis_om_hdr *)msgb_put(msg, |
| ABIS_OM_NOKIA_HDR_SIZE + len_data); |
| |
| oh->mdisc = ABIS_OM_MDISC_FOM; |
| oh->placement = ABIS_OM_PLACEMENT_ONLY; |
| oh->sequence = 0; |
| oh->length = sizeof(struct abis_om_nokia_hdr) + len_data; |
| |
| noh = (struct abis_om_nokia_hdr *)oh->data; |
| |
| noh->msg_type = msg_type; |
| noh->spare = 0; |
| noh->reference = htons(ref); |
| memcpy(noh->data, data, len_data); |
| |
| DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type)); |
| |
| return abis_nm_sendmsg(bts, msg); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static uint8_t download_req[] = { |
| 0x5F, 0x25, 0x0B, |
| /* ID = 0x25 (File identity) */ |
| /* length = 11 */ |
| /* [3] */ |
| 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, |
| 0x2A, 0x2A, 0x2A, |
| |
| 0x5F, 0x78, 0x03, |
| /* ID = 0x78 (File version) */ |
| /* length = 3 */ |
| /* [17] */ |
| 0x2A, 0x2A, 0x2A, |
| |
| 0x5F, 0x81, 0x0A, 0x01, |
| /* ID = 0x8A (SW load mode) */ |
| /* length = 1 */ |
| /* [24] */ |
| 0x01, |
| |
| 0x5F, 0x81, 0x06, 0x01, |
| /* ID = 0x86 (Acknowledgement period) */ |
| /* length = 1 */ |
| /* [29] */ |
| 0x01, |
| }; |
| |
| static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref) |
| { |
| uint8_t *data = download_req; |
| int len_data = sizeof(download_req); |
| |
| return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, |
| len_data); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static uint8_t ack[] = { |
| 0x5F, 0x23, 0x01, |
| /* ID = 0x23 (Ack-Nack) */ |
| /* length = 1 */ |
| /* [3] */ |
| 0x01, |
| }; |
| |
| static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref) |
| { |
| uint8_t *data = ack; |
| int len_data = sizeof(ack); |
| |
| return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static uint8_t reset[] = { |
| 0x5F, 0x40, 0x04, |
| /* ID = 0x40 (Object identity) */ |
| /* length = 4 */ |
| /* [3] */ |
| 0x00, 0x01, 0xFF, 0xFF, |
| }; |
| |
| static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref) |
| { |
| uint8_t *data = reset; |
| int len_data = sizeof(reset); |
| |
| return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, |
| uint16_t ref, uint8_t * data, int len) |
| { |
| int len_remain, len_to_send, max_send; |
| int seq = 0; |
| int ret; |
| |
| len_remain = len; |
| |
| while (len_remain) { |
| struct abis_om_hdr *oh; |
| struct abis_om_nokia_hdr *noh; |
| struct msgb *msg = nm_msgb_alloc(); |
| |
| if (seq == 0) |
| max_send = 256 - sizeof(struct abis_om_nokia_hdr); |
| else |
| max_send = 256; |
| |
| if (len_remain > max_send) { |
| len_to_send = max_send; |
| |
| if (seq == 0) { |
| /* first segment */ |
| oh = (struct abis_om_hdr *)msgb_put(msg, |
| ABIS_OM_NOKIA_HDR_SIZE |
| + |
| len_to_send); |
| |
| oh->mdisc = ABIS_OM_MDISC_FOM; |
| oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ |
| oh->sequence = seq; |
| oh->length = 0; /* 256 bytes */ |
| |
| noh = (struct abis_om_nokia_hdr *)oh->data; |
| |
| noh->msg_type = msg_type; |
| noh->spare = 0; |
| noh->reference = htons(ref); |
| memcpy(noh->data, data, len_to_send); |
| } else { |
| /* segment in between */ |
| oh = (struct abis_om_hdr *)msgb_put(msg, |
| sizeof |
| (struct |
| abis_om_hdr) |
| + |
| len_to_send); |
| |
| oh->mdisc = ABIS_OM_MDISC_FOM; |
| oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ |
| oh->sequence = seq; |
| oh->length = 0; /* 256 bytes */ |
| |
| memcpy(oh->data, data, len_to_send); |
| } |
| } else { |
| |
| len_to_send = len_remain; |
| |
| /* check if message fits in a single segment */ |
| |
| if (seq == 0) |
| return abis_nm_send(bts, msg_type, ref, data, |
| len_to_send); |
| |
| /* last segment */ |
| |
| oh = (struct abis_om_hdr *)msgb_put(msg, |
| sizeof(struct |
| abis_om_hdr) |
| + len_to_send); |
| |
| oh->mdisc = ABIS_OM_MDISC_FOM; |
| oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ |
| oh->sequence = seq; |
| oh->length = len_to_send; |
| |
| memcpy(oh->data, data, len_to_send); |
| } |
| |
| DEBUGPC(DNM, "Sending multi-segment %d\n", seq); |
| |
| ret = abis_nm_sendmsg(bts, msg); |
| if (ret < 0) |
| return ret; |
| |
| nokia_abis_nm_queue_send_next(bts); |
| |
| /* next segment */ |
| len_remain -= len_to_send; |
| data += len_to_send; |
| seq++; |
| } |
| return ret; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) |
| { |
| struct gsm_bts_trx *trx; |
| uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ |
| int len = 0; |
| int idx = 0; |
| int ret; |
| int hopping = 0; |
| int need_hopping = 0; |
| |
| memset(config, 0, sizeof(config)); |
| |
| llist_for_each_entry(trx, &bts->trx_list, list) { |
| #if 0 /* debugging */ |
| printf("TRX\n"); |
| printf(" arfcn: %d\n", trx->arfcn); |
| printf(" bsic: %d\n", trx->bts->bsic); |
| uint8_t ca[20]; |
| memset(ca, 0xFF, sizeof(ca)); |
| ret = generate_cell_chan_list(ca, trx->bts); |
| printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); |
| int i; |
| for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { |
| struct gsm_bts_trx_ts *ts = &trx->ts[i]; |
| |
| printf(" pchan %d: %d\n", i, ts->pchan); |
| } |
| #endif |
| ret = make_fu_config(trx, idx + 1, config + len, &hopping); |
| need_hopping |= hopping; |
| len += ret; |
| |
| idx++; |
| } |
| |
| ret = make_bts_config(bts_type, idx, config + len, need_hopping); |
| len += ret; |
| |
| #if 0 /* debugging */ |
| dump_elements(config, len); |
| #endif |
| |
| return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, |
| len); |
| } |
| |
| #define GET_NEXT_BYTE if(idx >= len) return 0; \ |
| ub = data[idx++]; |
| |
| static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, |
| int max_value) |
| { |
| uint8_t ub; |
| int idx = 0; |
| int found = 0; |
| int constructed __attribute__((unused)); |
| uint16_t id_value; |
| |
| for (;;) { |
| |
| GET_NEXT_BYTE; |
| |
| /* encoding bit, construced means that other elements are contained */ |
| constructed = ((ub & 0x20) ? 1 : 0); |
| |
| if ((ub & 0x1F) == 0x1F) { |
| /* fixed pattern, ID follows */ |
| GET_NEXT_BYTE; /* ID */ |
| id_value = ub & 0x7F; |
| if (ub & 0x80) { |
| /* extension bit */ |
| GET_NEXT_BYTE; /* ID low part */ |
| id_value = (id_value << 7) | (ub & 0x7F); |
| } |
| if (id_value == id) |
| found = 1; |
| } else { |
| id_value = (ub & 0x3F); |
| if (id_value == id) |
| found = 1; |
| } |
| |
| GET_NEXT_BYTE; /* length */ |
| |
| if (found) { |
| /* get data */ |
| uint8_t n = ub; |
| uint8_t i; |
| for (i = 0; i < n; i++) { |
| GET_NEXT_BYTE; |
| if (max_value <= 0) |
| return -1; /* buffer too small */ |
| *value = ub; |
| value++; |
| max_value--; |
| } |
| return n; /* length */ |
| } else { |
| /* skip data */ |
| uint8_t n = ub; |
| uint8_t i; |
| for (i = 0; i < n; i++) { |
| GET_NEXT_BYTE; |
| } |
| } |
| } |
| return 0; /* not found */ |
| } |
| |
| static int dump_elements(uint8_t * data, int len) |
| { |
| uint8_t ub; |
| int idx = 0; |
| int constructed; |
| uint16_t id_value; |
| static char indent[100] = ""; /* TODO: move static to BTS context */ |
| |
| for (;;) { |
| |
| GET_NEXT_BYTE; |
| |
| /* encoding bit, construced means that other elements are contained */ |
| constructed = ((ub & 0x20) ? 1 : 0); |
| |
| if ((ub & 0x1F) == 0x1F) { |
| /* fixed pattern, ID follows */ |
| GET_NEXT_BYTE; /* ID */ |
| id_value = ub & 0x7F; |
| if (ub & 0x80) { |
| /* extension bit */ |
| GET_NEXT_BYTE; /* ID low part */ |
| id_value = (id_value << 7) | (ub & 0x7F); |
| } |
| |
| } else { |
| id_value = (ub & 0x3F); |
| } |
| |
| GET_NEXT_BYTE; /* length */ |
| |
| printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, |
| get_element_name_string(id_value), |
| constructed ? "** constructed **" : ""); |
| printf("%s length = %d\n", indent, ub); |
| printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); |
| |
| if (constructed) { |
| int indent_len = strlen(indent); |
| strcat(indent, " "); |
| |
| dump_elements(data + idx, ub); |
| |
| indent[indent_len] = 0; |
| } |
| /* skip data */ |
| uint8_t n = ub; |
| uint8_t i; |
| for (i = 0; i < n; i++) { |
| GET_NEXT_BYTE; |
| } |
| } |
| return 0; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* taken from abis_nm.c */ |
| |
| static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) |
| { |
| int wait = 0; |
| struct msgb *msg; |
| /* the queue is empty */ |
| while (!llist_empty(&bts->abis_queue)) { |
| msg = msgb_dequeue(&bts->abis_queue); |
| wait = OBSC_NM_W_ACK_CB(msg); |
| abis_sendmsg(msg); |
| |
| if (wait) |
| break; |
| } |
| |
| bts->abis_nm_pend = wait; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* timer for restarting OML after BTS reset */ |
| |
| static void reset_timer_cb(void *_bts) |
| { |
| struct gsm_bts *bts = _bts; |
| struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; |
| struct e1inp_line *line; |
| |
| bts->nokia.wait_reset = 0; |
| |
| /* OML link */ |
| line = e1inp_line_find(e1_link->e1_nr); |
| if (!line) { |
| LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " |
| "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); |
| return; |
| } |
| |
| start_sabm_in_line(line, 0, -1); /* stop all first */ |
| start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| /* |
| This is how the configuration is done: |
| - start OML link |
| - reset BTS |
| - receive ACK, wait some time and restart OML link |
| - receive OMU STARTED message, send START DOWNLOAD REQ |
| - receive CNF REQ message, send CONF DATA |
| - receive ACK, start RSL link(s) |
| ACK some other messages received from the BTS. |
| |
| Probably its also possible to configure the BTS without a reset, this |
| has not been tested yet. |
| */ |
| |
| static int abis_nm_rcvmsg_fom(struct msgb *mb) |
| { |
| struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; |
| struct gsm_bts *bts = sign_link->trx->bts; |
| struct abis_om_hdr *oh = msgb_l2(mb); |
| struct abis_om_nokia_hdr *noh = msgb_l3(mb); |
| uint8_t mt = noh->msg_type; |
| int ret = 0; |
| uint16_t ref = ntohs(noh->reference); |
| uint8_t info[256]; |
| uint8_t ack = 0xFF; |
| uint8_t severity = 0xFF; |
| int str_len; |
| int len_data; |
| |
| if (bts->nokia.wait_reset) { |
| LOGP(DNM, LOGL_INFO, |
| "Ignore message while waiting for reset\n"); |
| return ret; |
| } |
| |
| if (oh->length < sizeof(struct abis_om_nokia_hdr)) { |
| LOGP(DNM, LOGL_ERROR, "Message too short\n"); |
| return -EINVAL; |
| } |
| |
| len_data = oh->length - sizeof(struct abis_om_nokia_hdr); |
| LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); |
| #if 0 /* debugging */ |
| dump_elements(noh->data, len_data); |
| #endif |
| |
| switch (mt) { |
| case NOKIA_MSG_OMU_STARTED: |
| if (find_element(noh->data, len_data, |
| NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, |
| sizeof(uint8_t)) == sizeof(uint8_t)) |
| LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", |
| bts->nokia.bts_type, |
| get_bts_type_string(bts->nokia.bts_type)); |
| else |
| LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); |
| /* send START_DOWNLOAD_REQ */ |
| abis_nm_download_req(bts, ref); |
| break; |
| case NOKIA_MSG_MF_REQ: |
| break; |
| case NOKIA_MSG_CONF_REQ: |
| /* send ACK */ |
| abis_nm_ack(bts, ref); |
| nokia_abis_nm_queue_send_next(bts); |
| /* send CONF_DATA */ |
| abis_nm_send_config(bts, bts->nokia.bts_type); |
| bts->nokia.configured = 1; |
| break; |
| case NOKIA_MSG_ACK: |
| if (find_element |
| (noh->data, len_data, NOKIA_EI_ACK, &ack, |
| sizeof(uint8_t)) == sizeof(uint8_t)) { |
| LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); |
| if (ack != 1) { |
| LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", |
| ack); |
| /* TODO: properly handle failures (NACK) */ |
| } |
| } else |
| LOGP(DNM, LOGL_ERROR, "ACK not found\n"); |
| |
| /* TODO: the assumption for the following is that no NACK was received */ |
| |
| /* ACK for reset message ? */ |
| if (!bts->nokia.did_reset) { |
| bts->nokia.did_reset = 1; |
| |
| /* |
| TODO: For the InSite processing the received data is |
| blocked in the driver during reset. |
| Otherwise the LAPD module might assert because the InSite |
| sends garbage on the E1 line during reset. |
| This is done by looking at "wait_reset" in the driver |
| (function handle_ts1_read()) and ignoring the received data. |
| It seems to be necessary for the MetroSite too. |
| */ |
| bts->nokia.wait_reset = 1; |
| |
| bts->nokia.reset_timer.cb = &reset_timer_cb; |
| bts->nokia.reset_timer.data = bts; |
| osmo_timer_schedule(&bts->nokia.reset_timer, RESET_INTERVAL); |
| |
| struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; |
| struct e1inp_line *line; |
| /* OML link */ |
| line = e1inp_line_find(e1_link->e1_nr); |
| if (!line) { |
| LOGP(DLINP, LOGL_ERROR, |
| "BTS %u OML link referring to " |
| "non-existing E1 line %u\n", bts->nr, |
| e1_link->e1_nr); |
| return -ENOMEM; |
| } |
| |
| start_sabm_in_line(line, 0, -1); /* stop all first */ |
| } |
| |
| /* ACK for CONF DATA message ? */ |
| if (bts->nokia.configured != 0) { |
| /* start TRX (RSL link) */ |
| |
| struct gsm_e1_subslot *e1_link = |
| &sign_link->trx->rsl_e1_link; |
| struct e1inp_line *line; |
| |
| bts->nokia.configured = 0; |
| |
| /* RSL Link */ |
| line = e1inp_line_find(e1_link->e1_nr); |
| if (!line) { |
| LOGP(DLINP, LOGL_ERROR, |
| "TRX (%u/%u) RSL link referring " |
| "to non-existing E1 line %u\n", |
| sign_link->trx->bts->nr, sign_link->trx->nr, |
| e1_link->e1_nr); |
| return -ENOMEM; |
| } |
| /* start TRX */ |
| start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */ |
| } |
| break; |
| case NOKIA_MSG_STATE_CHANGED: |
| /* send ACK */ |
| abis_nm_ack(bts, ref); |
| break; |
| case NOKIA_MSG_CONF_COMPLETE: |
| /* send ACK */ |
| abis_nm_ack(bts, ref); |
| break; |
| case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */ |
| /* send ACK (do we have to send an ACK ?) */ |
| abis_nm_ack(bts, ref); |
| break; |
| case NOKIA_MSG_ALARM: |
| find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, |
| sizeof(severity)); |
| /* TODO: there might be alarms with both elements set */ |
| str_len = |
| find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, |
| sizeof(info)); |
| if (str_len > 0) { |
| info[str_len] = 0; |
| LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", |
| get_severity_string(severity), severity, info); |
| } else { /* nothing found, try details */ |
| str_len = |
| find_element(noh->data, len_data, |
| NOKIA_EI_ALARM_DETAIL, info, |
| sizeof(info)); |
| if (str_len > 0) { |
| uint16_t code; |
| info[str_len] = 0; |
| code = (info[0] << 8) + info[1]; |
| LOGP(DNM, LOGL_INFO, |
| "ALARM Severity %s (%d), code 0x%X : %s\n", |
| get_severity_string(severity), severity, |
| code, info + 2); |
| } |
| } |
| /* send ACK */ |
| abis_nm_ack(bts, ref); |
| break; |
| } |
| |
| nokia_abis_nm_queue_send_next(bts); |
| |
| return ret; |
| } |
| |
| /* TODO: put in a separate file ? */ |
| |
| int abis_nokia_rcvmsg(struct msgb *msg) |
| { |
| struct abis_om_hdr *oh = msgb_l2(msg); |
| int rc = 0; |
| |
| /* Various consistency checks */ |
| if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { |
| LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", |
| oh->placement); |
| if (oh->placement != ABIS_OM_PLACEMENT_FIRST) |
| return -EINVAL; |
| } |
| if (oh->sequence != 0) { |
| LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", |
| oh->sequence); |
| return -EINVAL; |
| } |
| msg->l3h = (unsigned char *)oh + sizeof(*oh); |
| |
| switch (oh->mdisc) { |
| case ABIS_OM_MDISC_FOM: |
| LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n"); |
| rc = abis_nm_rcvmsg_fom(msg); |
| break; |
| case ABIS_OM_MDISC_MANUF: |
| LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n"); |
| break; |
| case ABIS_OM_MDISC_MMI: |
| case ABIS_OM_MDISC_TRAU: |
| LOGP(DNM, LOGL_ERROR, |
| "unimplemented ABIS OML message discriminator 0x%x\n", |
| oh->mdisc); |
| break; |
| default: |
| LOGP(DNM, LOGL_ERROR, |
| "unknown ABIS OML message discriminator 0x%x\n", |
| oh->mdisc); |
| return -EINVAL; |
| } |
| |
| msgb_free(msg); |
| return rc; |
| } |
| |
| static int bts_model_nokia_site_start(struct gsm_network *net); |
| |
| static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line) |
| { |
| e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); |
| } |
| |
| static struct gsm_bts_model model_nokia_site = { |
| .type = GSM_BTS_TYPE_NOKIA_SITE, |
| .name = "nokia_site", |
| .start = bts_model_nokia_site_start, |
| .oml_rcvmsg = &abis_nokia_rcvmsg, |
| .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops, |
| }; |
| |
| static struct gsm_network *my_net; |
| |
| static int bts_model_nokia_site_start(struct gsm_network *net) |
| { |
| model_nokia_site.features.data = &model_nokia_site._features_data[0]; |
| model_nokia_site.features.data_len = |
| sizeof(model_nokia_site._features_data); |
| |
| gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING); |
| gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD); |
| |
| osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); |
| osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); |
| osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); |
| |
| my_net = net; |
| |
| return 0; |
| } |
| |
| int bts_model_nokia_site_init(void) |
| { |
| return gsm_bts_model_register(&model_nokia_site); |
| } |