diff options
-rw-r--r-- | openbsc/include/openbsc/abis_om2000.h | 59 | ||||
-rw-r--r-- | openbsc/include/openbsc/gsm_data.h | 1 | ||||
-rw-r--r-- | openbsc/include/openbsc/signal.h | 2 | ||||
-rw-r--r-- | openbsc/include/openbsc/vty.h | 1 | ||||
-rw-r--r-- | openbsc/src/Makefile.am | 6 | ||||
-rw-r--r-- | openbsc/src/abis_om2000.c | 811 | ||||
-rw-r--r-- | openbsc/src/abis_om2000_vty.c | 278 | ||||
-rw-r--r-- | openbsc/src/bsc_hack.c | 1 | ||||
-rw-r--r-- | openbsc/src/bsc_vty.c | 1 | ||||
-rw-r--r-- | openbsc/src/bts_ericsson_rbs2000.c | 148 | ||||
-rw-r--r-- | openbsc/src/common_vty.c | 3 | ||||
-rw-r--r-- | openbsc/src/e1_config.c | 5 | ||||
-rw-r--r-- | openbsc/src/gsm_data.c | 1 | ||||
-rw-r--r-- | openbsc/src/input/dahdi.c | 9 | ||||
-rw-r--r-- | openbsc/src/input/lapd.c | 175 | ||||
-rw-r--r-- | openbsc/src/input/lapd.h | 7 |
16 files changed, 1489 insertions, 19 deletions
diff --git a/openbsc/include/openbsc/abis_om2000.h b/openbsc/include/openbsc/abis_om2000.h new file mode 100644 index 000000000..f15a50d5e --- /dev/null +++ b/openbsc/include/openbsc/abis_om2000.h @@ -0,0 +1,59 @@ +#ifndef OPENBSC_ABIS_OM2K_H +#define OPENBSC_ABIS_OM2K_H +/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface + * implemented based on protocol trace analysis, no formal documentation */ + +/* (C) 2010-2011 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/>. + * + */ + +struct abis_om2k_mo { + uint8_t class; + uint8_t bts; + uint8_t assoc_so; + uint8_t inst; +} __attribute__ ((packed)); + +struct om2k_is_conn_grp { + uint16_t icp1; + uint16_t icp2; + uint8_t cont_idx; +} __attribute__ ((packed)); + +extern const struct value_string om2k_mo_class_short_vals[]; + +int abis_om2k_rcvmsg(struct msgb *msg); + +extern const struct abis_om2k_mo om2k_mo_cf; + +int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t operational); +int abis_om2k_tx_is_conf_req(struct gsm_bts *bts, struct om2k_is_conn_grp *cg, + unsigned int num_cg); + +int abis_om2k_vty_init(void); + +#endif /* OPENBCS_ABIS_OM2K_H */ diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index e308ca4d7..a98302777 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -421,6 +421,7 @@ enum gsm_bts_type { GSM_BTS_TYPE_UNKNOWN, GSM_BTS_TYPE_BS11, GSM_BTS_TYPE_NANOBTS, + GSM_BTS_TYPE_RBS2000, }; struct gsm_bts_model { diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h index 866fa293d..027358627 100644 --- a/openbsc/include/openbsc/signal.h +++ b/openbsc/include/openbsc/signal.h @@ -145,6 +145,8 @@ enum signal_input { S_INP_TEI_UP, S_INP_TEI_DN, S_INP_LINE_INIT, + S_INP_LINE_ALARM, + S_INP_LINE_NOALARM, }; struct gsm_subscriber; diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h index 1be81a7f2..8c38313a5 100644 --- a/openbsc/include/openbsc/vty.h +++ b/openbsc/include/openbsc/vty.h @@ -33,6 +33,7 @@ enum bsc_vty_node { NAT_NODE, NAT_BSC_NODE, MSC_NODE, + OM2K_NODE, }; extern int bsc_vty_is_config_node(struct vty *vty, int node); diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 3f2140669..077054c3a 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -16,14 +16,14 @@ endif sbin_PROGRAMS = bsc_hack bs11_config isdnsync bsc_mgcp noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libmgcp.a -libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ - chan_alloc.c debug.c socket.c abis_nm_vty.c \ +libbsc_a_SOURCES = abis_rsl.c abis_nm.c abis_om2000.c gsm_data.c gsm_04_08_utils.c \ + chan_alloc.c debug.c socket.c abis_nm_vty.c abis_om2000_vty.c \ gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \ trau_frame.c trau_mux.c paging.c \ e1_config.c e1_input.c e1_input_vty.c \ input/misdn.c input/ipaccess.c input/dahdi.c input/lapd.c \ handover_logic.c talloc_ctx.c system_information.c rest_octets.c \ - bts_siemens_bs11.c bts_ipaccess_nanobts.c mncc_upqueue.c \ + bts_siemens_bs11.c bts_ipaccess_nanobts.c bts_ericsson_rbs2000.c mncc_upqueue.c \ bts_unknown.c bsc_version.c bsc_api.c bsc_vty.c meas_rep.c gsm_04_80.c libmsc_a_SOURCES = gsm_subscriber.c db.c \ diff --git a/openbsc/src/abis_om2000.c b/openbsc/src/abis_om2000.c new file mode 100644 index 000000000..ab224f994 --- /dev/null +++ b/openbsc/src/abis_om2000.c @@ -0,0 +1,811 @@ +/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface + * implemented based on protocol trace analysis, no formal documentation */ + +/* (C) 2010-2011 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 <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/abis_nm.h> +#include <openbsc/abis_om2000.h> +#include <openbsc/signal.h> + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 + +/* use following functions from abis_nm.c: + * om2k_msgb_alloc() + * abis_om2k_sendmsg() + */ + +struct abis_om2k_hdr { + struct abis_om_hdr om; + uint16_t msg_type; + struct abis_om2k_mo mo; + uint8_t data[0]; +} __attribute__ ((packed)); + +enum abis_om2k_msgtype { + OM2K_MSGT_ABORT_SP_CMD = 0x0000, + OM2K_MSGT_ABORT_SP_COMPL = 0x0002, + OM2K_MSGT_ALARM_REP_ACK = 0x0004, + OM2K_MSGT_ALARM_REP_NACK = 0x0005, + OM2K_MSGT_ALARM_REP = 0x0006, + OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, + OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, + OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, + OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, + OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, + OM2K_MSGT_ALARM_STATUS_RES = 0x000e, + OM2K_MSGT_CAL_TIME_RESP = 0x0010, + OM2K_MSGT_CAL_TIME_REJ = 0x0011, + OM2K_MSGT_CAL_TIME_REQ = 0x0012, + + OM2K_MSGT_CONNECT_CMD = 0x001c, + OM2K_MSGT_CONNECT_COMPL = 0x001e, + OM2K_MSGT_CONNECT_REJ = 0x001f, + + OM2K_MSGT_DISABLE_REQ = 0x0028, + OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, + OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, + OM2K_MSGT_DISABLE_RES_ACK = 0x002c, + OM2K_MSGT_DISABLE_RES_NACK = 0x002d, + OM2K_MSGT_DISABLE_RES = 0x002e, + OM2K_MSGT_DISCONNECT_CMD = 0x0030, + OM2K_MSGT_DISCONNECT_COMPL = 0x0032, + OM2K_MSGT_DISCONNECT_REJ = 0x0033, + OM2K_MSGT_ENABLE_REQ = 0x0034, + OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, + OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, + OM2K_MSGT_ENABLE_RES_ACK = 0x0038, + OM2K_MSGT_ENABLE_RES_NACK = 0x0039, + OM2K_MSGT_ENABLE_RES = 0x003a, + + OM2K_MSGT_FAULT_REP_ACK = 0x0040, + OM2K_MSGT_FAULT_REP_NACK = 0x0041, + OM2K_MSGT_FAULT_REP = 0x0042, + + OM2K_MSGT_IS_CONF_REQ = 0x0060, + OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, + OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, + OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, + OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, + OM2K_MSGT_IS_CONF_RES = 0x0066, + + OM2K_MSGT_OP_INFO = 0x0074, + OM2K_MSGT_OP_INFO_ACK = 0x0076, + OM2K_MSGT_OP_INFO_REJ = 0x0077, + OM2K_MSGT_RESET_CMD = 0x0078, + OM2K_MSGT_RESET_COMPL = 0x007a, + OM2K_MSGT_RESET_REJ = 0x007b, + + OM2K_MSGT_START_REQ = 0x0084, + OM2K_MSGT_START_REQ_ACK = 0x0086, + OM2K_MSGT_START_REQ_REJ = 0x0087, + OM2K_MSGT_START_RES_ACK = 0x0088, + OM2K_MSGT_START_RES_NACK = 0x0089, + OM2K_MSGT_START_RES = 0x008a, + OM2K_MSGT_STATUS_REQ = 0x008c, + OM2K_MSGT_STATUS_RESP = 0x008e, + OM2K_MSGT_STATUS_REJ = 0x008f, + + OM2K_MSGT_TEST_REQ = 0x0094, + OM2K_MSGT_TEST_REQ_ACK = 0x0096, + OM2K_MSGT_TEST_REQ_REJ = 0x0097, + OM2K_MSGT_TEST_RES_ACK = 0x0098, + OM2K_MSGT_TEST_RES_NACK = 0x0099, + OM2K_MSGT_TEST_RES = 0x009a, + + OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, + OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, + OM2K_MSGT_NEGOT_REQ = 0x0106, +}; + +enum abis_om2k_dei { + OM2K_DEI_CAL_TIME = 0x0d, + OM2K_DEI_END_LIST_NR = 0x13, + OM2K_DEI_IS_CONN_LIST = 0x27, + OM2K_DEI_LIST_NR = 0x28, + OM2K_DEI_OP_INFO = 0x2e, + OM2K_DEI_NEGOT_REC1 = 0x90, + OM2K_DEI_NEGOT_REC2 = 0x91, +}; + +enum abis_om2k_mo_cls { + OM2K_MO_CLS_TRXC = 0x01, + OM2K_MO_CLS_TS = 0x03, + OM2K_MO_CLS_TF = 0x04, + OM2K_MO_CLS_IS = 0x05, + OM2K_MO_CLS_CON = 0x06, + OM2K_MO_CLS_DP = 0x07, + OM2K_MO_CLS_CF = 0x0a, + OM2K_MO_CLS_TX = 0x0b, + OM2K_MO_CLS_RX = 0x0c, +}; + +static const struct value_string om2k_msgcode_vals[] = { + { 0x0000, "Abort SP Command" }, + { 0x0002, "Abort SP Complete" }, + { 0x0004, "Alarm Report ACK" }, + { 0x0005, "Alarm Report NACK" }, + { 0x0006, "Alarm Report" }, + { 0x0008, "Alarm Status Request" }, + { 0x000a, "Alarm Status Request Accept" }, + { 0x000b, "Alarm Status Request Reject" }, + { 0x000c, "Alarm Status Result ACK" }, + { 0x000d, "Alarm Status Result NACK" }, + { 0x000e, "Alarm Status Result" }, + { 0x0010, "Calendar Time Response" }, + { 0x0011, "Calendar Time Reject" }, + { 0x0012, "Calendar Time Request" }, + { 0x0014, "CON Configuration Request" }, + { 0x0016, "CON Configuration Request Accept" }, + { 0x0017, "CON Configuration Request Reject" }, + { 0x0018, "CON Configuration Result ACK" }, + { 0x0019, "CON Configuration Result NACK" }, + { 0x001a, "CON Configuration Result" }, + { 0x001c, "Connect Command" }, + { 0x001e, "Connect Complete" }, + { 0x001f, "Connect Rejecte" }, + { 0x0028, "Disable Request" }, + { 0x002a, "Disable Request Accept" }, + { 0x002b, "Disable Request Reject" }, + { 0x002c, "Disable Result ACK" }, + { 0x002d, "Disable Result NACK" }, + { 0x002e, "Disable Result" }, + { 0x0030, "Disconnect Command" }, + { 0x0032, "Disconnect Complete" }, + { 0x0033, "Disconnect Reject" }, + { 0x0034, "Enable Request" }, + { 0x0036, "Enable Request Accept" }, + { 0x0037, "Enable Request Reject" }, + { 0x0038, "Enable Result ACK" }, + { 0x0039, "Enable Result NACK" }, + { 0x003a, "Enable Result" }, + { 0x003c, "Escape Downlink Normal" }, + { 0x003d, "Escape Downlink NACK" }, + { 0x003e, "Escape Uplink Normal" }, + { 0x003f, "Escape Uplink NACK" }, + { 0x0040, "Fault Report ACK" }, + { 0x0041, "Fault Report NACK" }, + { 0x0042, "Fault Report" }, + { 0x0044, "File Package End Command" }, + { 0x0046, "File Package End Result" }, + { 0x0047, "File Package End Reject" }, + { 0x0048, "File Relation Request" }, + { 0x004a, "File Relation Response" }, + { 0x004b, "File Relation Request Reject" }, + { 0x004c, "File Segment Transfer" }, + { 0x004e, "File Segment Transfer Complete" }, + { 0x004f, "File Segment Transfer Reject" }, + { 0x0050, "HW Information Request" }, + { 0x0052, "HW Information Request Accept" }, + { 0x0053, "HW Information Request Reject" }, + { 0x0054, "HW Information Result ACK" }, + { 0x0055, "HW Information Result NACK" }, + { 0x0056, "HW Information Result" }, + { 0x0060, "IS Configuration Request" }, + { 0x0062, "IS Configuration Request Accept" }, + { 0x0063, "IS Configuration Request Reject" }, + { 0x0064, "IS Configuration Result ACK" }, + { 0x0065, "IS Configuration Result NACK" }, + { 0x0066, "IS Configuration Result" }, + { 0x0068, "Load Data End" }, + { 0x006a, "Load Data End Result" }, + { 0x006b, "Load Data End Reject" }, + { 0x006c, "Load Data Init" }, + { 0x006e, "Load Data Init Accept" }, + { 0x006f, "Load Data Init Reject" }, + { 0x0070, "Loop Control Command" }, + { 0x0072, "Loop Control Complete" }, + { 0x0073, "Loop Control Reject" }, + { 0x0074, "Operational Information" }, + { 0x0076, "Operational Information Accept" }, + { 0x0077, "Operational Information Reject" }, + { 0x0078, "Reset Command" }, + { 0x007a, "Reset Complete" }, + { 0x007b, "Reset Reject" }, + { 0x007c, "RX Configuration Request" }, + { 0x007e, "RX Configuration Request Accept" }, + { 0x007f, "RX Configuration Request Reject" }, + { 0x0080, "RX Configuration Result ACK" }, + { 0x0081, "RX Configuration Result NACK" }, + { 0x0082, "RX Configuration Result" }, + { 0x0084, "Start Request" }, + { 0x0086, "Start Request Accept" }, + { 0x0087, "Start Request Reject" }, + { 0x0088, "Start Result ACK" }, + { 0x0089, "Start Result NACK" }, + { 0x008a, "Start Result" }, + { 0x008c, "Status Request" }, + { 0x008e, "Status Response" }, + { 0x008f, "Status Reject" }, + { 0x0094, "Test Request" }, + { 0x0096, "Test Request Accept" }, + { 0x0097, "Test Request Reject" }, + { 0x0098, "Test Result ACK" }, + { 0x0099, "Test Result NACK" }, + { 0x009a, "Test Result" }, + { 0x00a0, "TF Configuration Request" }, + { 0x00a2, "TF Configuration Request Accept" }, + { 0x00a3, "TF Configuration Request Reject" }, + { 0x00a4, "TF Configuration Result ACK" }, + { 0x00a5, "TF Configuration Result NACK" }, + { 0x00a6, "TF Configuration Result" }, + { 0x00a8, "TS Configuration Request" }, + { 0x00aa, "TS Configuration Request Accept" }, + { 0x00ab, "TS Configuration Request Reject" }, + { 0x00ac, "TS Configuration Result ACK" }, + { 0x00ad, "TS Configuration Result NACK" }, + { 0x00ae, "TS Configuration Result" }, + { 0x00b0, "TX Configuration Request" }, + { 0x00b2, "TX Configuration Request Accept" }, + { 0x00b3, "TX Configuration Request Reject" }, + { 0x00b4, "TX Configuration Result ACK" }, + { 0x00b5, "TX Configuration Result NACK" }, + { 0x00b6, "TX Configuration Result" }, + { 0x00bc, "DIP Alarm Report ACK" }, + { 0x00bd, "DIP Alarm Report NACK" }, + { 0x00be, "DIP Alarm Report" }, + { 0x00c0, "DIP Alarm Status Request" }, + { 0x00c2, "DIP Alarm Status Response" }, + { 0x00c3, "DIP Alarm Status Reject" }, + { 0x00c4, "DIP Quality Report I ACK" }, + { 0x00c5, "DIP Quality Report I NACK" }, + { 0x00c6, "DIP Quality Report I" }, + { 0x00c8, "DIP Quality Report II ACK" }, + { 0x00c9, "DIP Quality Report II NACK" }, + { 0x00ca, "DIP Quality Report II" }, + { 0x00dc, "DP Configuration Request" }, + { 0x00de, "DP Configuration Request Accept" }, + { 0x00df, "DP Configuration Request Reject" }, + { 0x00e0, "DP Configuration Result ACK" }, + { 0x00e1, "DP Configuration Result NACK" }, + { 0x00e2, "DP Configuration Result" }, + { 0x00e4, "Capabilities HW Info Report ACK" }, + { 0x00e5, "Capabilities HW Info Report NACK" }, + { 0x00e6, "Capabilities HW Info Report" }, + { 0x00e8, "Capabilities Request" }, + { 0x00ea, "Capabilities Request Accept" }, + { 0x00eb, "Capabilities Request Reject" }, + { 0x00ec, "Capabilities Result ACK" }, + { 0x00ed, "Capabilities Result NACK" }, + { 0x00ee, "Capabilities Result" }, + { 0x00f0, "FM Configuration Request" }, + { 0x00f2, "FM Configuration Request Accept" }, + { 0x00f3, "FM Configuration Request Reject" }, + { 0x00f4, "FM Configuration Result ACK" }, + { 0x00f5, "FM Configuration Result NACK" }, + { 0x00f6, "FM Configuration Result" }, + { 0x00f8, "FM Report Request" }, + { 0x00fa, "FM Report Response" }, + { 0x00fb, "FM Report Reject" }, + { 0x00fc, "FM Start Command" }, + { 0x00fe, "FM Start Complete" }, + { 0x00ff, "FM Start Reject" }, + { 0x0100, "FM Stop Command" }, + { 0x0102, "FM Stop Complete" }, + { 0x0103, "FM Stop Reject" }, + { 0x0104, "Negotiation Request ACK" }, + { 0x0105, "Negotiation Request NACK" }, + { 0x0106, "Negotiation Request" }, + { 0x0108, "BTS Initiated Request ACK" }, + { 0x0109, "BTS Initiated Request NACK" }, + { 0x010a, "BTS Initiated Request" }, + { 0x010c, "Radio Channels Release Command" }, + { 0x010e, "Radio Channels Release Complete" }, + { 0x010f, "Radio Channels Release Reject" }, + { 0x0118, "Feature Control Command" }, + { 0x011a, "Feature Control Complete" }, + { 0x011b, "Feature Control Reject" }, + + { 0, NULL } +}; + +/* TS 12.21 Section 9.4: Attributes */ +static const struct value_string om2k_attr_vals[] = { + { 0x00, "Accordance indication" }, + { 0x01, "Alarm Id" }, + { 0x02, "Alarm Data" }, + { 0x03, "Alarm Severity" }, + { 0x04, "Alarm Status" }, + { 0x05, "Alarm Status Type" }, + { 0x06, "BCC" }, + { 0x07, "BS_AG_BKS_RES" }, + { 0x09, "BSIC" }, + { 0x0a, "BA_PA_MFRMS" }, + { 0x0b, "CBCH Indicator" }, + { 0x0c, "CCCH Options" }, + { 0x0d, "Calendar Time" }, + { 0x0f, "Channel Combination" }, + { 0x10, "CON Connection List" }, + { 0x11, "Data End Indication" }, + { 0x12, "DRX_DEV_MAX" }, + { 0x13, "End List Number" }, + { 0x14, "External Condition Map Class 1" }, + { 0x15, "External Condition Map Class 2" }, + { 0x16, "File Relation Indication" }, + { 0x17, "File Revision" }, + { 0x18, "File Segment Data" }, + { 0x19, "File Segment Length" }, + { 0x1a, "File Segment Sequence Number" }, + { 0x1b, "File Size" }, + { 0x1c, "Filling Marker" }, + { 0x1d, "FN Offset" }, + { 0x1e, "Frequency List" }, + { 0x1f, "Frequency Specifier RX" }, + { 0x20, "Frequency Specifier TX" }, + { 0x21, "HSN" }, + { 0x22, "ICM Indicator" }, + { 0x23, "Internal Fault Map Class 1A" }, + { 0x24, "Internal Fault Map Class 1B" }, + { 0x25, "Internal Fault Map Class 2A" }, + { 0x26, "Internal Fault Map Class 2A Extension" }, + { 0x27, "IS Connection List" }, + { 0x28, "List Number" }, + { 0x29, "File Package State Indication" }, + { 0x2a, "Local Access State" }, + { 0x2b, "MAIO" }, + { 0x2c, "MO State" }, + { 0x2d, "Ny1" }, + { 0x2e, "Operational Information" }, + { 0x2f, "Power" }, + { 0x30, "RU Position Data" }, + { 0x31, "Protocol Error" }, + { 0x32, "Reason Code" }, + { 0x33, "Receiver Diversity" }, + { 0x34, "Replacement Unit Map" }, + { 0x35, "Result Code" }, + { 0x36, "RU Revision Data" }, + { 0x38, "T3105" }, + { 0x39, "Test Loop Setting" }, + { 0x3a, "TF Mode" }, + { 0x3b, "TF Compensation Value" }, + { 0x3c, "Time Slot Number" }, + { 0x3d, "TSC" }, + { 0x3e, "RU Logical Id" }, + { 0x3f, "RU Serial Number Data" }, + { 0x40, "BTS Version" }, + { 0x41, "OML IWD Version" }, + { 0x42, "RWL IWD Version" }, + { 0x43, "OML Function Map 1" }, + { 0x44, "OML Function Map 2" }, + { 0x45, "RSL Function Map 1" }, + { 0x46, "RSL Function Map 2" }, + { 0x47, "Extended Range Indicator" }, + { 0x48, "Request Indicators" }, + { 0x49, "DIP Alarm Condition Map" }, + { 0x4a, "ES Incoming" }, + { 0x4b, "ES Outgoing" }, + { 0x4e, "SES Incoming" }, + { 0x4f, "SES Outgoing" }, + { 0x50, "Replacement Unit Map Extension" }, + { 0x52, "UAS Incoming" }, + { 0x53, "UAS Outgoing" }, + { 0x58, "DF Incoming" }, + { 0x5a, "DF Outgoing" }, + { 0x5c, "SF" }, + { 0x60, "S Bits Setting" }, + { 0x61, "CRC-4 Use Option" }, + { 0x62, "T Parameter" }, + { 0x63, "N Parameter" }, + { 0x64, "N1 Parameter" }, + { 0x65, "N3 Parameter" }, + { 0x66, "N4 Parameter" }, + { 0x67, "P Parameter" }, + { 0x68, "Q Parameter" }, + { 0x69, "BI_Q1" }, + { 0x6a, "BI_Q2" }, + { 0x74, "ICM Boundary Parameters" }, + { 0x77, "AFT" }, + { 0x78, "AFT RAI" }, + { 0x79, "Link Supervision Control" }, + { 0x7a, "Link Supervision Filtering Time" }, + { 0x7b, "Call Supervision Time" }, + { 0x7c, "Interval Length UAS Incoming" }, + { 0x7d, "Interval Length UAS Outgoing" }, + { 0x7e, "ICM Channel Rate" }, + { 0x7f, "Attribute Identifier" }, + { 0x80, "FM Frequency List" }, + { 0x81, "FM Frequency Report" }, + { 0x82, "FM Percentile" }, + { 0x83, "FM Clear Indication" }, + { 0x84, "HW Info Signature" }, + { 0x85, "MO Record" }, + { 0x86, "TF Synchronisation Source" }, + { 0x87, "TTA" }, + { 0x88, "End Segment Number" }, + { 0x89, "Segment Number" }, + { 0x8a, "Capabilities Signature" }, + { 0x8c, "File Relation List" }, + { 0x90, "Negotiation Record I" }, + { 0x91, "Negotiation Record II" }, + { 0x92, "Encryption Algorithm" }, + { 0x94, "Interference Rejection Combining" }, + { 0x95, "Dedication Information" }, + { 0x97, "Feature Code" }, + { 0x98, "FS Offset" }, + { 0x99, "ESB Timeslot" }, + { 0x9a, "Master TG Instance" }, + { 0x9b, "Master TX Chain Delay" }, + { 0x9c, "External Condition Class 2 Extension" }, + { 0x9d, "TSs MO State" }, + { 0, NULL } +}; + +const struct value_string om2k_mo_class_short_vals[] = { + { 0x01, "TRXC" }, + { 0x03, "TS" }, + { 0x04, "TF" }, + { 0x05, "IS" }, + { 0x06, "CON" }, + { 0x07, "DP" }, + { 0x0a, "CF" }, + { 0x0b, "TX" }, + { 0x0c, "RX" }, + { 0, NULL } +}; + +static struct msgb *om2k_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OM2000"); +} + +static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) +{ + msg->trx = bts->c0; + + return _abis_nm_sendmsg(msg); +} + +static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, + uint16_t msg_type, uint8_t attr_len) +{ + o2h->om.mdisc = ABIS_OM_MDISC_FOM; + o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; + o2h->om.sequence = 0; + o2h->om.length = 6 + attr_len; + o2h->msg_type = htons(msg_type); + memcpy(&o2h->mo, mo, sizeof(o2h->mo)); +} + +static char *om2k_mo_name(const struct abis_om2k_mo *mo) +{ + static char mo_buf[64]; + + memset(mo_buf, 0, sizeof(mo_buf)); + snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", + get_value_string(om2k_mo_class_short_vals, mo->class), + mo->bts, mo->assoc_so, mo->inst); + return mo_buf; +} + +const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; + +static int abis_om2k_cal_time_resp(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + time_t tm_t; + struct tm *tm; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP, 7); + + tm_t = time(NULL); + tm = localtime(&tm_t); + + msgb_put_u8(msg, OM2K_DEI_CAL_TIME); + msgb_put_u8(msg, tm->tm_year % 100); + msgb_put_u8(msg, tm->tm_mon + 1); + msgb_put_u8(msg, tm->tm_mday); + msgb_put_u8(msg, tm->tm_hour); + msgb_put_u8(msg, tm->tm_min); + msgb_put_u8(msg, tm->tm_sec); + + return abis_om2k_sendmsg(bts, msg); +} + +static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t msg_type) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, msg_type, 0); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, msg_type)); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); +} + +int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); +} + +int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); +} + +int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); +} + +int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); +} + +int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); +} + +int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); +} + +int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); +} + +int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t operational) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO, 2); + + msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_is_conf_req(struct gsm_bts *bts, struct om2k_is_conn_grp *cg, + unsigned int num_cg ) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ, + 2 + 2 + TLV_GROSS_LEN(num_cg * sizeof(*cg))); + + msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); + msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); + + msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, + num_cg * sizeof(*cg), (uint8_t *)cg); + + return abis_om2k_sendmsg(bts, msg); +} + +static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t *data, unsigned int len) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK, 2+len); + + msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); + + return abis_om2k_sendmsg(bts, msg); +} + +struct iwd_version { + uint8_t gen_char[3+1]; + uint8_t rev_char[3+1]; +}; + +struct iwd_type { + uint8_t num_vers; + struct iwd_version v[8]; +}; + +static int om2k_rx_negot_req(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + struct iwd_type iwd_types[16]; + uint8_t num_iwd_types = o2h->data[2]; + uint8_t *cur = o2h->data+3; + unsigned int i, v; + + uint8_t out_buf[1024]; + uint8_t *out_cur = out_buf+1; + uint8_t out_num_types = 0; + + memset(iwd_types, 0, sizeof(iwd_types)); + + /* Parse the RBS-supported IWD versions into iwd_types array */ + for (i = 0; i < num_iwd_types; i++) { + uint8_t num_versions = *cur++; + uint8_t iwd_type = *cur++; + + iwd_types[iwd_type].num_vers = num_versions; + + for (v = 0; v < num_versions; v++) { + struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; + + memcpy(iwd_v->gen_char, cur, 3); + cur += 3; + memcpy(iwd_v->rev_char, cur, 3); + cur += 3; + + DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, + iwd_v->gen_char, iwd_v->rev_char); + } + } + + /* Select the last version for each IWD type */ + for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { + struct iwd_type *type = &iwd_types[i]; + struct iwd_version *last_v; + + if (type->num_vers == 0) + continue; + + out_num_types++; + + last_v = &type->v[type->num_vers-1]; + + *out_cur++ = i; + memcpy(out_cur, last_v->gen_char, 3); + out_cur += 3; + memcpy(out_cur, last_v->rev_char, 3); + out_cur += 3; + } + + out_buf[0] = out_num_types; + + return abis_om2k_tx_negot_req_ack(msg->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); +} + +static int om2k_rx_start_res(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + int rc; + + rc = abis_om2k_tx_simple(msg->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); + rc = abis_om2k_tx_op_info(msg->trx->bts, &o2h->mo, 1); + + return rc; +} + +static int om2k_rx_op_info_ack(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + + /* FIXME: update Operational state in our structures */ + + return 0; +} + +int abis_om2k_rcvmsg(struct msgb *msg) +{ + struct gsm_bts *bts = msg->trx->bts; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + struct abis_om_hdr *oh = &o2h->om; + uint16_t msg_type = ntohs(o2h->msg_type); + 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 *)o2h + sizeof(*o2h); + + if (oh->mdisc != ABIS_OM_MDISC_FOM) { + LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", + oh->mdisc); + return -EINVAL; + } + + DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), + hexdump(msg->l2h, msgb_l2len(msg))); + + switch (msg_type) { + case OM2K_MSGT_CAL_TIME_REQ: + rc = abis_om2k_cal_time_resp(bts); + break; + case OM2K_MSGT_FAULT_REP: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); + break; + case OM2K_MSGT_NEGOT_REQ: + rc = om2k_rx_negot_req(msg); + break; + case OM2K_MSGT_START_RES: + rc = om2k_rx_start_res(msg); + break; + case OM2K_MSGT_OP_INFO_ACK: + rc = om2k_rx_op_info_ack(msg); + break; + case OM2K_MSGT_IS_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); + break; + case OM2K_MSGT_CONNECT_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); + break; + case OM2K_MSGT_RESET_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); + break; + case OM2K_MSGT_ENABLE_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); + break; + case OM2K_MSGT_START_REQ_ACK: + break; + case OM2K_MSGT_STATUS_RESP: + break; + default: + LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", + get_value_string(om2k_msgcode_vals, msg_type)); + } + + msgb_free(msg); + return rc; +} diff --git a/openbsc/src/abis_om2000_vty.c b/openbsc/src/abis_om2000_vty.c new file mode 100644 index 000000000..be843c516 --- /dev/null +++ b/openbsc/src/abis_om2000_vty.c @@ -0,0 +1,278 @@ +/* VTY interface for A-bis OM2000 */ + +/* (C) 2010-2011 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 <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/talloc.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> +#include <openbsc/abis_om2000.h> +#include <openbsc/vty.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> + +extern struct gsm_network *bsc_gsmnet; + +static struct cmd_node om2k_node = { + OM2K_NODE, + "%s(om2k)# ", + 1, +}; + +struct oml_node_state { + struct gsm_bts *bts; + struct abis_om2k_mo mo; +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +/* FIXME: auto-generate those strings from the value_string lists */ +#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" +#define OM2K_OBJCLASS_VTY_HELP "FIXME" + +DEFUN(om2k_class_inst, om2k_class_inst_cmd, + "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY + " <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OM2000 managed objects\n" + "Object Class\n" OM2K_OBJCLASS_VTY_HELP + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (bts->type != GSM_BTS_TYPE_RBS2000) { + vty_out(vty, "%% BTS %d not an Ericsson RBS%s", + bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; + +} + +DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, + "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" "Object Class\n" + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = atoi(argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; +} + +DEFUN(om2k_reset, om2k_reset_cmd, + "reset-command", + "Reset the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_start, om2k_start_cmd, + "start-request", + "Start the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_start_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_status, om2k_status_cmd, + "status-request", + "Get the MO Status\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_status_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_connect, om2k_connect_cmd, + "connect-command", + "Connect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disconnect, om2k_disconnect_cmd, + "disconnect-command", + "Disconnect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_enable, om2k_enable_cmd, + "enable-request", + "Enable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_enable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disable, om2k_disable_cmd, + "disable-request", + "Disable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_op_info, om2k_op_info_cmd, + "operational-info <0-1>", + "Set operational information\n") +{ + struct oml_node_state *oms = vty->index; + int oper = atoi(argv[0]); + + abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); + return CMD_SUCCESS; +} + +DEFUN(om2k_test, om2k_test_cmd, + "test-request", + "Test the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_test_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, + uint16_t icp2, uint8_t cont_idx) +{ + grp->icp1 = htons(icp1); + grp->icp2 = htons(icp2); + grp->cont_idx = cont_idx; +} + +DEFUN(om2k_is_conf_req, om2k_is_conf_req_cmd, + "is-conf-req", + "IS Configuration Request\n") +{ + struct oml_node_state *oms = vty->index; + struct om2k_is_conn_grp grps[6]; + + /* TRX 0 */ + om2k_fill_is_conn_grp(&grps[0], 512, 4, 4); + om2k_fill_is_conn_grp(&grps[1], 516, 8, 4); + om2k_fill_is_conn_grp(&grps[2], 520, 12, 4); + /* TRX 1 */ + om2k_fill_is_conn_grp(&grps[3], 524, 16, 4); + om2k_fill_is_conn_grp(&grps[4], 528, 20, 4); + om2k_fill_is_conn_grp(&grps[5], 532, 24, 4); + + abis_om2k_tx_is_conf_req(oms->bts, grps, ARRAY_SIZE(grps)); + return CMD_SUCCESS; +} + +int abis_om2k_vty_init(void) +{ + install_element(ENABLE_NODE, &om2k_class_inst_cmd); + install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); + install_node(&om2k_node, dummy_config_write); + + install_default(OM2K_NODE); + install_element(OM2K_NODE, &ournode_exit_cmd); + install_element(OM2K_NODE, &om2k_reset_cmd); + install_element(OM2K_NODE, &om2k_start_cmd); + install_element(OM2K_NODE, &om2k_status_cmd); + install_element(OM2K_NODE, &om2k_connect_cmd); + install_element(OM2K_NODE, &om2k_disconnect_cmd); + install_element(OM2K_NODE, &om2k_enable_cmd); + install_element(OM2K_NODE, &om2k_disable_cmd); + install_element(OM2K_NODE, &om2k_op_info_cmd); + install_element(OM2K_NODE, &om2k_test_cmd); + install_element(OM2K_NODE, &om2k_is_conf_req_cmd); + + return 0; +} diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c index dacaad382..14e5d8674 100644 --- a/openbsc/src/bsc_hack.c +++ b/openbsc/src/bsc_hack.c @@ -237,6 +237,7 @@ int main(int argc, char **argv) bts_model_unknown_init(); bts_model_bs11_init(); bts_model_nanobts_init(); + bts_model_rbs2k_init(); e1inp_init(); diff --git a/openbsc/src/bsc_vty.c b/openbsc/src/bsc_vty.c index 739d9aa28..3d7d69b81 100644 --- a/openbsc/src/bsc_vty.c +++ b/openbsc/src/bsc_vty.c @@ -2702,6 +2702,7 @@ int bsc_vty_init(void) install_element(ENABLE_NODE, &pdch_act_cmd); abis_nm_vty_init(); + abis_om2k_vty_init(); e1inp_vty_init(); bsc_vty_init_extra(); diff --git a/openbsc/src/bts_ericsson_rbs2000.c b/openbsc/src/bts_ericsson_rbs2000.c new file mode 100644 index 000000000..5ad47b292 --- /dev/null +++ b/openbsc/src/bts_ericsson_rbs2000.c @@ -0,0 +1,148 @@ +/* Ericsson RBS-2xxx specific code */ + +/* (C) 2011 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 <sys/types.h> + +#include <osmocore/tlv.h> + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/abis_om2000.h> +#include <openbsc/e1_input.h> +#include <openbsc/signal.h> + +#include "input/lapd.h" + +static struct gsm_bts_model model_rbs2k = { + .type = GSM_BTS_TYPE_RBS2000, + .name = "rbs2000", + .oml_rcvmsg = &abis_om2k_rcvmsg, +}; + +static void bootstrap_om_rbs2k(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); + abis_om2k_tx_start_req(bts, &om2k_mo_cf); + /* FIXME */ +} + +static int shutdown_om(struct gsm_bts *bts) +{ + /* FIXME */ + return 0; +} + + +/* Tell LAPD to start start the SAP (send SABM requests) for all signalling + * timeslots in this line */ +static void start_sabm_in_line(struct e1inp_line *line, int start) +{ + 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 (start) + lapd_sap_start(ts->driver.dahdi.lapd, link->tei, link->sapi); + else + lapd_sap_stop(ts->driver.dahdi.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_GLOBAL) + return 0; + + switch (signal) { + case S_GLOBAL_BTS_CLOSE_OM: + bts = signal_data; + if (bts->type == GSM_BTS_TYPE_RBS2000) + 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_INPUT) + return 0; + + switch (signal) { + case S_INP_TEI_UP: + switch (isd->link_type) { + case E1INP_SIGN_OML: + if (isd->trx->bts->type == GSM_BTS_TYPE_RBS2000) { + bootstrap_om_rbs2k(isd->trx->bts); + } + break; + } + break; + case S_INP_LINE_INIT: + /* Right now Ericsson RBS are only supported on DAHDI */ + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 1); + break; + case S_INP_LINE_ALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 0); + break; + case S_INP_LINE_NOALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 1); + break; + } + + return 0; +} + +int bts_model_rbs2k_init(void) +{ + model_rbs2k.features.data = &model_rbs2k._features_data[0]; + model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); + + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); + + register_signal_handler(SS_INPUT, inp_sig_cb, NULL); + register_signal_handler(SS_GLOBAL, gbl_sig_cb, NULL); + + return gsm_bts_model_register(&model_rbs2k); +} diff --git a/openbsc/src/common_vty.c b/openbsc/src/common_vty.c index 695d4c37b..25ab3c0a8 100644 --- a/openbsc/src/common_vty.c +++ b/openbsc/src/common_vty.c @@ -67,6 +67,7 @@ enum node_type bsc_vty_go_parent(struct vty *vty) } break; case OML_NODE: + case OM2K_NODE: vty->node = ENABLE_NODE; talloc_free(vty->index); vty->index = NULL; @@ -145,6 +146,7 @@ gDEFUN(ournode_exit, vty->index = NULL; break; case OML_NODE: + case OM2K_NODE: vty->node = ENABLE_NODE; talloc_free(vty->index); vty->index = NULL; @@ -196,6 +198,7 @@ int bsc_vty_is_config_node(struct vty *vty, int node) switch (node) { /* add items that are not config */ case OML_NODE: + case OM2K_NODE: case SUBSCR_NODE: case CONFIG_NODE: return 0; diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c index dd819926f..db290cbae 100644 --- a/openbsc/src/e1_config.c +++ b/openbsc/src/e1_config.c @@ -100,6 +100,11 @@ int e1_reconfig_trx(struct gsm_bts_trx *trx) } sign_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN); + if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { + /* FIXME: where to put the reference of the per-TRX OML? */ + e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, + trx->rsl_tei, SAPI_OML); + } rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, trx->rsl_tei, SAPI_RSL); if (!rsl_link) { diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c index 5ae525899..29fa16eb1 100644 --- a/openbsc/src/gsm_data.c +++ b/openbsc/src/gsm_data.c @@ -412,6 +412,7 @@ static const struct value_string bts_types[] = { { GSM_BTS_TYPE_UNKNOWN, "unknown" }, { GSM_BTS_TYPE_BS11, "bs11" }, { GSM_BTS_TYPE_NANOBTS, "nanobts" }, + { GSM_BTS_TYPE_RBS2000, "rbs2000" }, { 0, NULL } }; diff --git a/openbsc/src/input/dahdi.c b/openbsc/src/input/dahdi.c index c142b8ae4..56851eed4 100644 --- a/openbsc/src/input/dahdi.c +++ b/openbsc/src/input/dahdi.c @@ -69,6 +69,7 @@ static const struct value_string dahdi_evt_names[] = { static void handle_dahdi_exception(struct e1inp_ts *ts) { int rc, evt; + struct input_signal_data isd; rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt); if (rc < 0) @@ -78,12 +79,16 @@ static void handle_dahdi_exception(struct e1inp_ts *ts) ts->line->num, ts->line->name, ts->num, get_value_string(dahdi_evt_names, evt)); + isd.line = ts->line; + switch (evt) { case DAHDI_EVENT_ALARM: - /* FIXME: we should notify the code that the line is gone */ + /* we should notify the code that the line is gone */ + dispatch_signal(SS_INPUT, S_INP_LINE_ALARM, &isd); break; case DAHDI_EVENT_NOALARM: - /* FIXME: alarm has gone, we should re-start the SABM requests */ + /* alarm has gone, we should re-start the SABM requests */ + dispatch_signal(SS_INPUT, S_INP_LINE_NOALARM, &isd); break; } } diff --git a/openbsc/src/input/lapd.c b/openbsc/src/input/lapd.c index 0c3b4698f..9bfc2cb4a 100644 --- a/openbsc/src/input/lapd.c +++ b/openbsc/src/input/lapd.c @@ -1,9 +1,9 @@ /* OpenBSC minimal LAPD implementation */ /* (C) 2009 by oystein@homelien.no - * (C) 2011 by Harald Welte <laforge@gnumonks.org> * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com> + * (C) 2011 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -23,6 +23,12 @@ * */ +/* TODO: + * detect RR timeout and set SAP state back to SABM_RETRANSMIT + * use of value_string + * further code cleanup (spaghetti) + */ + #include <stdio.h> #include <string.h> #include <assert.h> @@ -33,13 +39,14 @@ #include <osmocore/linuxlist.h> #include <osmocore/talloc.h> #include <osmocore/msgb.h> +#include <osmocore/timer.h> #include <openbsc/debug.h> +#define SABM_INTERVAL 0, 300000 + typedef enum { LAPD_TEI_NONE = 0, - LAPD_TEI_ASSIGNED, - LAPD_TEI_ACTIVE, } lapd_tei_state; @@ -93,10 +100,24 @@ const char *lapd_cmd_types[] = { }; +enum lapd_sap_state { + SAP_STATE_INACTIVE, + SAP_STATE_SABM_RETRANS, + SAP_STATE_ACTIVE, +}; + +const char *lapd_sap_states[] = { + "INACTIVE", + "SABM_RETRANS", + "ACTIVE", +}; + const char *lapd_msg_types = "?ISU"; +/* structure representing an allocated TEI within a LAPD instance */ struct lapd_tei { struct llist_head list; + struct lapd_instance *li; uint8_t tei; /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */ @@ -104,6 +125,19 @@ struct lapd_tei { int va; /* last acked by peer */ int vr; /* next expected to be received */ lapd_tei_state state; + + struct llist_head sap_list; +}; + +/* Structure representing a SAP within a TEI. We use this for TE-mode to + * re-transmit SABM */ +struct lapd_sap { + struct llist_head list; + struct lapd_tei *tei; + uint8_t sapi; + enum lapd_sap_state state; + + struct timer_list sabme_timer; /* timer to re-transmit SABM message */ }; /* 3.5.2.2 Send state variable V(S) @@ -142,6 +176,7 @@ struct lapd_tei { * correctly received all I frames numbered up to and including N(R) − 1. */ +/* Resolve TEI structure from given numeric TEI */ static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) { struct lapd_tei *lt; @@ -155,28 +190,87 @@ static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) static void lapd_tei_set_state(struct lapd_tei *teip, int newstate) { - DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei, + DEBUGP(DMI, "state change on TEI %d: %s -> %s\n", teip->tei, lapd_tei_states[teip->state], lapd_tei_states[newstate]); teip->state = newstate; }; - -int lapd_tei_alloc(struct lapd_instance *li, uint8_t tei) +/* Allocate a new TEI */ +struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei) { struct lapd_tei *teip; teip = talloc_zero(li, struct lapd_tei); if (!teip) - return -ENOMEM; + return NULL; + teip->li = li; teip->tei = tei; llist_add(&teip->list, &li->tei_list); + INIT_LLIST_HEAD(&teip->sap_list); lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); - return 0; + return teip; +} + +/* Find a SAP within a given TEI */ +static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi) +{ + struct lapd_sap *sap; + + llist_for_each_entry(sap, &teip->sap_list, list) { + if (sap->sapi == sapi) + return sap; + } + + return NULL; } +static void sabme_timer_cb(void *_sap); + +/* Allocate a new SAP within a given TEI */ +static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi) +{ + struct lapd_sap *sap = talloc_zero(teip, struct lapd_sap); + + LOGP(DMI, LOGL_INFO, "Allocating SAP for SAPI=%u / TEI=%u\n", + sapi, teip->tei); + + sap->sapi = sapi; + sap->tei = teip; + sap->sabme_timer.cb = &sabme_timer_cb; + sap->sabme_timer.data = sap; + + llist_add(&sap->list, &teip->sap_list); + + return sap; +} + +static void lapd_sap_set_state(struct lapd_tei *teip, uint8_t sapi, + enum lapd_sap_state newstate) +{ + struct lapd_sap *sap = lapd_sap_find(teip, sapi); + if (!sap) + return; + + DEBUGP(DMI, "state change on TEI %u / SAPI %u: %s -> %s\n", teip->tei, + sapi, lapd_sap_states[sap->state], lapd_sap_states[newstate]); + switch (sap->state) { + case SAP_STATE_SABM_RETRANS: + if (newstate != SAP_STATE_SABM_RETRANS) + bsc_del_timer(&sap->sabme_timer); + break; + default: + if (newstate == SAP_STATE_SABM_RETRANS) + bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); + break; + } + + sap->state = newstate; +}; + +/* Input function into TEI manager */ static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) { uint8_t entity = data[0]; @@ -214,6 +308,7 @@ static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) }; }; +/* General input function for any data received for this LAPD instance */ uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len, int *ilen, lapd_mph_type *prim) { @@ -391,6 +486,7 @@ uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len teip->vr = 0; teip->va = 0; lapd_tei_set_state(teip, LAPD_TEI_ACTIVE); + lapd_sap_set_state(teip, sapi, SAP_STATE_ACTIVE); *prim = LAPD_MPH_ACTIVATE_IND; break; case LAPD_CMD_RR: @@ -478,7 +574,8 @@ uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len return NULL; }; -int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +/* low-level function to send a single SABM message */ +static int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) { struct msgb *msg = msgb_alloc_headroom(1024, 128, "LAPD SABM"); if (!msg) @@ -486,10 +583,6 @@ int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) DEBUGP(DMI, "Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi); - /* make sure we know the TEI at the time the response comes in */ - if (!teip_from_tei(li, tei)) - lapd_tei_alloc(li, tei); - msgb_put_u8(msg, (sapi << 2) | (li->network_side ? 2 : 0)); msgb_put_u8(msg, (tei << 1) | 1); msgb_put_u8(msg, 0x7F); @@ -501,6 +594,61 @@ int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) return 0; } +/* timer call-back function for SABM re-transmission */ +static void sabme_timer_cb(void *_sap) +{ + struct lapd_sap *sap = _sap; + + lapd_send_sabm(sap->tei->li, sap->tei->tei, sap->sapi); + + if (sap->state == SAP_STATE_SABM_RETRANS) + bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); +} + +/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +{ + struct lapd_sap *sap; + struct lapd_tei *teip; + + teip = teip_from_tei(li, tei); + if (!teip) + teip = lapd_tei_alloc(li, tei); + + sap = lapd_sap_find(teip, sapi); + if (sap) + return -EEXIST; + + sap = lapd_sap_alloc(teip, sapi); + + lapd_sap_set_state(teip, sapi, SAP_STATE_SABM_RETRANS); + + return 0; +} + +/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +{ + struct lapd_tei *teip; + struct lapd_sap *sap; + + teip = teip_from_tei(li, tei); + if (!teip) + return -ENODEV; + + sap = lapd_sap_find(teip, sapi); + if (!sap) + return -ENODEV; + + lapd_sap_set_state(teip, sapi, SAP_STATE_INACTIVE); + + llist_del(&sap->list); + talloc_free(sap); + + return 0; +} + +/* Transmit Data (I-Frame) on the given LAPD Instance / TEI / SAPI */ void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, uint8_t *data, unsigned int len) { @@ -530,6 +678,7 @@ void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, li->transmit_cb(buf, len, li->cbdata); }; +/* Allocate a new LAPD instance */ struct lapd_instance *lapd_instance_alloc(int network_side, void (*tx_cb)(uint8_t *data, int len, void *cbdata), void *cbdata) diff --git a/openbsc/src/input/lapd.h b/openbsc/src/input/lapd.h index 724d0cff8..fd11edaa3 100644 --- a/openbsc/src/input/lapd.h +++ b/openbsc/src/input/lapd.h @@ -36,6 +36,11 @@ struct lapd_instance *lapd_instance_alloc(int network_side, void (*tx_cb)(uint8_t *data, int len, void *cbdata), void *cbdata); -int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi); + +/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi); + +/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi); #endif /* OPENBSC_LAPD_H */ |