| /* Siemens BS-11 microBTS configuration tool */ |
| |
| /* (C) 2009 by Harald Welte <laforge@gnumonks.org> |
| * All Rights Reserved |
| * |
| * This software is based on ideas (but not code) of BS11Config |
| * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 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 General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <getopt.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <openbsc/gsm_data.h> |
| #include <openbsc/abis_nm.h> |
| #include <openbsc/msgb.h> |
| #include <openbsc/tlv.h> |
| #include <openbsc/debug.h> |
| #include <openbsc/select.h> |
| #include <openbsc/rs232.h> |
| |
| /* state of our bs11_config application */ |
| enum bs11cfg_state { |
| STATE_NONE, |
| STATE_LOGON_WAIT, |
| STATE_LOGON_ACK, |
| STATE_SWLOAD, |
| STATE_QUERY, |
| }; |
| static enum bs11cfg_state bs11cfg_state = STATE_NONE; |
| static char *command; |
| struct timer_list status_timer; |
| |
| static const u_int8_t obj_li_attr[] = { |
| NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, |
| NM_ATT_BS11_L1_PROT_TYPE, 0x00, |
| NM_ATT_BS11_LINE_CFG, 0x00, |
| }; |
| static const u_int8_t obj_bbsig0_attr[] = { |
| NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, |
| NM_ATT_BS11_DIVERSITY, 0x01, 0x00, |
| }; |
| static const u_int8_t obj_pa0_attr[] = { |
| NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, |
| }; |
| static const char *trx1_password = "1111111111"; |
| #define TEI_OML 25 |
| |
| static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; |
| |
| static struct debug_target *stderr_target; |
| |
| /* dummy function to keep gsm_data.c happy */ |
| struct counter *counter_alloc(const char *name) |
| { |
| return NULL; |
| } |
| |
| int handle_serial_msg(struct msgb *rx_msg); |
| |
| /* create all objects for an initial configuration */ |
| static int create_objects(struct gsm_bts *bts) |
| { |
| fprintf(stdout, "Crating Objects for minimal config\n"); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), |
| obj_li_attr); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, |
| sizeof(obj_bbsig0_attr), obj_bbsig0_attr); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, |
| sizeof(obj_pa0_attr), obj_pa0_attr); |
| abis_nm_bs11_create_envaBTSE(bts, 0); |
| abis_nm_bs11_create_envaBTSE(bts, 1); |
| abis_nm_bs11_create_envaBTSE(bts, 2); |
| abis_nm_bs11_create_envaBTSE(bts, 3); |
| |
| abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); |
| |
| abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); |
| |
| sleep(1); |
| |
| abis_nm_bs11_set_trx1_pw(bts, trx1_password); |
| |
| sleep(1); |
| |
| return 0; |
| } |
| |
| static int create_trx1(struct gsm_bts *bts) |
| { |
| u_int8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; |
| u_int8_t *cur = bbsig1_attr; |
| struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); |
| |
| if (!trx) |
| trx = gsm_bts_trx_alloc(bts); |
| |
| fprintf(stdout, "Crating Objects for TRX1\n"); |
| |
| abis_nm_bs11_set_trx1_pw(bts, trx1_password); |
| |
| sleep(1); |
| |
| cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, |
| (u_int8_t *)trx1_password); |
| memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, |
| sizeof(bbsig1_attr), bbsig1_attr); |
| abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, |
| sizeof(obj_pa0_attr), obj_pa0_attr); |
| abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); |
| |
| return 0; |
| } |
| |
| static char *serial_port = "/dev/ttyUSB0"; |
| static char *fname_safety = "BTSBMC76.SWI"; |
| static char *fname_software = "HS011106.SWL"; |
| static int delay_ms = 0; |
| static int win_size = 8; |
| static int param_disconnect = 0; |
| static int param_restart = 0; |
| static int param_forced = 0; |
| static struct gsm_bts *g_bts; |
| |
| static int file_is_readable(const char *fname) |
| { |
| int rc; |
| struct stat st; |
| |
| rc = stat(fname, &st); |
| if (rc < 0) |
| return 0; |
| |
| if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int percent; |
| static int percent_old; |
| |
| /* callback function passed to the ABIS OML code */ |
| static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, |
| void *data, void *param) |
| { |
| if (hook != GSM_HOOK_NM_SWLOAD) |
| return 0; |
| |
| switch (event) { |
| case NM_MT_LOAD_INIT_ACK: |
| fprintf(stdout, "Software Load Initiate ACK\n"); |
| break; |
| case NM_MT_LOAD_INIT_NACK: |
| fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); |
| exit(5); |
| break; |
| case NM_MT_LOAD_END_ACK: |
| if (data) { |
| /* we did a safety load and must activate it */ |
| abis_nm_software_activate(g_bts, fname_safety, |
| swload_cbfn, g_bts); |
| sleep(5); |
| } |
| break; |
| case NM_MT_LOAD_END_NACK: |
| fprintf(stderr, "ERROR: Software Load End NACK\n"); |
| exit(3); |
| break; |
| case NM_MT_ACTIVATE_SW_NACK: |
| fprintf(stderr, "ERROR: Activate Software NACK\n"); |
| exit(4); |
| break; |
| case NM_MT_ACTIVATE_SW_ACK: |
| bs11cfg_state = STATE_NONE; |
| |
| break; |
| case NM_MT_LOAD_SEG_ACK: |
| percent = abis_nm_software_load_status(g_bts); |
| if (percent > percent_old) |
| printf("Software Download Progress: %d%%\n", percent); |
| percent_old = percent; |
| break; |
| } |
| return 0; |
| } |
| |
| static const char *bs11_link_state[] = { |
| [0x00] = "Down", |
| [0x01] = "Up", |
| [0x02] = "Restoring", |
| }; |
| |
| static const char *linkstate_name(u_int8_t linkstate) |
| { |
| if (linkstate > ARRAY_SIZE(bs11_link_state)) |
| return "Unknown"; |
| |
| return bs11_link_state[linkstate]; |
| } |
| |
| static const char *mbccu_load[] = { |
| [0] = "No Load", |
| [1] = "Load BTSCAC", |
| [2] = "Load BTSDRX", |
| [3] = "Load BTSBBX", |
| [4] = "Load BTSARC", |
| [5] = "Load", |
| }; |
| |
| static const char *mbccu_load_name(u_int8_t linkstate) |
| { |
| if (linkstate > ARRAY_SIZE(mbccu_load)) |
| return "Unknown"; |
| |
| return mbccu_load[linkstate]; |
| } |
| |
| static const char *bts_phase_name(u_int8_t phase) |
| { |
| switch (phase) { |
| case BS11_STATE_WARM_UP: |
| case BS11_STATE_WARM_UP_2: |
| return "Warm Up"; |
| break; |
| case BS11_STATE_LOAD_SMU_SAFETY: |
| return "Load SMU Safety"; |
| break; |
| case BS11_STATE_LOAD_SMU_INTENDED: |
| return "Load SMU Intended"; |
| break; |
| case BS11_STATE_LOAD_MBCCU: |
| return "Load MBCCU"; |
| break; |
| case BS11_STATE_SOFTWARE_RQD: |
| return "Software required"; |
| break; |
| case BS11_STATE_WAIT_MIN_CFG: |
| case BS11_STATE_WAIT_MIN_CFG_2: |
| return "Wait minimal config"; |
| break; |
| case BS11_STATE_MAINTENANCE: |
| return "Maintenance"; |
| break; |
| case BS11_STATE_NORMAL: |
| return "Normal"; |
| break; |
| case BS11_STATE_ABIS_LOAD: |
| return "Abis load"; |
| break; |
| default: |
| return "Unknown"; |
| break; |
| } |
| } |
| |
| static const char *trx_power_name(u_int8_t pwr) |
| { |
| switch (pwr) { |
| case BS11_TRX_POWER_GSM_2W: |
| return "2W (GSM)"; |
| case BS11_TRX_POWER_GSM_250mW: |
| return "250mW (GSM)"; |
| case BS11_TRX_POWER_GSM_80mW: |
| return "80mW (GSM)"; |
| case BS11_TRX_POWER_GSM_30mW: |
| return "30mW (GSM)"; |
| case BS11_TRX_POWER_DCS_3W: |
| return "3W (DCS)"; |
| case BS11_TRX_POWER_DCS_1W6: |
| return "1.6W (DCS)"; |
| case BS11_TRX_POWER_DCS_500mW: |
| return "500mW (DCS)"; |
| case BS11_TRX_POWER_DCS_160mW: |
| return "160mW (DCS)"; |
| default: |
| return "unknown value"; |
| } |
| } |
| |
| static const char *pll_mode_name(u_int8_t mode) |
| { |
| switch (mode) { |
| case BS11_LI_PLL_LOCKED: |
| return "E1 Locked"; |
| case BS11_LI_PLL_STANDALONE: |
| return "Standalone"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| static const char *cclk_acc_name(u_int8_t acc) |
| { |
| switch (acc) { |
| case 0: |
| /* Out of the demanded +/- 0.05ppm */ |
| return "Medium"; |
| case 1: |
| /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ |
| return "High"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| static const char *obj_name(struct abis_om_fom_hdr *foh) |
| { |
| static char retbuf[256]; |
| |
| retbuf[0] = 0; |
| |
| switch (foh->obj_class) { |
| case NM_OC_BS11: |
| strcat(retbuf, "BS11 "); |
| switch (foh->obj_inst.bts_nr) { |
| case BS11_OBJ_PA: |
| sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", |
| foh->obj_inst.ts_nr); |
| break; |
| case BS11_OBJ_LI: |
| sprintf(retbuf+strlen(retbuf), "Line Interface "); |
| break; |
| case BS11_OBJ_CCLK: |
| sprintf(retbuf+strlen(retbuf), "CCLK "); |
| break; |
| } |
| break; |
| case NM_OC_SITE_MANAGER: |
| strcat(retbuf, "SITE MANAGER "); |
| break; |
| } |
| return retbuf; |
| } |
| |
| static void print_state(struct tlv_parsed *tp) |
| { |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { |
| u_int8_t phase, mbccu; |
| if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { |
| phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); |
| printf("PHASE: %u %-20s ", phase & 0xf, |
| bts_phase_name(phase)); |
| } |
| if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { |
| mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); |
| printf("MBCCU0: %-11s MBCCU1: %-11s ", |
| mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); |
| } |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && |
| TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { |
| u_int8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); |
| printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); |
| } |
| printf("\n"); |
| } |
| |
| static int print_attr(struct tlv_parsed *tp) |
| { |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { |
| printf("\tBS-11 ESN PCB Serial Number: %s\n", |
| TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { |
| printf("\tBS-11 ESN Hardware Code Number: %s\n", |
| TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { |
| printf("\tBS-11 ESN Firmware Code Number: %s\n", |
| TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); |
| } |
| #if 0 |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { |
| printf("BS-11 Boot Software Version: %s\n", |
| TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); |
| } |
| #endif |
| if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && |
| TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { |
| const u_int8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); |
| printf("\tE1 Channel: Port=%u Timeslot=%u ", |
| chan[0], chan[1]); |
| if (chan[2] == 0xff) |
| printf("(Full Slot)\n"); |
| else |
| printf("Subslot=%u\n", chan[2]); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_TEI)) |
| printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && |
| TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { |
| printf("\tTRX Power: %s\n", |
| trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && |
| TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { |
| printf("\tPLL Mode: %s\n", |
| pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && |
| TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { |
| const u_int8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); |
| printf("\tPLL Set Value=%d, Work Value=%d\n", |
| vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && |
| TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { |
| const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); |
| printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); |
| } |
| if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && |
| TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { |
| const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); |
| printf("\tCCLK Type=%d\n", *acc); |
| } |
| |
| |
| return 0; |
| } |
| |
| static void cmd_query(void) |
| { |
| struct gsm_bts_trx *trx = g_bts->c0; |
| |
| bs11cfg_state = STATE_QUERY; |
| abis_nm_bs11_get_serno(g_bts); |
| abis_nm_bs11_get_oml_tei_ts(g_bts); |
| abis_nm_bs11_get_pll_mode(g_bts); |
| abis_nm_bs11_get_cclk(g_bts); |
| abis_nm_bs11_get_trx_power(trx); |
| trx = gsm_bts_trx_num(g_bts, 1); |
| if (trx) |
| abis_nm_bs11_get_trx_power(trx); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } |
| |
| /* handle a response from the BTS to a GET STATE command */ |
| static int handle_state_resp(enum abis_bs11_phase state) |
| { |
| int rc = 0; |
| |
| switch (state) { |
| case BS11_STATE_WARM_UP: |
| case BS11_STATE_LOAD_SMU_SAFETY: |
| case BS11_STATE_LOAD_SMU_INTENDED: |
| case BS11_STATE_LOAD_MBCCU: |
| break; |
| case BS11_STATE_SOFTWARE_RQD: |
| bs11cfg_state = STATE_SWLOAD; |
| /* send safety load. Use g_bts as private 'param' |
| * argument, so our swload_cbfn can distinguish |
| * a safety load from a regular software */ |
| if (file_is_readable(fname_safety)) |
| rc = abis_nm_software_load(g_bts, fname_safety, |
| win_size, param_forced, |
| swload_cbfn, g_bts); |
| else |
| fprintf(stderr, "No valid Safety Load file \"%s\"\n", |
| fname_safety); |
| break; |
| case BS11_STATE_WAIT_MIN_CFG: |
| case BS11_STATE_WAIT_MIN_CFG_2: |
| bs11cfg_state = STATE_SWLOAD; |
| rc = create_objects(g_bts); |
| break; |
| case BS11_STATE_MAINTENANCE: |
| if (command) { |
| if (!strcmp(command, "disconnect")) |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| else if (!strcmp(command, "reconnect")) |
| rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); |
| else if (!strcmp(command, "software") |
| && bs11cfg_state != STATE_SWLOAD) { |
| bs11cfg_state = STATE_SWLOAD; |
| /* send software (FIXME: over A-bis?) */ |
| if (file_is_readable(fname_software)) |
| rc = abis_nm_bs11_load_swl(g_bts, fname_software, |
| win_size, param_forced, |
| swload_cbfn); |
| else |
| fprintf(stderr, "No valid Software file \"%s\"\n", |
| fname_software); |
| } else if (!strcmp(command, "delete-trx1")) { |
| printf("Locing BBSIG and PA objects of TRX1\n"); |
| abis_nm_chg_adm_state(g_bts, NM_OC_BS11, |
| BS11_OBJ_BBSIG, 0, 1, |
| NM_STATE_LOCKED); |
| abis_nm_chg_adm_state(g_bts, NM_OC_BS11, |
| BS11_OBJ_PA, 0, 1, |
| NM_STATE_LOCKED); |
| sleep(1); |
| printf("Deleting BBSIG and PA objects of TRX1\n"); |
| abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); |
| abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "create-trx1")) { |
| create_trx1(g_bts); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "pll-e1-locked")) { |
| abis_nm_bs11_set_pll_locked(g_bts, 1); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "pll-standalone")) { |
| abis_nm_bs11_set_pll_locked(g_bts, 0); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "oml-tei")) { |
| abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); |
| command = NULL; |
| } else if (!strcmp(command, "restart")) { |
| abis_nm_bs11_restart(g_bts); |
| command = NULL; |
| } else if (!strcmp(command, "query")) { |
| cmd_query(); |
| } else if (!strcmp(command, "create-bport1")) { |
| abis_nm_bs11_create_bport(g_bts, 1); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "delete-bport1")) { |
| abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); |
| sleep(1); |
| abis_nm_bs11_delete_bport(g_bts, 1); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "bport0-star")) { |
| abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } else if (!strcmp(command, "bport0-multidrop")) { |
| abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); |
| sleep(1); |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| command = NULL; |
| } |
| } |
| break; |
| case BS11_STATE_NORMAL: |
| if (command) { |
| if (!strcmp(command, "reconnect")) |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| else if (!strcmp(command, "disconnect")) |
| abis_nm_bs11_bsc_disconnect(g_bts, 0); |
| else if (!strcmp(command, "query")) { |
| cmd_query(); |
| } |
| } else if (param_disconnect) { |
| param_disconnect = 0; |
| abis_nm_bs11_bsc_disconnect(g_bts, 0); |
| if (param_restart) { |
| param_restart = 0; |
| abis_nm_bs11_restart(g_bts); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| return rc; |
| } |
| |
| /* handle a fully-received message/packet from the RS232 port */ |
| int handle_serial_msg(struct msgb *rx_msg) |
| { |
| struct abis_om_hdr *oh; |
| struct abis_om_fom_hdr *foh; |
| struct tlv_parsed tp; |
| int rc = -1; |
| |
| #if 0 |
| if (rx_msg->len < LAPD_HDR_LEN |
| + sizeof(struct abis_om_fom_hdr) |
| + sizeof(struct abis_om_hdr)) { |
| if (!memcmp(rx_msg->data + 2, too_fast, |
| sizeof(too_fast))) { |
| fprintf(stderr, "BS11 tells us we're too " |
| "fast, try --delay bigger than %u\n", |
| delay_ms); |
| return -E2BIG; |
| } else |
| fprintf(stderr, "unknown BS11 message\n"); |
| } |
| #endif |
| |
| oh = (struct abis_om_hdr *) msgb_l2(rx_msg); |
| foh = (struct abis_om_fom_hdr *) oh->data; |
| switch (foh->msg_type) { |
| case NM_MT_BS11_LMT_LOGON_ACK: |
| printf("LMT LOGON: ACK\n\n"); |
| if (bs11cfg_state == STATE_NONE) |
| bs11cfg_state = STATE_LOGON_ACK; |
| rc = abis_nm_bs11_get_state(g_bts); |
| break; |
| case NM_MT_BS11_LMT_LOGOFF_ACK: |
| printf("LMT LOGOFF: ACK\n"); |
| exit(0); |
| break; |
| case NM_MT_BS11_GET_STATE_ACK: |
| rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); |
| print_state(&tp); |
| if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && |
| TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) |
| rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); |
| break; |
| case NM_MT_GET_ATTR_RESP: |
| printf("\n%sATTRIBUTES:\n", obj_name(foh)); |
| abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh)); |
| rc = print_attr(&tp); |
| //hexdump(foh->data, oh->length-sizeof(*foh)); |
| break; |
| case NM_MT_BS11_SET_ATTR_ACK: |
| printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", |
| foh->obj_class, foh->obj_inst.bts_nr, |
| foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); |
| rc = 0; |
| break; |
| case NM_MT_BS11_SET_ATTR_NACK: |
| printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", |
| foh->obj_class, foh->obj_inst.bts_nr, |
| foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); |
| break; |
| default: |
| rc = abis_nm_rcvmsg(rx_msg); |
| } |
| if (rc < 0) { |
| perror("ERROR in main loop"); |
| //break; |
| } |
| if (rc == 1) |
| return rc; |
| |
| switch (bs11cfg_state) { |
| case STATE_NONE: |
| abis_nm_bs11_factory_logon(g_bts, 1); |
| break; |
| case STATE_LOGON_ACK: |
| bsc_schedule_timer(&status_timer, 5, 0); |
| break; |
| default: |
| break; |
| } |
| |
| return rc; |
| } |
| |
| int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj, |
| struct gsm_nm_state *old_state, struct gsm_nm_state *new_state) |
| { |
| return 0; |
| } |
| |
| void status_timer_cb(void *data) |
| { |
| abis_nm_bs11_get_state(g_bts); |
| } |
| |
| static void print_banner(void) |
| { |
| printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n"); |
| printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); |
| } |
| |
| static void print_help(void) |
| { |
| printf("bs11_config [options] [command]\n"); |
| printf("\nSupported options:\n"); |
| printf("\t-h --help\t\t\tPrint this help text\n"); |
| printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n"); |
| printf("\t-s --software <file>\t\tSpecify Software file\n"); |
| printf("\t-S --safety <file>\t\tSpecify Safety Load file\n"); |
| printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n"); |
| printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); |
| printf("\t-w --win-size <num>\t\tSpecify Window Size\n"); |
| printf("\t-f --forced\t\t\tForce Software Load\n"); |
| printf("\nSupported commands:\n"); |
| printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n"); |
| printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n"); |
| printf("\tresconnect\tReconnect A-bis link (go into normal state)\n"); |
| printf("\trestart\t\tRestart the BTS\n"); |
| printf("\tsoftware\tDownload Software (only in administrative state)\n"); |
| printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); |
| printf("\tdelete-trx1\tDelete objects for TRX1\n"); |
| printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n"); |
| printf("\tpll-standalone\tSet the PLL to be in standalone mode\n"); |
| printf("\toml-tei\tSet OML E1 TS and TEI\n"); |
| printf("\tbport0-star\tSet BPORT0 line config to star\n"); |
| printf("\tbport0-multiport\tSet BPORT0 line config to multiport\n"); |
| printf("\tcreate-bport1\tCreate BPORT1 object\n"); |
| printf("\tdelete-bport1\tDelete BPORT1 object\n"); |
| } |
| |
| static void handle_options(int argc, char **argv) |
| { |
| int option_index = 0; |
| print_banner(); |
| |
| while (1) { |
| int c; |
| static struct option long_options[] = { |
| { "help", 0, 0, 'h' }, |
| { "port", 1, 0, 'p' }, |
| { "software", 1, 0, 's' }, |
| { "safety", 1, 0, 'S' }, |
| { "delay", 1, 0, 'd' }, |
| { "disconnect", 0, 0, 'D' }, |
| { "win-size", 1, 0, 'w' }, |
| { "forced", 0, 0, 'f' }, |
| { "restart", 0, 0, 'r' }, |
| { "debug", 1, 0, 'b'}, |
| }; |
| |
| c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", |
| long_options, &option_index); |
| |
| if (c == -1) |
| break; |
| |
| switch (c) { |
| case 'h': |
| print_help(); |
| exit(0); |
| case 'p': |
| serial_port = optarg; |
| break; |
| case 'b': |
| debug_parse_category_mask(stderr_target, optarg); |
| break; |
| case 's': |
| fname_software = optarg; |
| break; |
| case 'S': |
| fname_safety = optarg; |
| break; |
| case 'd': |
| delay_ms = atoi(optarg); |
| break; |
| case 'w': |
| win_size = atoi(optarg); |
| break; |
| case 'D': |
| param_disconnect = 1; |
| break; |
| case 'f': |
| param_forced = 1; |
| break; |
| case 'r': |
| param_disconnect = 1; |
| param_restart = 1; |
| break; |
| default: |
| break; |
| } |
| } |
| if (optind < argc) |
| command = argv[optind]; |
| } |
| |
| static int num_sigint; |
| |
| static void signal_handler(int signal) |
| { |
| fprintf(stdout, "\nsignal %u received\n", signal); |
| |
| switch (signal) { |
| case SIGINT: |
| num_sigint++; |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| if (num_sigint >= 3) |
| exit(0); |
| break; |
| } |
| } |
| |
| int main(int argc, char **argv) |
| { |
| struct gsm_network *gsmnet; |
| int rc; |
| |
| debug_init(); |
| stderr_target = debug_target_create_stderr(); |
| debug_add_target(stderr_target); |
| debug_set_all_filter(stderr_target, 1); |
| handle_options(argc, argv); |
| |
| gsmnet = gsm_network_init(1, 1, NULL); |
| if (!gsmnet) { |
| fprintf(stderr, "Unable to allocate gsm network\n"); |
| exit(1); |
| } |
| g_bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_TSC, |
| HARDCODED_BSIC); |
| |
| rc = rs232_setup(serial_port, delay_ms, g_bts); |
| if (rc < 0) { |
| fprintf(stderr, "Problem setting up serial port\n"); |
| exit(1); |
| } |
| |
| signal(SIGINT, &signal_handler); |
| |
| abis_nm_bs11_factory_logon(g_bts, 1); |
| //abis_nm_bs11_get_serno(g_bts); |
| |
| status_timer.cb = status_timer_cb; |
| |
| while (1) { |
| bsc_select_main(0); |
| } |
| |
| abis_nm_bs11_factory_logon(g_bts, 0); |
| |
| exit(0); |
| } |