aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/utils
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
committerHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
commit31c00f7d6fa63937f2c973157d196a427f6eef95 (patch)
tree6b7c81d92b6a8b83d0588b2b59d47fd0cca7a052 /openbsc/src/utils
parent9349d7ff7c5866110a1f2421ccc68a487e4030be (diff)
re-structure the OpenBSC directory layout
The new structure divides the code into a number of libraries for the BSC core functionality, MSC core functionality, Abis transport, TRAU and other bits. This doesn't introduce any functional code change but simply moves around files and alters Makefile.am accordingly. Next step would be to disentangle a lot of the inter-library dependencies and make the individual bits of code more independent.
Diffstat (limited to 'openbsc/src/utils')
-rw-r--r--openbsc/src/utils/Makefile.am10
-rw-r--r--openbsc/src/utils/bs11_config.c918
-rw-r--r--openbsc/src/utils/isdnsync.c191
-rw-r--r--openbsc/src/utils/rs232.c248
4 files changed, 1367 insertions, 0 deletions
diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am
new file mode 100644
index 00000000..2847c6ad
--- /dev/null
+++ b/openbsc/src/utils/Makefile.am
@@ -0,0 +1,10 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
+
+bin_PROGRAMS = bs11_config isdnsync
+
+bs11_config_SOURCES = bs11_config.c rs232.c
+bs11_config_LDADD = ../common/libcommon.a ../abis/libabis.a ../bsc/libbsc.a
+
+isdnsync_SOURCES = isdnsync.c
diff --git a/openbsc/src/utils/bs11_config.c b/openbsc/src/utils/bs11_config.c
new file mode 100644
index 00000000..eaed8b75
--- /dev/null
+++ b/openbsc/src/utils/bs11_config.c
@@ -0,0 +1,918 @@
+/* Siemens BS-11 microBTS configuration tool */
+
+/* (C) 2009-2010 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 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 <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 <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <openbsc/debug.h>
+#include <osmocore/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, *value;
+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 log_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 *bport_lcfg_name(u_int8_t lcfg)
+{
+ switch (lcfg) {
+ case BS11_LINE_CFG_STAR:
+ return "Star";
+ case BS11_LINE_CFG_MULTIDROP:
+ return "Multi-Drop";
+ 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;
+ case NM_OC_BS11_BPORT:
+ sprintf(retbuf+strlen(retbuf), "BPORT%u ",
+ foh->obj_inst.bts_nr);
+ 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);
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) &&
+ TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) {
+ const u_int8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG);
+ printf("\tLine Configuration: %s (%d)\n",
+ bport_lcfg_name(*lcfg), *lcfg);
+ }
+
+
+
+ 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);
+ abis_nm_bs11_get_bport_line_cfg(g_bts, 0);
+ abis_nm_bs11_get_bport_line_cfg(g_bts, 1);
+ 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, 0xff, 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, "pll-setvalue")) {
+ abis_nm_bs11_set_pll(g_bts, atoi(value));
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "pll-workvalue")) {
+ /* To set the work value we need to login as FIELD */
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ sleep(1);
+ abis_nm_bs11_infield_logon(g_bts, 1);
+ sleep(1);
+ abis_nm_bs11_set_pll(g_bts, atoi(value));
+ sleep(1);
+ abis_nm_bs11_infield_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;
+ } else if (!strcmp(command, "bport1-multidrop")) {
+ abis_nm_bs11_set_bport_line_cfg(g_bts, 1, 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, g_bts, 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, g_bts, 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;
+ case NM_MT_GET_ATTR_NACK:
+ printf("\n%sGET ATTR NACK\n", obj_name(foh));
+ break;
+ case NM_MT_BS11_CREATE_OBJ_ACK:
+ printf("\n%sCREATE OBJECT ACK\n", obj_name(foh));
+ break;
+ case NM_MT_BS11_CREATE_OBJ_NACK:
+ printf("\n%sCREATE OBJECT NACK\n", obj_name(foh));
+ break;
+ case NM_MT_BS11_DELETE_OBJ_ACK:
+ printf("\n%sDELETE OBJECT ACK\n", obj_name(foh));
+ break;
+ case NM_MT_BS11_DELETE_OBJ_NACK:
+ printf("\n%sDELETE OBJECT NACK\n", obj_name(foh));
+ 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;
+}
+
+void status_timer_cb(void *data)
+{
+ abis_nm_bs11_get_state(g_bts);
+}
+
+static void print_banner(void)
+{
+ printf("bs11_config (C) 2009-2010 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\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\t\tQuery the BS-11 about serial number and configuration\n");
+ printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n");
+ printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n");
+ printf("\trestart\t\t\tRestart the BTS\n");
+ printf("\tsoftware\t\tDownload Software (only in administrative state)\n");
+ printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
+ printf("\tdelete-trx1\t\tDelete objects for TRX1\n");
+ printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n");
+ printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n");
+ printf("\tpll-setvalue <value>\tSet the PLL set value\n");
+ printf("\tpll-workvalue <value>\tSet the PLL work value\n");
+ printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n");
+ printf("\tbport0-star\t\tSet BPORT0 line config to star\n");
+ printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n");
+ printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n");
+ printf("\tcreate-bport1\t\tCreate BPORT1 object\n");
+ printf("\tdelete-bport1\t\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':
+ log_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];
+ if (optind+1 < argc)
+ value = argv[optind+1];
+
+}
+
+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;
+ }
+}
+
+extern int bts_model_bs11_init(void);
+int main(int argc, char **argv)
+{
+ struct gsm_network *gsmnet;
+ int rc;
+
+ log_init(&log_info);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+ handle_options(argc, argv);
+ bts_model_bs11_init();
+
+ 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);
+}
+
+/* dummy to be able to compile */
+void gsm_net_update_ctype(struct gsm_network *net)
+{
+}
diff --git a/openbsc/src/utils/isdnsync.c b/openbsc/src/utils/isdnsync.c
new file mode 100644
index 00000000..1c4aa5d6
--- /dev/null
+++ b/openbsc/src/utils/isdnsync.c
@@ -0,0 +1,191 @@
+/* isdnsync.c
+ *
+ * Author Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "mISDNif.h"
+#define MISDN_OLD_AF_COMPATIBILITY
+#define AF_COMPATIBILITY_FUNC
+#include "compat_af_isdn.h"
+
+int card = 0;
+int sock = -1;
+
+int mISDN_open(void)
+{
+ int fd, ret;
+ struct mISDN_devinfo devinfo;
+ struct sockaddr_mISDN l2addr;
+
+ fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
+ if (fd < 0) {
+ fprintf(stderr, "could not open socket (%s)\n", strerror(errno));
+ return fd;
+ }
+ devinfo.id = card;
+ ret = ioctl(fd, IMGETDEVINFO, &devinfo);
+ if (ret < 0) {
+ fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno));
+ close(fd);
+ return ret;
+ }
+ close(fd);
+ if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
+ && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) {
+ fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno));
+ close(fd);
+ return ret;
+ }
+ fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE);
+ if (fd < 0) {
+ fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno));
+ return fd;
+ }
+ l2addr.family = AF_ISDN;
+ l2addr.dev = card;
+ l2addr.channel = 0;
+ l2addr.sapi = 0;
+ l2addr.tei = 0;
+ ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr));
+ if (ret < 0) {
+ fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno));
+ close(fd);
+ return ret;
+ }
+ sock = fd;
+
+ return sock;
+}
+
+
+void mISDN_handle(void)
+{
+ int ret;
+ fd_set rfd;
+ struct timeval tv;
+ struct sockaddr_mISDN addr;
+ socklen_t alen;
+ unsigned char buffer[2048];
+ struct mISDNhead *hh = (struct mISDNhead *)buffer;
+ int l1 = 0, l2 = 0, tei = 0;
+
+ while(1) {
+again:
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ ret = select(sock+1, &rfd, NULL, NULL, &tv);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno));
+ break;
+ }
+ if (FD_ISSET(sock, &rfd)) {
+ alen = sizeof(addr);
+ ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen);
+ if (ret < 0) {
+ fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno));
+ } else if (ret < MISDN_HEADER_LEN) {
+ fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__);
+ } else {
+ switch(hh->prim) {
+ case MPH_ACTIVATE_IND:
+ case PH_ACTIVATE_IND:
+ if (!l1) {
+ printf("PH_ACTIVATE\n");
+ printf("*** Sync available from interface :-)\n");
+ l1 = 1;
+ }
+ goto again;
+ break;
+ case MPH_DEACTIVATE_IND:
+ case PH_DEACTIVATE_IND:
+ if (l1) {
+ printf("PH_DEACTIVATE\n");
+ printf("*** Lost sync on interface :-(\n");
+ l1 = 0;
+ }
+ goto again;
+ break;
+ case DL_ESTABLISH_IND:
+ case DL_ESTABLISH_CNF:
+ printf("DL_ESTABLISH\n");
+ l2 = 1;
+ goto again;
+ break;
+ case DL_RELEASE_IND:
+ case DL_RELEASE_CNF:
+ printf("DL_RELEASE\n");
+ l2 = 0;
+ goto again;
+ break;
+ case DL_INFORMATION_IND:
+ printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi);
+ tei = 1;
+ break;
+ default:
+// printf("prim %x\n", hh->prim);
+ goto again;
+ }
+ }
+ }
+ if (tei && !l2) {
+ hh->prim = DL_ESTABLISH_REQ;
+ printf("-> activating layer 2\n");
+ sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc <= 1)
+ {
+ printf("Usage: %s <card>\n\n", argv[0]);
+ printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n");
+ printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n");
+ return(0);
+ }
+
+ card = atoi(argv[1]);
+
+ init_af_isdn();
+
+ if ((ret = mISDN_open() < 0))
+ return(ret);
+
+ mISDN_handle();
+
+ close(sock);
+
+ return 0;
+}
diff --git a/openbsc/src/utils/rs232.c b/openbsc/src/utils/rs232.c
new file mode 100644
index 00000000..75505710
--- /dev/null
+++ b/openbsc/src/utils/rs232.c
@@ -0,0 +1,248 @@
+/* OpenBSC BS-11 T-Link interface using POSIX serial port */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#include <osmocore/select.h>
+#include <osmocore/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/rs232.h>
+
+/* adaption layer from GSM 08.59 + 12.21 to RS232 */
+
+struct serial_handle {
+ struct bsc_fd fd;
+ struct llist_head tx_queue;
+
+ struct msgb *rx_msg;
+ unsigned int rxmsg_bytes_missing;
+
+ unsigned int delay_ms;
+ struct gsm_bts *bts;
+};
+
+/* FIXME: this needs to go */
+static struct serial_handle _ser_handle, *ser_handle = &_ser_handle;
+
+#define LAPD_HDR_LEN 10
+
+static int handle_ser_write(struct bsc_fd *bfd);
+
+/* callback from abis_nm */
+int _abis_nm_sendmsg(struct msgb *msg, int to_trx_oml)
+{
+ struct serial_handle *sh = ser_handle;
+ u_int8_t *lapd;
+ unsigned int len;
+
+ msg->l2h = msg->data;
+
+ /* prepend LAPD header */
+ lapd = msgb_push(msg, LAPD_HDR_LEN);
+
+ len = msg->len - 2;
+
+ lapd[0] = (len >> 8) & 0xff;
+ lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */
+ lapd[2] = 0x00;
+ lapd[3] = 0x07;
+ lapd[4] = 0x01;
+ lapd[5] = 0x3e;
+ lapd[6] = 0x00;
+ lapd[7] = 0x00;
+ lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */
+ lapd[9] = lapd[8] ^ 0x38;
+
+ msgb_enqueue(&sh->tx_queue, msg);
+ sh->fd.when |= BSC_FD_WRITE;
+
+ /* we try to immediately send */
+ handle_ser_write(&sh->fd);
+
+ return 0;
+}
+
+/* select.c callback in case we can write to the RS232 */
+static int handle_ser_write(struct bsc_fd *bfd)
+{
+ struct serial_handle *sh = bfd->data;
+ struct msgb *msg;
+ int written;
+
+ msg = msgb_dequeue(&sh->tx_queue);
+ if (!msg) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+
+ DEBUGP(DMI, "RS232 TX: %s\n", hexdump(msg->data, msg->len));
+
+ /* send over serial line */
+ written = write(bfd->fd, msg->data, msg->len);
+ if (written < msg->len) {
+ perror("short write:");
+ msgb_free(msg);
+ return -1;
+ }
+
+ msgb_free(msg);
+ usleep(sh->delay_ms*1000);
+
+ return 0;
+}
+
+#define SERIAL_ALLOC_SIZE 300
+
+/* select.c callback in case we can read from the RS232 */
+static int handle_ser_read(struct bsc_fd *bfd)
+{
+ struct serial_handle *sh = bfd->data;
+ struct msgb *msg;
+ int rc = 0;
+
+ if (!sh->rx_msg) {
+ sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE, "RS232 Rx");
+ sh->rx_msg->l2h = NULL;
+ sh->rx_msg->trx = sh->bts->c0;
+ }
+ msg = sh->rx_msg;
+
+ /* first read two byes to obtain length */
+ if (msg->len < 2) {
+ rc = read(sh->fd.fd, msg->tail, 2 - msg->len);
+ if (rc < 0) {
+ perror("ERROR reading from serial port");
+ msgb_free(msg);
+ return rc;
+ }
+ msgb_put(msg, rc);
+
+ if (msg->len >= 2) {
+ /* parse LAPD payload length */
+ if (msg->data[0] != 0)
+ fprintf(stderr, "Suspicious header byte 0: 0x%02x\n",
+ msg->data[0]);
+
+ sh->rxmsg_bytes_missing = msg->data[0] << 8;
+ sh->rxmsg_bytes_missing += msg->data[1];
+
+ if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2)
+ fprintf(stderr, "Invalid length in hdr: %u\n",
+ sh->rxmsg_bytes_missing);
+ }
+ } else {
+ /* try to read as many of the missing bytes as are available */
+ rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
+ if (rc < 0) {
+ perror("ERROR reading from serial port");
+ msgb_free(msg);
+ return rc;
+ }
+ msgb_put(msg, rc);
+ sh->rxmsg_bytes_missing -= rc;
+
+ if (sh->rxmsg_bytes_missing == 0) {
+ /* we have one complete message now */
+ sh->rx_msg = NULL;
+
+ if (msg->len > LAPD_HDR_LEN)
+ msg->l2h = msg->data + LAPD_HDR_LEN;
+
+ DEBUGP(DMI, "RS232 RX: %s\n", hexdump(msg->data, msg->len));
+ rc = handle_serial_msg(msg);
+ }
+ }
+
+ return rc;
+}
+
+/* select.c callback */
+static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_ser_read(bfd);
+
+ if (rc < 0)
+ return rc;
+
+ if (what & BSC_FD_WRITE)
+ rc = handle_ser_write(bfd);
+
+ return rc;
+}
+
+int rs232_setup(const char *serial_port, unsigned int delay_ms,
+ struct gsm_bts *bts)
+{
+ int rc, serial_fd;
+ struct termios tio;
+
+ serial_fd = open(serial_port, O_RDWR);
+ if (serial_fd < 0) {
+ perror("cannot open serial port:");
+ return serial_fd;
+ }
+
+ /* set baudrate */
+ rc = tcgetattr(serial_fd, &tio);
+ if (rc < 0) {
+ perror("tcgetattr()");
+ return rc;
+ }
+ cfsetispeed(&tio, B19200);
+ cfsetospeed(&tio, B19200);
+ tio.c_cflag |= (CREAD | CLOCAL | CS8);
+ tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tio.c_iflag |= (INPCK | ISTRIP);
+ tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
+ tio.c_oflag &= ~(OPOST);
+ rc = tcsetattr(serial_fd, TCSADRAIN, &tio);
+ if (rc < 0) {
+ perror("tcsetattr()");
+ return rc;
+ }
+
+ INIT_LLIST_HEAD(&ser_handle->tx_queue);
+ ser_handle->fd.fd = serial_fd;
+ ser_handle->fd.when = BSC_FD_READ;
+ ser_handle->fd.cb = serial_fd_cb;
+ ser_handle->fd.data = ser_handle;
+ ser_handle->delay_ms = delay_ms;
+ ser_handle->bts = bts;
+ rc = bsc_register_fd(&ser_handle->fd);
+ if (rc < 0) {
+ fprintf(stderr, "could not register FD: %s\n",
+ strerror(rc));
+ return rc;
+ }
+
+ return 0;
+}