#ifdef FEMTOBTS_API_VERSION
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
#define SuperFemto_Prim_t FemtoBts_Prim_t
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
#define HW_SYSMOBTS_V1 1
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
#endif
extern int initialize_layer1(uint32_t dsp_flags);
extern int print_system_info();
extern int activate_rf_frontend(int clock_source, int clock_cor);
extern int power_scan(int band, int arfcn, int duration, float *mean_rssi);
extern int follow_sch(int band, int arfcn, int calib, int reference, HANDLE *layer1);
extern int follow_bch(HANDLE layer1);
extern int find_bsic(void);
extern int set_tsc_from_bsic(HANDLE layer1, int bsic);
extern int set_clock_cor(int clock_corr, int calib, int source);
extern int rf_clock_info(int *clkErr, int *clkErrRes);
extern int mph_close(HANDLE layer1);
extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
extern int follow_bcch(HANDLE layer1);
extern int follow_pch(HANDLE layer1);
extern int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sapi);
#endif
osmo-bts-0.4.0/contrib/sysmobts-mgr.service 0000664 0000000 0000000 00000000314 12600264262 0020744 0 ustar 00root root 0000000 0000000 [Unit]
Description=sysmocom sysmoBTS manager
[Service]
Type=simple
ExecStart=/usr/bin/sysmobts-mgr -ns -c /etc/osmocom/sysmobts-mgr.cfg
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
osmo-bts-0.4.0/contrib/sysmobts.init 0000775 0000000 0000000 00000001255 12600264262 0017474 0 ustar 00root root 0000000 0000000 #!/bin/sh
### BEGIN INIT INFO
# Provides: sysmobts
# Required-Start:
# Required-Stop: $local_fs
# Default-Start: 5
# Default-Stop: 0 6
# Short-Description: Start screen session with sysmobts software
# Description:
### END INIT INFO
case "$1" in
start)
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts -S sysmobts
;;
stop)
/usr/bin/screen -d -r sysmobts -X quit
exit 1
;;
restart|reload|force-reload)
exit 0
;;
show)
;;
*)
echo "Usage: sysmobts {start|stop|show|reload|restart}" >&2
exit 1
;;
esac
osmo-bts-0.4.0/contrib/sysmobts.service 0000664 0000000 0000000 00000001145 12600264262 0020164 0 ustar 00root root 0000000 0000000 [Unit]
Description=sysmocom sysmoBTS
[Service]
Type=simple
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg -M
ExecStopPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
ExecStopPost=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0 ; sleep 3s; cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0; sleep 1s'
Restart=always
RestartSec=2
RestartPreventExitStatus=1
# The msg queues must be read fast enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target
osmo-bts-0.4.0/doc/ 0000775 0000000 0000000 00000000000 12600264262 0014023 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/doc/control_interface.txt 0000664 0000000 0000000 00000003407 12600264262 0020270 0 ustar 00root root 0000000 0000000 The osmo-bts control interface is currently supporting the following operations:
h2. generic
h3. trx.0.thermal-attenuation
The idea of this paramter is to attenuate the system output power as part of
thermal management. In some cases the PA might be passing a critical level,
so an external control process can use this attribute to reduce the system
output power.
Please note that all values in the context of transmit power calculation
are integers in milli-dB (1/10000 bel), so the below example is setting
the attenuation at 3 dB:
bsc_control.py -d localhost -p 4238 -s trx.0.thermal-attenuation 3000
Got message: SET_REPLY 1 trx.0.thermal-attenuation 3000
bsc_control.py -d localhost -p 4238 -g trx.0.thermal-attenuation
Got message: GET_REPLY 1 trx.0.thermal-attenuation 3000
h2. sysmobts specific
h3. trx.0.clock-info
obtain information on the current clock status:
bsc_control.py -d localhost -p 4238 -g trx.0.clock-info
Got message: GET_REPLY 1 trx.0.clock-info -100,ocxo,0,0,gps
which is to be interpreted as:
* current clock correction value is -100 ppb
* current clock source is OCXO
* deviation between clock source and calibration source is 0 ppb
* resolution of clock error measurement is 0 ppt (0 means no result yet)
* current calibration source is GPS
When this attribute is set, any value passed on is discarded, but the clock
calibration process is re-started.
h3. trx.0.clock-correction
This attribute can get and set the current clock correction value:
bsc_control.py -d localhost -p 4238 -g trx.0.clock-correction
Got message: GET_REPLY 1 trx.0.clock-correction -100
bsc_control.py -d localhost -p 4238 -s trx.0.clock-correction -- -99
Got message: SET_REPLY 1 trx.0.clock-correction success
osmo-bts-0.4.0/doc/examples/ 0000775 0000000 0000000 00000000000 12600264262 0015641 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/doc/examples/osmo-bts.cfg 0000664 0000000 0000000 00000000741 12600264262 0020067 0 ustar 00root root 0000000 0000000 !
! OsmoBTS () configuration saved from vty
!!
!
log stderr
logging color 0
logging timestamp 0
logging level all everything
logging level rsl info
logging level oml info
logging level rll notice
logging level rr notice
logging level meas notice
logging level pag info
logging level l1c info
logging level l1p info
logging level dsp debug
logging level abis notice
!
line vty
no login
!
bts 0
band 1800
ipa unit-id 1234 0
oml remote-ip 192.168.100.11
osmo-bts-0.4.0/doc/examples/sysmobts-mgr.cfg 0000664 0000000 0000000 00000000740 12600264262 0020771 0 ustar 00root root 0000000 0000000 !
! SysmoMgr (0.3.0.141-33e5) configuration saved from vty
!!
!
log stderr
logging filter all 1
logging color 1
logging timestamp 0
logging level all everything
logging level temp info
logging level fw info
logging level find info
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
!
line vty
no login
!
sysmobts-mgr
osmo-bts-0.4.0/git-version-gen 0000775 0000000 0000000 00000012500 12600264262 0016217 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Print a version string.
scriptversion=2010-01-28.01
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
#
# 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 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
# It may be run two ways:
# - from a git repository in which the "git describe" command below
# produces useful output (thus requiring at least one signed tag)
# - from a non-git-repo directory containing a .tarball-version file, which
# presumes this script is invoked like "./git-version-gen .tarball-version".
# In order to use intra-version strings in your project, you will need two
# separate generated version string files:
#
# .tarball-version - present only in a distribution tarball, and not in
# a checked-out repository. Created with contents that were learned at
# the last time autoconf was run, and used by git-version-gen. Must not
# be present in either $(srcdir) or $(builddir) for git-version-gen to
# give accurate answers during normal development with a checked out tree,
# but must be present in a tarball when there is no version control system.
# Therefore, it cannot be used in any dependencies. GNUmakefile has
# hooks to force a reconfigure at distribution time to get the value
# correct, without penalizing normal development with extra reconfigures.
#
# .version - present in a checked-out repository and in a distribution
# tarball. Usable in dependencies, particularly for files that don't
# want to depend on config.h but do want to track version changes.
# Delete this file prior to any autoconf run where you want to rebuild
# files to pick up a version string change; and leave it stale to
# minimize rebuild time after unrelated changes to configure sources.
#
# It is probably wise to add these two files to .gitignore, so that you
# don't accidentally commit either generated file.
#
# Use the following line in your configure.ac, so that $(VERSION) will
# automatically be up-to-date each time configure is run (and note that
# since configure.ac no longer includes a version string, Makefile rules
# should not depend on configure.ac for version updates).
#
# AC_INIT([GNU project],
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
# [bug-project@example])
#
# Then use the following lines in your Makefile.am, so that .version
# will be present for dependencies, and so that .tarball-version will
# exist in distribution tarballs.
#
# BUILT_SOURCES = $(top_srcdir)/.version
# $(top_srcdir)/.version:
# echo $(VERSION) > $@-t && mv $@-t $@
# dist-hook:
# echo $(VERSION) > $(distdir)/.tarball-version
case $# in
1) ;;
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
esac
tarball_version_file=$1
nl='
'
# First see if there is a tarball-only version file.
# then try "git describe", then default.
if test -f $tarball_version_file
then
v=`cat $tarball_version_file` || exit 1
case $v in
*$nl*) v= ;; # reject multi-line output
[0-9]*) ;;
*) v= ;;
esac
test -z "$v" \
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
fi
if test -n "$v"
then
: # use $v
elif
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& case $v in
[0-9]*) ;;
v[0-9]*) ;;
*) (exit 1) ;;
esac
then
# Is this a new git that lists number of commits since the last
# tag or the previous older version that did not?
# Newer: v6.10-77-g0f8faeb
# Older: v6.10-g0f8faeb
case $v in
*-*-*) : git describe is okay three part flavor ;;
*-*)
: git describe is older two part flavor
# Recreate the number of commits and rewrite such that the
# result is the same as if we were using the newer version
# of git describe.
vtag=`echo "$v" | sed 's/-.*//'`
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
;;
esac
# Change the first '-' to a '.', so version-comparing tools work properly.
# Remove the "g" in git describe's output string, to save a byte.
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
else
v=UNKNOWN
fi
v=`echo "$v" |sed 's/^v//'`
# Don't declare a version "dirty" merely because a time stamp has changed.
git status > /dev/null 2>&1
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
case "$dirty" in
'') ;;
*) # Append the suffix only if there isn't one already.
case $v in
*-dirty) ;;
*) v="$v-dirty" ;;
esac ;;
esac
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
echo "$v" | tr -d '\012'
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:
osmo-bts-0.4.0/include/ 0000775 0000000 0000000 00000000000 12600264262 0014701 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/include/Makefile.am 0000664 0000000 0000000 00000000070 12600264262 0016732 0 ustar 00root root 0000000 0000000 SUBDIRS = osmo-bts
noinst_HEADERS = openbsc/gsm_data.h
osmo-bts-0.4.0/include/openbsc/ 0000775 0000000 0000000 00000000000 12600264262 0016332 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/include/openbsc/gsm_data.h 0000777 0000000 0000000 00000000000 12600264262 0024163 2../osmo-bts/gsm_data.h ustar 00root root 0000000 0000000 osmo-bts-0.4.0/include/osmo-bts/ 0000775 0000000 0000000 00000000000 12600264262 0016444 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/include/osmo-bts/Makefile.am 0000664 0000000 0000000 00000000353 12600264262 0020501 0 ustar 00root root 0000000 0000000 noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
power_control.h
osmo-bts-0.4.0/include/osmo-bts/abis.h 0000664 0000000 0000000 00000001114 12600264262 0017530 0 ustar 00root root 0000000 0000000 #ifndef _ABIS_H
#define _ABIS_H
#include
#include
#include
#define OML_RETRY_TIMER 5
#define OML_PING_TIMER 20
enum {
LINK_STATE_IDLE = 0,
LINK_STATE_RETRYING,
LINK_STATE_CONNECTING,
LINK_STATE_CONNECT,
};
void abis_init(struct gsm_bts *bts);
struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
char *model_name);
int abis_oml_sendmsg(struct msgb *msg);
int abis_bts_rsl_sendmsg(struct msgb *msg);
uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link);
#endif /* _ABIS_H */
osmo-bts-0.4.0/include/osmo-bts/amr.h 0000664 0000000 0000000 00000000662 12600264262 0017400 0 ustar 00root root 0000000 0000000 #ifndef _OSMO_BTS_AMR_H
#define _OSMO_BTS_AMR_H
#include
#define AMR_TOC_QBIT 0x04
#define AMR_CMR_NONE 0xF
void amr_log_mr_conf(int ss, int logl, const char *pfx,
struct amr_multirate_conf *amr_mrc);
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
const uint8_t *mr_conf, unsigned int len);
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan);
#endif /* _OSMO_BTS_AMR_H */
osmo-bts-0.4.0/include/osmo-bts/bts.h 0000664 0000000 0000000 00000002605 12600264262 0017410 0 ustar 00root root 0000000 0000000 #ifndef _BTS_H
#define _BTS_H
#include
enum bts_global_status {
BTS_STATUS_RF_ACTIVE,
BTS_STATUS_RF_MUTE,
BTS_STATUS_LAST,
};
extern void *tall_bts_ctx;
int bts_init(struct gsm_bts *bts);
void bts_shutdown(struct gsm_bts *bts, const char *reason);
struct gsm_bts *create_bts(uint8_t num_trx, char *id);
int create_ms(struct gsm_bts_trx *trx, int maskc, uint8_t *maskv_tx,
uint8_t *maskv_rx);
void destroy_bts(struct gsm_bts *bts);
int work_bts(struct gsm_bts *bts);
int bts_link_estab(struct gsm_bts *bts);
int trx_link_estab(struct gsm_bts_trx *trx);
void bts_new_si(void *arg);
void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb);
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
void bts_update_agch_max_queue_length(struct gsm_bts *bts);
int bts_agch_max_queue_length(int T, int bcch_conf);
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
int is_ag_res);
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time);
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan);
int lchan_init_lapdm(struct gsm_lchan *lchan);
void load_timer_start(struct gsm_bts *bts);
void bts_update_status(enum bts_global_status which, int on);
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx);
struct gsm_time *get_time(struct gsm_bts *bts);
#endif /* _BTS_H */
osmo-bts-0.4.0/include/osmo-bts/bts_model.h 0000664 0000000 0000000 00000002466 12600264262 0020575 0 ustar 00root root 0000000 0000000 #ifndef BTS_MODEL_H
#define BTS_MODEL_H
#include
#include
#include
#include
/* BTS model specific functions needed by the common code */
int bts_model_init(struct gsm_bts *bts);
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
void *obj);
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int obj_kind, void *obj);
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj);
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, uint8_t adm_state);
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
int bts_model_trx_close(struct gsm_bts_trx *trx);
int bts_model_vty_init(struct gsm_bts *bts);
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
int bts_model_oml_estab(struct gsm_bts *bts);
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan);
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
void bts_model_abis_close(struct gsm_bts *bts);
#endif
osmo-bts-0.4.0/include/osmo-bts/cbch.h 0000664 0000000 0000000 00000001053 12600264262 0017513 0 ustar 00root root 0000000 0000000 #pragma once
#include
#include
#include
#include
/* incoming SMS broadcast command from RSL */
int bts_process_smscb_cmd(struct gsm_bts *bts,
struct rsl_ie_cb_cmd_type cmd_type,
uint8_t msg_len, const uint8_t *msg);
/* call-back from bts model specific code when it wants to obtain a CBCH
* block for a given gsm_time. outbuf must have 23 bytes of space. */
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time);
osmo-bts-0.4.0/include/osmo-bts/control_if.h 0000664 0000000 0000000 00000000174 12600264262 0020755 0 ustar 00root root 0000000 0000000 #pragma once
int bts_ctrl_cmds_install(struct gsm_bts *bts);
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts);
osmo-bts-0.4.0/include/osmo-bts/gsm_data.h 0000664 0000000 0000000 00000006233 12600264262 0020400 0 ustar 00root root 0000000 0000000 #ifndef _GSM_DATA_H
#define _GSM_DATA_H
#include
#include
#include
#include
#include
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE 999999
#define GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT 41
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
struct pcu_sock_state;
struct smscb_msg;
struct gsm_network {
struct llist_head bts_list;
unsigned int num_bts;
uint16_t mcc, mnc;
struct pcu_sock_state *pcu_state;
};
/* data structure for BTS related data specific to the BTS role */
struct gsm_bts_role_bts {
struct {
/* Interference Boundaries for OML */
int16_t boundary[6];
uint8_t intave;
} interference;
unsigned int t200_ms[7];
unsigned int t3105_ms;
struct {
uint8_t overload_period;
struct {
/* Input parameters from OML */
uint8_t load_ind_thresh; /* percent */
uint8_t load_ind_period; /* seconds */
/* Internal data */
struct osmo_timer_list timer;
unsigned int pch_total;
unsigned int pch_used;
} ccch;
struct {
/* Input parameters from OML */
int16_t busy_thresh; /* in dBm */
uint16_t averaging_slots;
/* Internal data */
unsigned int total; /* total nr */
unsigned int busy; /* above busy_thresh */
unsigned int access; /* access bursts */
} rach;
} load;
uint8_t ny1;
uint8_t max_ta;
/* AGCH queuing */
struct llist_head agch_queue;
int agch_queue_length;
int agch_max_queue_length;
int agch_queue_thresh_level; /* Cleanup threshold in percent of max len */
int agch_queue_low_level; /* Low water mark in percent of max len */
int agch_queue_high_level; /* High water mark in percent of max len */
/* TODO: Use a rate counter group instead */
uint64_t agch_queue_dropped_msgs;
uint64_t agch_queue_merged_msgs;
uint64_t agch_queue_rejected_msgs;
uint64_t agch_queue_agch_msgs;
uint64_t agch_queue_pch_msgs;
struct paging_state *paging_state;
char *bsc_oml_host;
unsigned int rtp_jitter_buf_ms;
struct {
uint8_t ciphers; /* flags A5/1==0x1, A5/2==0x2, A5/3==0x4 */
} support;
struct {
uint8_t tc4_ctr;
} si;
struct gsm_time gsm_time;
uint8_t radio_link_timeout;
int ul_power_target; /* Uplink Rx power target */
/* used by the sysmoBTS to adjust band */
uint8_t auto_band;
struct {
struct llist_head queue; /* list of struct smscb_msg */
struct smscb_msg *cur_msg; /* current SMS-CB */
} smscb_state;
};
enum lchan_ciph_state {
LCHAN_CIPH_NONE,
LCHAN_CIPH_RX_REQ,
LCHAN_CIPH_RX_CONF,
LCHAN_CIPH_RXTX_REQ,
LCHAN_CIPH_RX_CONF_TX_REQ,
LCHAN_CIPH_RXTX_CONF,
};
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
#include "openbsc/gsm_data_shared.h"
struct femtol1_hdl;
static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
{
return trx->role_bts.l1h;
}
struct trx_l1h;
static inline struct trx_l1h *trx_l1h_hdl(struct gsm_bts_trx *trx)
{
return trx->role_bts.l1h;
}
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
/* cipher code */
#define CIPHER_A5(x) (1 << (x-1))
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher);
#endif /* _GSM_DATA_H */
osmo-bts-0.4.0/include/osmo-bts/handover.h 0000664 0000000 0000000 00000000377 12600264262 0020432 0 ustar 00root root 0000000 0000000 #pragma once
enum {
HANDOVER_NONE = 0,
HANDOVER_ENABLED,
HANDOVER_WAIT_FRAME,
};
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay);
void handover_frame(struct gsm_lchan *lchan);
void handover_reset(struct gsm_lchan *lchan);
osmo-bts-0.4.0/include/osmo-bts/l1sap.h 0000664 0000000 0000000 00000005261 12600264262 0017641 0 ustar 00root root 0000000 0000000 #ifndef L1SAP_H
#define L1SAP_H
/* timeslot and subslot from chan_nr */
#define L1SAP_CHAN2TS(chan_nr) (chan_nr & 7)
#define L1SAP_CHAN2SS_TCHH(chan_nr) ((chan_nr >> 3) & 1)
#define L1SAP_CHAN2SS_SDCCH4(chan_nr) ((chan_nr >> 3) & 3)
#define L1SAP_CHAN2SS_SDCCH8(chan_nr) ((chan_nr >> 3) & 7)
/* logical channel from chan_nr + link_id */
#define L1SAP_IS_LINK_SACCH(link_id) ((link_id & 0xC0) == 0x40)
#define L1SAP_IS_CHAN_TCHF(chan_nr) ((chan_nr & 0xf8) == 0x08)
#define L1SAP_IS_CHAN_TCHH(chan_nr) ((chan_nr & 0xf0) == 0x10)
#define L1SAP_IS_CHAN_SDCCH4(chan_nr) ((chan_nr & 0xe0) == 0x20)
#define L1SAP_IS_CHAN_SDCCH8(chan_nr) ((chan_nr & 0xc0) == 0x40)
#define L1SAP_IS_CHAN_BCCH(chan_nr) ((chan_nr & 0xf8) == 0x80)
#define L1SAP_IS_CHAN_RACH(chan_nr) ((chan_nr & 0xf8) == 0x88)
#define L1SAP_IS_CHAN_AGCH_PCH(chan_nr) ((chan_nr & 0xf8) == 0x90)
/* rach type from ra */
#define L1SAP_IS_PACKET_RACH(ra) ((ra & 0xf0) == 0x70)
/* CCCH block from frame number */
#define L1SAP_FN2CCCHBLOCK(fn) ((fn % 51) / 5 - 1)
/* PTCH layout from frame number */
#define L1SAP_FN2MACBLOCK(fn) ((fn % 52) / 4)
#define L1SAP_FN2PTCCHBLOCK(fn) ((fn / 52) & 7)
#define L1SAP_IS_PTCCH(fn) ((fn % 52) == 12)
/* subslot from any chan_nr */
static inline uint8_t l1sap_chan2ss(uint8_t chan_nr)
{
if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
return L1SAP_CHAN2SS_SDCCH8(chan_nr);
if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
return L1SAP_CHAN2SS_SDCCH4(chan_nr);
if (L1SAP_IS_CHAN_TCHH(chan_nr))
return L1SAP_CHAN2SS_TCHH(chan_nr);
return 0;
}
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data */
struct msgb *l1sap_msgb_alloc(unsigned int l2_len);
/* any L1 prim received from bts model */
int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
/* pcu (socket interface) sends us a data request primitive */
int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
/* call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len);
/* channel control */
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp);
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr);
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr);
extern const struct value_string gsmtap_sapi_names[];
extern struct gsmtap_inst *gsmtap;
extern uint32_t gsmtap_sapi_mask;
extern uint8_t gsmtap_sapi_acch;
#define msgb_l1sap_prim(msg) ((struct osmo_phsap_prim *)(msg)->l1h)
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len);
#endif /* L1SAP_H */
osmo-bts-0.4.0/include/osmo-bts/logging.h 0000664 0000000 0000000 00000000506 12600264262 0020244 0 ustar 00root root 0000000 0000000 #ifndef _LOGGING_H
#define _LOGGING_H
#define DEBUG
#include
enum {
DRSL,
DOML,
DRLL,
DRR,
DMEAS,
DPAG,
DL1C,
DL1P,
DDSP,
DPCU,
DHO,
DTRX,
DLOOP,
DABIS,
DRTP,
DSUM,
};
extern const struct log_info bts_log_info;
int bts_log_init(const char *category_mask);
#endif /* _LOGGING_H */
osmo-bts-0.4.0/include/osmo-bts/measurement.h 0000664 0000000 0000000 00000000477 12600264262 0021152 0 ustar 00root root 0000000 0000000 #ifndef OSMO_BTS_MEAS_H
#define OSMO_BTS_MEAS_H
int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm);
int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn);
/* build the 3 byte RSL uplinke measurement IE content */
int lchan_build_rsl_ul_meas(struct gsm_lchan *, uint8_t *buf);
#endif
osmo-bts-0.4.0/include/osmo-bts/msg_utils.h 0000664 0000000 0000000 00000000575 12600264262 0020632 0 ustar 00root root 0000000 0000000 /*
* Routines to check the structurally integrity of messages
*/
#pragma once
struct msgb;
/**
* Classification of OML message. ETSI for plain GSM 12.21
* messages and IPA/Osmo for manufacturer messages.
*/
enum {
OML_MSG_TYPE_ETSI,
OML_MSG_TYPE_IPA,
OML_MSG_TYPE_OSMO,
};
int msg_verify_ipa_structure(struct msgb *msg);
int msg_verify_oml_structure(struct msgb *msg);
osmo-bts-0.4.0/include/osmo-bts/oml.h 0000664 0000000 0000000 00000002427 12600264262 0017411 0 ustar 00root root 0000000 0000000 #ifndef _OML_H
#define _OML_H
struct gsm_bts;
struct gsm_abis_mo;
struct msgb;
int oml_init(void);
int down_oml(struct gsm_bts *bts, struct msgb *msg);
struct msgb *oml_msgb_alloc(void);
int oml_send_msg(struct msgb *msg, int is_mauf);
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
int oml_mo_statechg_ack(struct gsm_abis_mo *mo);
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
/* Change the state and send STATE CHG REP */
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
/* First initialization of MO, does _not_ generate state changes */
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state);
/* Update admin state and send ACK/NACK */
int oml_mo_rf_lock_chg(struct gsm_abis_mo *mo, uint8_t mute_state[8],
int success);
/* Transmit STATE CHG REP even if there was no state change */
int oml_tx_state_changed(struct gsm_abis_mo *mo);
int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo);
int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause);
int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type,
uint8_t cause);
#endif // _OML_H */
osmo-bts-0.4.0/include/osmo-bts/paging.h 0000664 0000000 0000000 00000003311 12600264262 0020060 0 ustar 00root root 0000000 0000000 #ifndef OSMO_BTS_PAGING_H
#define OSMO_BTS_PAGING_H
#include
#include
#include
struct paging_state;
struct gsm_bts_role_bts;
/* initialize paging code */
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
unsigned int num_paging_max,
unsigned int paging_lifetime);
/* (re) configure paging code */
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime);
void paging_reset(struct paging_state *ps);
/* The max number of paging entries */
unsigned int paging_get_queue_max(struct paging_state *ps);
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max);
/* The lifetime of a paging entry */
unsigned int paging_get_lifetime(struct paging_state *ps);
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime);
/* update with new SYSTEM INFORMATION parameters */
int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc);
/* Add an identity to the paging queue */
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
const uint8_t *identity_lv, uint8_t chan_needed);
/* Add an IMM.ASS message to the paging queue */
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
uint8_t len);
/* generate paging message for given gsm time */
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
int *is_empty);
/* inspection methods below */
int paging_group_queue_empty(struct paging_state *ps, uint8_t group);
int paging_queue_length(struct paging_state *ps);
int paging_buffer_space(struct paging_state *ps);
#endif
osmo-bts-0.4.0/include/osmo-bts/pcu_if.h 0000664 0000000 0000000 00000001222 12600264262 0020057 0 ustar 00root root 0000000 0000000 #ifndef _PCU_IF_H
#define _PCU_IF_H
int pcu_tx_info_ind(void);
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr);
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
int8_t rssi);
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn);
int pcu_tx_time_ind(uint32_t fn);
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed);
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
int pcu_sock_init(void);
void pcu_sock_exit(void);
#endif /* _PCU_IF_H */
osmo-bts-0.4.0/include/osmo-bts/pcuif_proto.h 0000664 0000000 0000000 00000007667 12600264262 0021166 0 ustar 00root root 0000000 0000000 #ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x05
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
#define PCU_IF_FLAG_CS4 (1 << 19)
#define PCU_IF_FLAG_MCS1 (1 << 20)
#define PCU_IF_FLAG_MCS2 (1 << 21)
#define PCU_IF_FLAG_MCS3 (1 << 22)
#define PCU_IF_FLAG_MCS4 (1 << 23)
#define PCU_IF_FLAG_MCS5 (1 << 24)
#define PCU_IF_FLAG_MCS6 (1 << 25)
#define PCU_IF_FLAG_MCS7 (1 << 26)
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
struct gsm_pcu_if_data {
uint8_t sapi;
uint8_t len;
uint8_t data[162];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
uint8_t sapi;
uint8_t spare[3];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_rach_ind {
uint8_t sapi;
uint8_t ra;
int16_t qta;
uint32_t fn;
uint16_t arfcn;
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
uint32_t version;
uint32_t flags;
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc, lac, rac;
/* NSE */
uint16_t nsei;
uint8_t nse_timer[7];
uint8_t cell_timer[11];
/* cell */
uint16_t cell_id;
uint16_t repeat_time;
uint8_t repeat_count;
uint16_t bvci;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint8_t t3193_10ms;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
uint8_t cv_countdown;
uint16_t dl_tbf_ext;
uint16_t ul_tbf_ext;
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
uint16_t nsvci[2];
uint16_t local_port[2];
uint16_t remote_port[2];
uint32_t remote_ip[2];
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
uint8_t activate;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t spare;
} __attribute__ ((packed));
struct gsm_pcu_if_time_ind {
uint32_t fn;
} __attribute__ ((packed));
struct gsm_pcu_if_pag_req {
uint8_t sapi;
uint8_t chan_needed;
uint8_t identity_lv[9];
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
uint8_t bts_nr; /* bts number */
uint8_t spare[2];
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
} u;
} __attribute__ ((packed));
#endif /* _PCUIF_PROTO_H */
osmo-bts-0.4.0/include/osmo-bts/power_control.h 0000664 0000000 0000000 00000000245 12600264262 0021512 0 ustar 00root root 0000000 0000000 #pragma once
#include
#include
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power, const int rxLevel);
osmo-bts-0.4.0/include/osmo-bts/rsl.h 0000664 0000000 0000000 00000002500 12600264262 0017412 0 ustar 00root root 0000000 0000000 #ifndef _RSL_H
#define _RSL_H
/**
* What kind of release/activation is done? A silent one for
* the PDCH or one triggered through RSL?
*/
enum {
LCHAN_REL_ACT_RSL,
LCHAN_REL_ACT_PCU,
LCHAN_REL_ACT_OML,
};
int msgb_queue_flush(struct llist_head *list);
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
int rsl_tx_rf_res(struct gsm_bts_trx *trx);
int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
uint8_t ra, uint8_t acc_delay);
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan);
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay);
/* call-back for LAPDm code, called when it wants to send msgs UP */
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause);
int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail);
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
uint16_t busy, uint16_t access);
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr);
#endif // _RSL_H */
osmo-bts-0.4.0/include/osmo-bts/signal.h 0000664 0000000 0000000 00000000360 12600264262 0020071 0 ustar 00root root 0000000 0000000 #ifndef OSMO_BTS_SIGNAL_H
#define OSMO_BTS_SIGNAL_H
#include
enum sig_subsys {
SS_GLOBAL,
};
enum signals_global {
S_NEW_SYSINFO,
S_NEW_OP_STATE,
S_NEW_NSE_ATTR,
S_NEW_CELL_ATTR,
S_NEW_NSVC_ATTR,
};
#endif
osmo-bts-0.4.0/include/osmo-bts/tx_power.h 0000664 0000000 0000000 00000004335 12600264262 0020471 0 ustar 00root root 0000000 0000000 #pragma once
#include
#include
/* our unit is 'milli dB" or "milli dBm", i.e. 1/1000 of a dB(m) */
#define to_mdB(x) (x * 1000)
/* PA calibration table */
struct pa_calibration {
int gain_mdB[1024]; /* gain provided at given ARFCN */
/* FIXME: thermal calibration */
};
/* representation of a RF power amplifier */
struct power_amp {
/* nominal gain of the PA */
int nominal_gain_mdB;
/* table with calibrated actual gain for each ARFCN */
struct pa_calibration calib;
};
/* Transmit power related parameters of a transceiver */
struct trx_power_params {
/* specified maximum output of TRX at full power, has to be
* initialized by BTS model at startup*/
int trx_p_max_out_mdBm;
/* intended current total system output power */
int p_total_tgt_mdBm;
/* actual current total system output power, filled in by tx_power code */
int p_total_cur_mdBm;
/* current temporary attenuation due to thermal management,
* set by thermal management code via control interface */
int thermal_attenuation_mdB;
/* external gain (+) or attenuation (-) added by the user, configured
* by the user via VTY */
int user_gain_mdB;
/* calibration table of internal PA */
struct power_amp pa;
/* calibration table of user PA */
struct power_amp user_pa;
/* power ramping related data */
struct {
/* maximum initial Pout including all PAs */
int max_initial_pout_mdBm;
/* temporary attenuation due to power ramping */
int attenuation_mdB;
unsigned int step_size_mdB;
unsigned int step_interval_sec;
struct osmo_timer_list step_timer;
} ramp;
};
int get_p_max_out_mdBm(struct gsm_bts_trx *trx);
int get_p_nominal_mdBm(struct gsm_bts_trx *trx);
int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_target_mdBm_lchan(struct gsm_lchan *lchan);
int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan);
int get_p_trxout_actual_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
int get_p_trxout_actual_mdBm_lchan(struct gsm_lchan *lchan);
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass);
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm);
osmo-bts-0.4.0/include/osmo-bts/vty.h 0000664 0000000 0000000 00000000753 12600264262 0017444 0 ustar 00root root 0000000 0000000 #ifndef OSMOBTS_VTY_H
#define OSMOBTS_VTY_H
#include
#include
enum bts_vty_node {
BTS_NODE = _LAST_OSMOVTY_NODE + 1,
TRX_NODE,
};
extern struct cmd_element ournode_exit_cmd;
extern struct cmd_element ournode_end_cmd;
enum node_type bts_vty_go_parent(struct vty *vty);
int bts_vty_is_config_node(struct vty *vty, int node);
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat);
extern struct vty_app_info bts_vty_info;
#endif
osmo-bts-0.4.0/src/ 0000775 0000000 0000000 00000000000 12600264262 0014045 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/src/Makefile.am 0000664 0000000 0000000 00000000161 12600264262 0016077 0 ustar 00root root 0000000 0000000 SUBDIRS = common
if ENABLE_SYSMOBTS
SUBDIRS += osmo-bts-sysmo
endif
if ENABLE_TRX
SUBDIRS += osmo-bts-trx
endif
osmo-bts-0.4.0/src/common/ 0000775 0000000 0000000 00000000000 12600264262 0015335 5 ustar 00root root 0000000 0000000 osmo-bts-0.4.0/src/common/Makefile.am 0000664 0000000 0000000 00000001055 12600264262 0017372 0 ustar 00root root 0000000 0000000 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
noinst_LIBRARIES = libbts.a
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
l1sap.c cbch.c power_control.c
osmo-bts-0.4.0/src/common/abis.c 0000664 0000000 0000000 00000013471 12600264262 0016425 0 ustar 00root root 0000000 0000000 /* Abis/IP interface routines utilizing libosmo-abis (Pablo) */
/* (C) 2011 by Andreas Eversberg
* (C) 2011-2013 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include "btsconfig.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct gsm_bts *g_bts;
int abis_oml_sendmsg(struct msgb *msg)
{
struct gsm_bts *bts = msg->trx->bts;
/* osmo-bts uses msg->trx internally, but libosmo-abis uses
* the signalling link at msg->dst */
msg->dst = bts->oml_link;
return abis_sendmsg(msg);
}
int abis_bts_rsl_sendmsg(struct msgb *msg)
{
/* osmo-bts uses msg->trx internally, but libosmo-abis uses
* the signalling link at msg->dst */
msg->dst = msg->trx->rsl_link;
return abis_sendmsg(msg);
}
static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
enum e1inp_sign_type type)
{
struct e1inp_sign_link *sign_link = NULL;
switch (type) {
case E1INP_SIGN_OML:
LOGP(DABIS, LOGL_INFO, "OML Signalling link up\n");
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line);
sign_link = g_bts->oml_link =
e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML-1],
E1INP_SIGN_OML, NULL, 255, 0);
sign_link->trx = g_bts->c0;
bts_link_estab(g_bts);
break;
case E1INP_SIGN_RSL:
LOGP(DABIS, LOGL_INFO, "RSL Signalling link up\n");
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line);
sign_link = g_bts->c0->rsl_link =
e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1],
E1INP_SIGN_RSL, NULL, 0, 0);
/* FIXME: This assumes there is only one TRX! */
sign_link->trx = g_bts->c0;
trx_link_estab(sign_link->trx);
break;
default:
break;
}
return sign_link;
}
static void sign_link_down(struct e1inp_line *line)
{
LOGP(DABIS, LOGL_ERROR, "Signalling link down\n");
if (g_bts->c0->rsl_link) {
e1inp_sign_link_destroy(g_bts->c0->rsl_link);
g_bts->c0->rsl_link = NULL;
trx_link_estab(g_bts->c0);
}
if (g_bts->oml_link)
e1inp_sign_link_destroy(g_bts->oml_link);
g_bts->oml_link = NULL;
bts_model_abis_close(g_bts);
}
/* callback for incoming mesages from A-bis/IP */
static int sign_link_cb(struct msgb *msg)
{
struct e1inp_sign_link *link = msg->dst;
/* osmo-bts code assumes msg->trx is set, but libosmo-abis works
* with the sign_link stored in msg->dst, so we have to convert
* here */
msg->trx = link->trx;
switch (link->type) {
case E1INP_SIGN_OML:
down_oml(link->trx->bts, msg);
break;
case E1INP_SIGN_RSL:
down_rsl(link->trx, msg);
break;
default:
msgb_free(msg);
break;
}
return 0;
}
uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link)
{
int fd = link->ts->driver.ipaccess.fd.fd;
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
int rc;
rc = getpeername(fd, (struct sockaddr *)&sin, &slen);
if (rc < 0) {
LOGP(DOML, LOGL_ERROR, "Cannot determine remote IP Addr: %s\n",
strerror(errno));
return 0;
}
/* we assume that the soket is AF_INET. As Abis/IP contains
* lots of hard-coded IPv4 addresses, this safe */
OSMO_ASSERT(sin.sin_family == AF_INET);
return ntohl(sin.sin_addr.s_addr);
}
static int inp_s_cbfn(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
if (subsys != SS_L_INPUT)
return 0;
DEBUGP(DABIS, "Input Signal %u received\n", signal);
return 0;
}
static struct ipaccess_unit bts_dev_info = {
.unit_name = "sysmoBTS",
.equipvers = "", /* FIXME: read this from hw */
.swversion = PACKAGE_VERSION,
.location1 = "",
.location2 = "",
.serno = "",
};
static struct e1inp_line_ops line_ops = {
.cfg = {
.ipa = {
.role = E1INP_LINE_R_BTS,
.dev = &bts_dev_info,
},
},
.sign_link_up = sign_link_up,
.sign_link_down = sign_link_down,
.sign_link = sign_link_cb,
};
void abis_init(struct gsm_bts *bts)
{
g_bts = bts;
oml_init();
libosmo_abis_init(NULL);
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
}
struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
char *model_name)
{
struct e1inp_line *line;
/* patch in various data from VTY and othe sources */
line_ops.cfg.ipa.addr = dst_host;
osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
bts_dev_info.site_id = bts->ip_access.site_id;
bts_dev_info.bts_id = bts->ip_access.bts_id;
bts_dev_info.unit_name = model_name;
if (bts->description)
bts_dev_info.unit_name = bts->description;
bts_dev_info.location2 = model_name;
line = e1inp_line_find(0);
if (!line)
line = e1inp_line_create(0, "ipa");
if (!line)
return NULL;
e1inp_line_bind_ops(line, &line_ops);
/* This will open the OML connection now */
if (e1inp_line_update(line) < 0)
return NULL;
return line;
}
osmo-bts-0.4.0/src/common/amr.c 0000664 0000000 0000000 00000005620 12600264262 0016263 0 ustar 00root root 0000000 0000000 #include
#include
#include
#include
#include
void amr_log_mr_conf(int ss, int logl, const char *pfx,
struct amr_multirate_conf *amr_mrc)
{
int i;
LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u",
pfx, amr_mrc->num_modes);
for (i = 0; i < amr_mrc->num_modes; i++)
LOGPC(ss, logl, ", mode[%u] = %u/%u/%u",
i, amr_mrc->mode[i].mode,
amr_mrc->mode[i].threshold_bts,
amr_mrc->mode[i].hysteresis_bts);
LOGPC(ss, logl, "\n");
}
/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more
* comfortable internal data structure */
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
const uint8_t *mr_conf, unsigned int len)
{
uint8_t mr_version = mr_conf[0] >> 5;
uint8_t num_codecs = 0;
int i, j = 0;
if (mr_version != 1) {
LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n",
mr_version);
goto ret_einval;
}
/* check number of active codecs */
for (i = 0; i < 8; i++) {
if (mr_conf[1] & (1 << i))
num_codecs++;
}
/* check for minimum length */
if (num_codecs == 0 ||
(num_codecs == 1 && len < 2) ||
(num_codecs == 2 && len < 4) ||
(num_codecs == 3 && len < 5) ||
(num_codecs == 4 && len < 6) ||
(num_codecs > 4)) {
LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u "
"not possible\n", num_codecs, len);
goto ret_einval;
}
/* copy the first two octets of the IE */
amr_mrc->gsm48_ie[0] = mr_conf[0];
amr_mrc->gsm48_ie[1] = mr_conf[1];
amr_mrc->num_modes = num_codecs;
for (i = 0; i < 8; i++) {
if (mr_conf[1] & (1 << i)) {
amr_mrc->mode[j++].mode = i;
}
}
if (num_codecs >= 2) {
amr_mrc->mode[0].threshold_bts = mr_conf[1] & 0x3F;
amr_mrc->mode[0].hysteresis_bts = mr_conf[2] >> 4;
}
if (num_codecs >= 3) {
amr_mrc->mode[1].threshold_bts =
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
amr_mrc->mode[1].hysteresis_bts = (mr_conf[3] >> 2) & 0xF;
}
if (num_codecs >= 4) {
amr_mrc->mode[2].threshold_bts =
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
amr_mrc->mode[2].hysteresis_bts = mr_conf[4] & 0xF;
}
return num_codecs;
ret_einval:
return -EINVAL;
}
/*! \brief determine AMR initial codec mode for given logical channel
* \returns integer between 0..3 for AMR codce mode 1..4 */
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan)
{
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
struct gsm48_multi_rate_conf *mr_conf =
(struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie;
if (mr_conf->icmi) {
/* initial mode given, coding in TS 05.09 3.4.1 */
return mr_conf->smod;
} else {
/* implicit rule according to TS 05.09 Chapter 3.4.3 */
switch (amr_mrc->num_modes) {
case 2:
case 3:
/* return the most robust */
return 0;
case 4:
/* return the second-most robust */
return 1;
case 1:
default:
/* return the only mode we have */
return 0;
}
}
}
osmo-bts-0.4.0/src/common/bts.c 0000664 0000000 0000000 00000037302 12600264262 0016276 0 ustar 00root root 0000000 0000000 /* BTS support code common to all supported BTS models */
/* (C) 2011 by Andreas Eversberg
* (C) 2011 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct gsm_network bts_gsmnet = {
.bts_list = { &bts_gsmnet.bts_list, &bts_gsmnet.bts_list },
.num_bts = 0,
};
void *tall_bts_ctx;
/* Table 3.1 TS 04.08: Values of parameter S */
static const uint8_t tx_integer[] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50,
};
static const uint8_t s_values[][2] = {
{ 55, 41 }, { 76, 52 }, { 109, 58 }, { 163, 86 }, { 217, 115 },
};
static int bts_signal_cbfn(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
if (subsys == SS_GLOBAL && signal == S_NEW_SYSINFO) {
struct gsm_bts *bts = signal_data;
bts_update_agch_max_queue_length(bts);
}
return 0;
}
int bts_init(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb;
struct gsm_bts_trx *trx;
int rc;
static int initialized = 0;
/* add to list of BTSs */
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
bts->band = GSM_BAND_1800;
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
INIT_LLIST_HEAD(&btsb->agch_queue);
btsb->agch_queue_length = 0;
/* enable management with default levels,
* raise threshold to GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE to
* disable this feature.
*/
btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT;
btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT;
btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT;
/* configurable via VTY */
btsb->paging_state = paging_init(btsb, 200, 0);
btsb->ul_power_target = -75; /* dBm default */
/* configurable via OML */
btsb->load.ccch.load_ind_period = 112;
load_timer_start(bts);
btsb->rtp_jitter_buf_ms = 100;
btsb->max_ta = 63;
btsb->ny1 = 4;
btsb->t3105_ms = 300;
/* default RADIO_LINK_TIMEOUT */
btsb->radio_link_timeout = 32;
/* Start with the site manager */
oml_mo_state_init(&bts->site_mgr.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
/* set BTS to dependency */
oml_mo_state_init(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nse.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.cell.mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_DEPENDENCY);
oml_mo_state_init(&bts->gprs.nsvc[1].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
/* initialize bts data structure */
llist_for_each_entry(trx, &bts->trx_list, list) {
struct trx_power_params *tpp = &trx->power_params;
int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
int k;
for (k = 0; k < ARRAY_SIZE(ts->lchan); k++) {
struct gsm_lchan *lchan = &ts->lchan[k];
INIT_LLIST_HEAD(&lchan->dl_tch_queue);
}
}
/* Default values for the power adjustments */
tpp->ramp.max_initial_pout_mdBm = to_mdB(23);
tpp->ramp.step_size_mdB = to_mdB(2);
tpp->ramp.step_interval_sec = 1;
}
osmo_rtp_init(tall_bts_ctx);
rc = bts_model_init(bts);
if (rc < 0) {
llist_del(&bts->list);
return rc;
}
bts_gsmnet.num_bts++;
if (!initialized) {
osmo_signal_register_handler(SS_GLOBAL, bts_signal_cbfn, NULL);
initialized = 1;
}
INIT_LLIST_HEAD(&btsb->smscb_state.queue);
return rc;
}
static void shutdown_timer_cb(void *data)
{
fprintf(stderr, "Shutdown timer expired\n");
exit(42);
}
static struct osmo_timer_list shutdown_timer = {
.cb = &shutdown_timer_cb,
};
void bts_shutdown(struct gsm_bts *bts, const char *reason)
{
struct gsm_bts_trx *trx;
if (osmo_timer_pending(&shutdown_timer)) {
LOGP(DOML, LOGL_NOTICE,
"BTS is already being shutdown.\n");
return;
}
LOGP(DOML, LOGL_NOTICE, "Shutting down BTS %u, Reason %s\n",
bts->nr, reason);
llist_for_each_entry(trx, &bts->trx_list, list) {
bts_model_trx_deact_rf(trx);
bts_model_trx_close(trx);
}
/* shedule a timer to make sure select loop logic can run again
* to dispatch any pending primitives */
osmo_timer_schedule(&shutdown_timer, 3, 0);
}
/* main link is established, send status report */
int bts_link_estab(struct gsm_bts *bts)
{
int i, j;
LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n");
/* BTS and SITE MGR are EANBLED, BTS is DEPENDENCY */
oml_tx_state_changed(&bts->site_mgr.mo);
oml_tx_state_changed(&bts->mo);
/* those should all be in DEPENDENCY */
oml_tx_state_changed(&bts->gprs.nse.mo);
oml_tx_state_changed(&bts->gprs.cell.mo);
oml_tx_state_changed(&bts->gprs.nsvc[0].mo);
oml_tx_state_changed(&bts->gprs.nsvc[1].mo);
/* All other objects start off-line until the BTS Model code says otherwise */
for (i = 0; i < bts->num_trx; i++) {
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
oml_tx_state_changed(&trx->mo);
oml_tx_state_changed(&trx->bb_transc.mo);
for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
struct gsm_bts_trx_ts *ts = &trx->ts[j];
oml_tx_state_changed(&ts->mo);
}
}
return bts_model_oml_estab(bts);
}
/* RSL link is established, send status report */
int trx_link_estab(struct gsm_bts_trx *trx)
{
struct e1inp_sign_link *link = trx->rsl_link;
uint8_t radio_state = link ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n",
trx->nr, link ? "up" : "down");
oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK);
if (link)
rsl_tx_rf_res(trx);
else
bts_model_trx_deact_rf(trx);
return 0;
}
int lchan_init_lapdm(struct gsm_lchan *lchan)
{
struct lapdm_channel *lc = &lchan->lapdm_ch;
lapdm_channel_init(lc, LAPDM_MODE_BTS);
lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY);
lapdm_channel_set_l1(lc, NULL, lchan);
lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan);
return 0;
}
#define CCCH_RACH_RATIO_COMBINED256 (256*1/9)
#define CCCH_RACH_RATIO_SEPARATE256 (256*10/55)
int bts_agch_max_queue_length(int T, int bcch_conf)
{
int S, ccch_rach_ratio256, i;
int T_group = 0;
int is_ccch_comb = 0;
if (bcch_conf == RSL_BCCH_CCCH_CONF_1_C)
is_ccch_comb = 1;
/*
* The calculation is based on the ratio of the number RACH slots and
* CCCH blocks per time:
* Lmax = (T + 2*S) / R_RACH * R_CCCH
* where
* T3126_min = (T + 2*S) / R_RACH, as defined in GSM 04.08, 11.1.1
* R_RACH is the RACH slot rate (e.g. RACHs per multiframe)
* R_CCCH is the CCCH block rate (same time base like R_RACH)
* S and T are defined in GSM 04.08, 3.3.1.1.2
* The ratio is mainly influenced by the downlink only channels
* (BCCH, FCCH, SCH, CBCH) that can not be used for CCCH.
* An estimation with an error of < 10% is used:
* ~ 1/9 if CCCH is combined with SDCCH, and
* ~ 1/5.5 otherwise.
*/
ccch_rach_ratio256 = is_ccch_comb ?
CCCH_RACH_RATIO_COMBINED256 :
CCCH_RACH_RATIO_SEPARATE256;
for (i = 0; i < ARRAY_SIZE(tx_integer); i++) {
if (tx_integer[i] == T) {
T_group = i % 5;
break;
}
}
S = s_values[T_group][is_ccch_comb];
return (T + 2 * S) * ccch_rach_ratio256 / 256;
}
void bts_update_agch_max_queue_length(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct gsm48_system_information_type_3 *si3;
int old_max_length = btsb->agch_max_queue_length;
if (!(bts->si_valid & (1<agch_max_queue_length =
bts_agch_max_queue_length(si3->rach_control.tx_integer,
si3->control_channel_desc.ccch_conf);
if (btsb->agch_max_queue_length != old_max_length)
LOGP(DRSL, LOGL_INFO, "Updated AGCH max queue length to %d\n",
btsb->agch_max_queue_length);
}
#define REQ_REFS_PER_IMM_ASS_REJ 4
static int store_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
struct gsm48_req_ref *req_refs,
uint8_t *wait_inds,
int count)
{
switch (count) {
case 0:
/* TODO: Warning ? */
return 0;
default:
count = 4;
rej->req_ref4 = req_refs[3];
rej->wait_ind4 = wait_inds[3];
/* fall through */
case 3:
rej->req_ref3 = req_refs[2];
rej->wait_ind3 = wait_inds[2];
/* fall through */
case 2:
rej->req_ref2 = req_refs[1];
rej->wait_ind2 = wait_inds[1];
/* fall through */
case 1:
rej->req_ref1 = req_refs[0];
rej->wait_ind1 = wait_inds[0];
break;
}
switch (count) {
case 1:
rej->req_ref2 = req_refs[0];
rej->wait_ind2 = wait_inds[0];
/* fall through */
case 2:
rej->req_ref3 = req_refs[0];
rej->wait_ind3 = wait_inds[0];
/* fall through */
case 3:
rej->req_ref4 = req_refs[0];
rej->wait_ind4 = wait_inds[0];
/* fall through */
default:
break;
}
return count;
}
static int extract_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
struct gsm48_req_ref *req_refs,
uint8_t *wait_inds)
{
int count = 0;
req_refs[count] = rej->req_ref1;
wait_inds[count] = rej->wait_ind1;
count++;
if (memcmp(&rej->req_ref1, &rej->req_ref2, sizeof(rej->req_ref2))) {
req_refs[count] = rej->req_ref2;
wait_inds[count] = rej->wait_ind2;
count++;
}
if (memcmp(&rej->req_ref1, &rej->req_ref3, sizeof(rej->req_ref3)) &&
memcmp(&rej->req_ref2, &rej->req_ref3, sizeof(rej->req_ref3))) {
req_refs[count] = rej->req_ref3;
wait_inds[count] = rej->wait_ind3;
count++;
}
if (memcmp(&rej->req_ref1, &rej->req_ref4, sizeof(rej->req_ref4)) &&
memcmp(&rej->req_ref2, &rej->req_ref4, sizeof(rej->req_ref4)) &&
memcmp(&rej->req_ref3, &rej->req_ref4, sizeof(rej->req_ref4))) {
req_refs[count] = rej->req_ref4;
wait_inds[count] = rej->wait_ind4;
count++;
}
return count;
}
static int try_merge_imm_ass_rej(struct gsm48_imm_ass_rej *old_rej,
struct gsm48_imm_ass_rej *new_rej)
{
struct gsm48_req_ref req_refs[2 * REQ_REFS_PER_IMM_ASS_REJ];
uint8_t wait_inds[2 * REQ_REFS_PER_IMM_ASS_REJ];
int count = 0;
int stored = 0;
if (new_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return 0;
if (old_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return 0;
/* GSM 08.58, 5.7
* -> The BTS may combine serveral IMM.ASS.REJ messages
* -> Identical request refs in one message may be squeezed
*
* GSM 04.08, 9.1.20.2
* -> Request ref and wait ind are duplicated to fill the message
*/
/* Extract all entries */
count = extract_imm_ass_rej_refs(old_rej,
&req_refs[count], &wait_inds[count]);
if (count == REQ_REFS_PER_IMM_ASS_REJ)
return 0;
count += extract_imm_ass_rej_refs(new_rej,
&req_refs[count], &wait_inds[count]);
/* Store entries into old message */
stored = store_imm_ass_rej_refs(old_rej,
&req_refs[stored], &wait_inds[stored],
count);
count -= stored;
if (count == 0)
return 1;
/* Store remaining entries into new message */
stored += store_imm_ass_rej_refs(new_rej,
&req_refs[stored], &wait_inds[stored],
count);
return 0;
}
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
int hard_limit = 1000;
struct gsm48_imm_ass_rej *imm_ass_cmd = msgb_l3(msg);
if (btsb->agch_queue_length > hard_limit) {
LOGP(DSUM, LOGL_ERROR,
"AGCH: too many messages in queue, "
"refusing message type 0x%02x, length = %d/%d\n",
((struct gsm48_imm_ass *)msgb_l3(msg))->msg_type,
btsb->agch_queue_length, btsb->agch_max_queue_length);
btsb->agch_queue_rejected_msgs++;
return -ENOMEM;
}
if (btsb->agch_queue_length > 0) {
struct msgb *last_msg =
llist_entry(btsb->agch_queue.prev, struct msgb, list);
struct gsm48_imm_ass_rej *last_imm_ass_rej = msgb_l3(last_msg);
if (try_merge_imm_ass_rej(last_imm_ass_rej, imm_ass_cmd)) {
btsb->agch_queue_merged_msgs++;
msgb_free(msg);
return 0;
}
}
msgb_enqueue(&btsb->agch_queue, msg);
btsb->agch_queue_length++;
return 0;
}
struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct msgb *msg = msgb_dequeue(&btsb->agch_queue);
if (!msg)
return NULL;
btsb->agch_queue_length--;
return msg;
}
/*
* Remove lower prio messages if the queue has grown too long.
*
* \return 0 iff the number of messages in the queue would fit into the AGCH
* reserved part of the CCCH.
*/
static void compact_agch_queue(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct msgb *msg, *msg2;
int max_len, slope, offs;
int level_low = btsb->agch_queue_low_level;
int level_high = btsb->agch_queue_high_level;
int level_thres = btsb->agch_queue_thresh_level;
max_len = btsb->agch_max_queue_length;
if (max_len == 0)
max_len = 1;
if (btsb->agch_queue_length < max_len * level_thres / 100)
return;
/* p^
* 1+ /'''''
* | /
* | /
* 0+---/--+----+--> Q length
* low high max_len
*/
offs = max_len * level_low / 100;
if (level_high > level_low)
slope = 0x10000 * 100 / (level_high - level_low);
else
slope = 0x10000 * max_len; /* p_drop >= 1 if len > offs */
llist_for_each_entry_safe(msg, msg2, &btsb->agch_queue, list) {
struct gsm48_imm_ass *imm_ass_cmd = msgb_l3(msg);
int p_drop;
if (imm_ass_cmd->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
return;
/* IMMEDIATE ASSIGN REJECT */
p_drop = (btsb->agch_queue_length - offs) * slope / max_len;
if ((random() & 0xffff) >= p_drop)
return;
llist_del(&msg->list);
btsb->agch_queue_length--;
msgb_free(msg);
btsb->agch_queue_dropped_msgs++;
}
return;
}
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
int is_ag_res)
{
struct msgb *msg = NULL;
struct gsm_bts_role_bts *btsb = bts->role;
int rc = 0;
int is_empty = 1;
/* Do queue house keeping.
* This needs to be done every time a CCCH message is requested, since
* the queue max length is calculated based on the CCCH block rate and
* PCH messages also reduce the drain of the AGCH queue.
*/
compact_agch_queue(bts);
/* Check for paging messages first if this is PCH */
if (!is_ag_res)
rc = paging_gen_msg(btsb->paging_state, out_buf, gt, &is_empty);
/* Check whether the block may be overwritten */
if (!is_empty)
return rc;
msg = bts_agch_dequeue(bts);
if (!msg)
return rc;
/* Copy AGCH message */
memcpy(out_buf, msgb_l3(msg), msgb_l3len(msg));
rc = msgb_l3len(msg);
msgb_free(msg);
if (is_ag_res)
btsb->agch_queue_agch_msgs++;
else
btsb->agch_queue_pch_msgs++;
return rc;
}
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
{
int sup;
if (rsl_cipher < 1 || rsl_cipher > 8)
return -ENOTSUP;
/* No encryption is always supported */
if (rsl_cipher == 1)
return 1;
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
return sup > 0;
}
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx)
{
return trx->ms_power_control == 1;
}
struct gsm_time *get_time(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts->role;
return &btsb->gsm_time;
}
osmo-bts-0.4.0/src/common/bts_ctrl_commands.c 0000664 0000000 0000000 00000003733 12600264262 0021204 0 ustar 00root root 0000000 0000000 /* Control Interface for osmo-bts */
/* (C) 2014 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
CTRL_CMD_DEFINE(therm_att, "thermal-attenuation");
static int get_therm_att(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts_trx *trx = cmd->node;
struct trx_power_params *tpp = &trx->power_params;
cmd->reply = talloc_asprintf(cmd, "%d", tpp->thermal_attenuation_mdB);
return CTRL_CMD_REPLY;
}
static int set_therm_att(struct ctrl_cmd *cmd, void *data)
{
struct gsm_bts_trx *trx = cmd->node;
struct trx_power_params *tpp = &trx->power_params;
int val = atoi(cmd->value);
printf("set_therm_att(trx=%p, tpp=%p)\n", trx, tpp);
tpp->thermal_attenuation_mdB = val;
power_ramp_start(trx, tpp->p_total_cur_mdBm, 0);
return get_therm_att(cmd, data);
}
static int verify_therm_att(struct ctrl_cmd *cmd, const char *value, void *data)
{
int val = atoi(value);
/* permit between 0 to 40 dB attenuation */
if (val < 0 || val > to_mdB(40))
return 1;
return 0;
}
int bts_ctrl_cmds_install(struct gsm_bts *bts)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_therm_att);
return rc;
}
osmo-bts-0.4.0/src/common/bts_ctrl_lookup.c 0000664 0000000 0000000 00000005535 12600264262 0020716 0 ustar 00root root 0000000 0000000 /* Control Interface for osmo-bts */
/* (C) 2014 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
extern vector ctrl_node_vec;
/*! \brief control interface lookup function for bsc/bts gsm_data
* \param[in] data Private data passed to controlif_setup()
* \param[in] vline Vector of the line holding the command string
* \param[out] node_type type (CTRL_NODE_) that was determined
* \param[out] node_data private dta of node that was determined
* \param i Current index into vline, up to which it is parsed
*/
static int bts_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
struct gsm_bts *bts = data;
struct gsm_bts_trx *trx = NULL;
struct gsm_bts_trx_ts *ts = NULL;
char *token = vector_slot(vline, *i);
long num;
/* TODO: We need to make sure that the following chars are digits
* and/or use strtol to check if number conversion was successful
* Right now something like net.bts_stats will not work */
if (!strcmp(token, "trx")) {
if (*node_type != CTRL_NODE_ROOT || !*node_data)
goto err_missing;
bts = *node_data;
(*i)++;
if (!ctrl_parse_get_num(vline, *i, &num))
goto err_index;
trx = gsm_bts_trx_num(bts, num);
if (!trx)
goto err_missing;
*node_data = trx;
*node_type = CTRL_NODE_TRX;
} else if (!strcmp(token, "ts")) {
if (*node_type != CTRL_NODE_TRX || !*node_data)
goto err_missing;
trx = *node_data;
(*i)++;
if (!ctrl_parse_get_num(vline, *i, &num))
goto err_index;
if ((num >= 0) && (num < TRX_NR_TS))
ts = &trx->ts[num];
if (!ts)
goto err_missing;
*node_data = ts;
*node_type = CTRL_NODE_TS;
} else
return 0;
return 1;
err_missing:
return -ENODEV;
err_index:
return -ERANGE;
}
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts)
{
struct ctrl_handle *hdl;
int rc = 0;
hdl = ctrl_interface_setup(bts, OSMO_CTRL_PORT_BTS, bts_ctrl_node_lookup);
if (!hdl)
return NULL;
rc = bts_ctrl_cmds_install(bts);
if (rc) {
/* FIXME: close control interface */
return NULL;
}
return hdl;
}
osmo-bts-0.4.0/src/common/cbch.c 0000664 0000000 0000000 00000012420 12600264262 0016377 0 ustar 00root root 0000000 0000000 /* Cell Broadcast routines */
/* (C) 2014 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
struct smscb_msg {
struct llist_head list; /* list in smscb_state.queue */
uint8_t msg[GSM412_MSG_LEN]; /* message buffer */
uint8_t next_seg; /* next segment number */
uint8_t num_segs; /* total number of segments */
};
static int get_smscb_null_block(uint8_t *out)
{
struct gsm412_block_type *block_type = (struct gsm412_block_type *) out;
block_type->spare = 0;
block_type->lpd = 1;
block_type->seq_nr = GSM412_SEQ_NULL_MSG;
block_type->lb = 0;
memset(out+1, GSM_MACBLOCK_PADDING, GSM412_BLOCK_LEN);
return 0;
}
/* get the next block of the current CB message */
static int get_smscb_block(struct gsm_bts *bts, uint8_t *out)
{
int to_copy;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct gsm412_block_type *block_type;
struct smscb_msg *msg = btsb->smscb_state.cur_msg;
if (!msg) {
/* No message: Send NULL mesage */
return get_smscb_null_block(out);
}
block_type = (struct gsm412_block_type *) out++;
/* LPD is always 01 */
block_type->spare = 0;
block_type->lpd = 1;
/* determine how much data to copy */
to_copy = GSM412_MSG_LEN - (msg->next_seg * GSM412_BLOCK_LEN);
if (to_copy > GSM412_BLOCK_LEN)
to_copy = GSM412_BLOCK_LEN;
/* copy data and increment index */
memcpy(out, &msg->msg[msg->next_seg * GSM412_BLOCK_LEN], to_copy);
/* set + increment sequence number */
block_type->seq_nr = msg->next_seg++;
/* determine if this is the last block */
if (block_type->seq_nr + 1 == msg->num_segs)
block_type->lb = 1;
else
block_type->lb = 0;
if (block_type->lb == 1) {
/* remove/release the message memory */
talloc_free(btsb->smscb_state.cur_msg);
btsb->smscb_state.cur_msg = NULL;
}
return block_type->lb;
}
static const uint8_t last_block_rsl2um[4] = {
[RSL_CB_CMD_LASTBLOCK_4] = 4,
[RSL_CB_CMD_LASTBLOCK_1] = 1,
[RSL_CB_CMD_LASTBLOCK_2] = 2,
[RSL_CB_CMD_LASTBLOCK_3] = 3,
};
/* incoming SMS broadcast command from RSL */
int bts_process_smscb_cmd(struct gsm_bts *bts,
struct rsl_ie_cb_cmd_type cmd_type,
uint8_t msg_len, const uint8_t *msg)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct smscb_msg *scm;
if (msg_len > sizeof(scm->msg)) {
LOGP(DLSMS, LOGL_ERROR,
"Cannot process SMSCB of %u bytes (max %lu)\n",
msg_len, sizeof(scm->msg));
return -EINVAL;
}
scm = talloc_zero_size(bts, sizeof(*scm));
/* initialize entire message with default padding */
memset(scm->msg, GSM_MACBLOCK_PADDING, sizeof(scm->msg));
/* next segment is first segment */
scm->next_seg = 0;
switch (cmd_type.command) {
case RSL_CB_CMD_TYPE_NORMAL:
case RSL_CB_CMD_TYPE_SCHEDULE:
case RSL_CB_CMD_TYPE_NULL:
scm->num_segs = last_block_rsl2um[cmd_type.last_block&3];
memcpy(scm->msg, msg, msg_len);
/* def_bcast is ignored */
break;
case RSL_CB_CMD_TYPE_DEFAULT:
/* use def_bcast, ignore command */
/* def_bcast == 0: normal mess */
break;
}
llist_add_tail(&scm->list, &btsb->smscb_state.queue);
return 0;
}
static struct smscb_msg *select_next_smscb(struct gsm_bts *bts)
{
struct smscb_msg *msg;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
if (llist_empty(&btsb->smscb_state.queue))
return NULL;
msg = llist_entry(btsb->smscb_state.queue.next,
struct smscb_msg, list);
llist_del(&msg->list);
return msg;
}
/* call-back from bts model specific code when it wants to obtain a CBCH
* block for a given gsm_time. outbuf must have 23 bytes of space. */
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
uint32_t fn = gsm_gsmtime2fn(g_time);
/* According to 05.02 Section 6.5.4 */
uint32_t tb = (fn / 51) % 8;
int rc = 0;
/* The multiframes used for the basic cell broadcast channel
* shall be those in * which TB = 0,1,2 and 3. The multiframes
* used for the extended cell broadcast channel shall be those
* in which TB = 4, 5, 6 and 7 */
/* The SMSCB header shall be sent in the multiframe in which TB
* = 0 for the basic, and TB = 4 for the extended cell
* broadcast channel. */
switch (tb) {
case 0:
/* select a new SMSCB message */
btsb->smscb_state.cur_msg = select_next_smscb(bts);
rc = get_smscb_block(bts, outbuf);
break;
case 1: case 2: case 3:
rc = get_smscb_block(bts, outbuf);
break;
case 4: case 5: case 6: case 7:
/* always send NULL frame in extended CBCH for now */
rc = get_smscb_null_block(outbuf);
break;
}
return rc;
}
osmo-bts-0.4.0/src/common/gsm_data_shared.c 0000664 0000000 0000000 00000000104 12600264262 0020601 0 ustar 00root root 0000000 0000000 #include "../../../openbsc/openbsc/src/libcommon/gsm_data_shared.c"
osmo-bts-0.4.0/src/common/handover.c 0000664 0000000 0000000 00000011052 12600264262 0017306 0 ustar 00root root 0000000 0000000 /* Paging message encoding + queue management */
/* (C) 2012-2013 by Harald Welte
* Andreas Eversberg
* (C) 2014 by Holger Hans Peter Freyther
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Transmit a handover related PHYS INFO on given lchan */
static int ho_tx_phys_info(struct gsm_lchan *lchan)
{
struct msgb *msg = msgb_alloc_headroom(1024, 128, "PHYS INFO");
struct gsm48_hdr *gh;
if (!msg)
return -ENOMEM;
LOGP(DHO, LOGL_INFO,
"%s Sending PHYSICAL INFORMATION to MS.\n",
gsm_lchan_name(lchan));
/* Build RSL UNITDATA REQUEST message with 04.08 PHYS INFO */
msg->l3h = msg->data;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_INFO;
msgb_put_u8(msg, lchan->rqd_ta);
rsl_rll_push_l3(msg, RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan),
0x00, 0);
lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch);
return 0;
}
/* timer call-back for T3105 (handover PHYS INFO re-transmit) */
static void ho_t3105_cb(void *data)
{
struct gsm_lchan *lchan = data;
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
LOGP(DHO, LOGL_INFO, "%s T3105 timeout (%d resends left)\n",
gsm_lchan_name(lchan), btsb->ny1 - lchan->ho.phys_info_count);
if (lchan->state != LCHAN_S_ACTIVE) {
LOGP(DHO, LOGL_NOTICE,
"%s is in not active. It is in state %s. Ignoring\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
return;
}
if (lchan->ho.phys_info_count >= btsb->ny1) {
/* HO Abort */
LOGP(DHO, LOGL_NOTICE, "%s NY1 reached, sending CONNection "
"FAILure to BSC.\n", gsm_lchan_name(lchan));
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
return;
}
ho_tx_phys_info(lchan);
lchan->ho.phys_info_count++;
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
}
/* received random access on dedicated channel */
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
/* Ignore invalid handover ref */
if (lchan->ho.ref != ra) {
LOGP(DHO, LOGL_INFO, "%s RACH on dedicated channel received, but "
"ra=0x%02x != expected ref=0x%02x. (This is no bug)\n",
gsm_lchan_name(lchan), ra, lchan->ho.ref);
return;
}
LOGP(DHO, LOGL_NOTICE,
"%s RACH on dedicated channel received with TA=%u\n",
gsm_lchan_name(lchan), acc_delay);
/* Set timing advance */
lchan->rqd_ta = acc_delay;
/* Stop handover detection, wait for valid frame */
lchan->ho.active = HANDOVER_WAIT_FRAME;
if (l1sap_chan_modify(lchan->ts->trx, gsm_lchan2chan_nr(lchan)) != 0) {
LOGP(DHO, LOGL_ERROR,
"%s failed to modify channel after handover\n",
gsm_lchan_name(lchan));
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
return;
}
/* Send HANDover DETect to BSC */
rsl_tx_hando_det(lchan, &lchan->rqd_ta);
/* Send PHYS INFO */
lchan->ho.phys_info_count = 1;
ho_tx_phys_info(lchan);
/* Start T3105 */
LOGP(DHO, LOGL_DEBUG,
"%s Starting T3105 with %u ms\n",
gsm_lchan_name(lchan), btsb->t3105_ms);
lchan->ho.t3105.cb = ho_t3105_cb;
lchan->ho.t3105.data = lchan;
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
}
/* received frist valid data frame on dedicated channel */
void handover_frame(struct gsm_lchan *lchan)
{
LOGP(DHO, LOGL_INFO,
"%s First valid frame detected\n", gsm_lchan_name(lchan));
handover_reset(lchan);
}
/* release handover state */
void handover_reset(struct gsm_lchan *lchan)
{
/* Stop T3105 */
osmo_timer_del(&lchan->ho.t3105);
/* Handover process is done */
lchan->ho.active = HANDOVER_NONE;
}
osmo-bts-0.4.0/src/common/l1sap.c 0000664 0000000 0000000 00000073222 12600264262 0016527 0 ustar 00root root 0000000 0000000 /* L1 SAP primitives */
/* (C) 2011 by Harald Welte
* (C) 2013 by Andreas Eversberg
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
0x2B, 0x2B, 0x2B
};
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data
* in order to wrap femtobts header arround l2 data, there must be enough space
* in front and behind data pointer */
struct msgb *l1sap_msgb_alloc(unsigned int l2_len)
{
struct msgb *msg = msgb_alloc_headroom(512, 128, "l1sap_prim");
if (!msg)
return NULL;
msg->l1h = msgb_put(msg, sizeof(struct osmo_phsap_prim));
return msg;
}
static int l1sap_tx_ciph_req(struct gsm_bts_trx *trx, uint8_t chan_nr,
uint8_t downlink, uint8_t uplink)
{
struct osmo_phsap_prim l1sap_ciph;
osmo_prim_init(&l1sap_ciph.oph, SAP_GSM_PH, PRIM_MPH_INFO,
PRIM_OP_REQUEST, NULL);
l1sap_ciph.u.info.type = PRIM_INFO_ACT_CIPH;
l1sap_ciph.u.info.u.ciph_req.chan_nr = chan_nr;
l1sap_ciph.u.info.u.ciph_req.downlink = downlink;
l1sap_ciph.u.info.u.ciph_req.uplink = uplink;
return l1sap_down(trx, &l1sap_ciph);
}
/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable
* uni-directional de-cryption on the uplink. We need this ugly layering
* violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD)
* to this point in L1 */
static int check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan,
uint8_t chan_nr)
{
/* only do this if we are in the right state */
switch (lchan->ciph_state) {
case LCHAN_CIPH_NONE:
case LCHAN_CIPH_RX_REQ:
break;
default:
return 0;
}
/* First byte (Address Field) of LAPDm header) */
if (msg->data[0] != 0x03)
return 0;
/* First byte (protocol discriminator) of RR */
if ((msg->data[3] & 0xF) != GSM48_PDISC_RR)
return 0;
/* 2nd byte (msg type) of RR */
if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD)
return 0;
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 0, 1);
return 1;
}
struct gsmtap_inst *gsmtap = NULL;
uint32_t gsmtap_sapi_mask = 0;
uint8_t gsmtap_sapi_acch = 0;
const struct value_string gsmtap_sapi_names[] = {
{ GSMTAP_CHANNEL_BCCH, "BCCH" },
{ GSMTAP_CHANNEL_CCCH, "CCCH" },
{ GSMTAP_CHANNEL_RACH, "RACH" },
{ GSMTAP_CHANNEL_AGCH, "AGCH" },
{ GSMTAP_CHANNEL_PCH, "PCH" },
{ GSMTAP_CHANNEL_SDCCH, "SDCCH" },
{ GSMTAP_CHANNEL_TCH_F, "TCH/F" },
{ GSMTAP_CHANNEL_TCH_H, "TCH/H" },
{ GSMTAP_CHANNEL_PACCH, "PACCH" },
{ GSMTAP_CHANNEL_PDCH, "PDTCH" },
{ GSMTAP_CHANNEL_PTCCH, "PTCCH" },
{ GSMTAP_CHANNEL_CBCH51,"CBCH" },
{ GSMTAP_CHANNEL_ACCH, "SACCH" },
{ 0, NULL }
};
/* send primitive as gsmtap */
static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
{
struct msgb *msg = l1sap->oph.msg;
uint8_t chan_nr, link_id;
*data = msg->data + sizeof(struct osmo_phsap_prim);
*len = msg->len - sizeof(struct osmo_phsap_prim);
*fn = l1sap->u.data.fn;
*tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
chan_nr = l1sap->u.data.chan_nr;
link_id = l1sap->u.data.link_id;
if (L1SAP_IS_CHAN_TCHF(chan_nr)) {
*chan_type = GSMTAP_CHANNEL_TCH_F;
} else if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
*ss = L1SAP_CHAN2SS_TCHH(chan_nr);
*chan_type = GSMTAP_CHANNEL_TCH_H;
} else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) {
*ss = L1SAP_CHAN2SS_SDCCH4(chan_nr);
*chan_type = GSMTAP_CHANNEL_SDCCH;
} else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) {
*ss = L1SAP_CHAN2SS_SDCCH8(chan_nr);
*chan_type = GSMTAP_CHANNEL_SDCCH;
} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
*chan_type = GSMTAP_CHANNEL_BCCH;
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
#warning Set BS_AG_BLKS_RES
/* The sapi depends on DSP configuration, not
* on the actual SYSTEM INFORMATION 3. */
if (L1SAP_FN2CCCHBLOCK(*fn) >= 1)
*chan_type = GSMTAP_CHANNEL_PCH;
else
*chan_type = GSMTAP_CHANNEL_AGCH;
}
if (L1SAP_IS_LINK_SACCH(link_id))
*chan_type |= GSMTAP_CHANNEL_ACCH;
return 0;
}
static int gsmtap_pdch(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
{
struct msgb *msg = l1sap->oph.msg;
*data = msg->data + sizeof(struct osmo_phsap_prim);
*len = msg->len - sizeof(struct osmo_phsap_prim);
*fn = l1sap->u.data.fn;
*tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
if (L1SAP_IS_PTCCH(*fn)) {
*chan_type = GSMTAP_CHANNEL_PTCCH;
*ss = L1SAP_FN2PTCCHBLOCK(*fn);
if (l1sap->oph.primitive
== PRIM_OP_INDICATION) {
if ((*data[0]) == 7)
return -EINVAL;
(*data)++;
(*len)--;
}
} else
*chan_type = GSMTAP_CHANNEL_PACCH;
return 0;
}
static int gsmtap_ph_rach(struct osmo_phsap_prim *l1sap, uint8_t *chan_type,
uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, int *len)
{
uint8_t chan_nr;
*chan_type = GSMTAP_CHANNEL_RACH;
*fn = l1sap->u.rach_ind.fn;
*tn = L1SAP_CHAN2TS(l1sap->u.rach_ind.chan_nr);
chan_nr = l1sap->u.rach_ind.chan_nr;
if (L1SAP_IS_CHAN_TCHH(chan_nr))
*ss = L1SAP_CHAN2SS_TCHH(chan_nr);
else if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
*ss = L1SAP_CHAN2SS_SDCCH4(chan_nr);
else if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
*ss = L1SAP_CHAN2SS_SDCCH8(chan_nr);
*data = &l1sap->u.rach_ind.ra;
*len = 1;
return 0;
}
static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
uint8_t *data;
int len;
uint8_t chan_type = 0, tn = 0, ss = 0;
uint32_t fn;
uint16_t uplink = GSMTAP_ARFCN_F_UPLINK;
int rc;
if (!gsmtap)
return 0;
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
uplink = 0;
/* fall through */
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
if (trx->ts[tn].pchan == GSM_PCHAN_PDCH)
rc = gsmtap_pdch(l1sap, &chan_type, &tn, &ss, &fn, &data,
&len);
else
rc = gsmtap_ph_data(l1sap, &chan_type, &tn, &ss, &fn,
&data, &len);
break;
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
rc = gsmtap_ph_rach(l1sap, &chan_type, &tn, &ss, &fn, &data,
&len);
break;
default:
rc = -ENOTSUP;
}
if (rc)
return rc;
if (len == 0)
return 0;
if ((chan_type & GSMTAP_CHANNEL_ACCH)) {
if (!gsmtap_sapi_acch)
return 0;
} else {
if (!((1 << (chan_type & 31)) & gsmtap_sapi_mask))
return 0;
}
gsmtap_send(gsmtap, trx->arfcn | uplink, tn, chan_type, ss, fn, 0, 0,
data, len);
return 0;
}
/* time information received from bts model */
static int l1sap_info_time_ind(struct gsm_bts *bts,
struct osmo_phsap_prim *l1sap,
struct info_time_ind_param *info_time_ind)
{
struct gsm_bts_trx *trx;
struct gsm_bts_role_bts *btsb = bts->role;
int frames_expired = info_time_ind->fn - btsb->gsm_time.fn;
DEBUGP(DL1P, "MPH_INFO time ind %u\n", info_time_ind->fn);
/* Update our data structures with the current GSM time */
gsm_fn2gsmtime(&btsb->gsm_time, info_time_ind->fn);
/* Update time on PCU interface */
pcu_tx_time_ind(info_time_ind->fn);
/* check if the measurement period of some lchan has ended
* and pre-compute the respective measurement */
llist_for_each_entry(trx, &bts->trx_list, list)
trx_meas_check_compute(trx, info_time_ind->fn - 1);
/* increment number of RACH slots that have passed by since the
* last time indication */
if (trx == bts->c0) {
unsigned int num_rach_per_frame;
/* 27 / 51 taken from TS 05.01 Figure 3 */
if (bts->c0->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4)
num_rach_per_frame = 27;
else
num_rach_per_frame = 51;
btsb->load.rach.total += frames_expired * num_rach_per_frame;
}
return 0;
}
/* measurement information received from bts model */
static int l1sap_info_meas_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap,
struct info_meas_ind_param *info_meas_ind)
{
struct bts_ul_meas ulm;
struct gsm_lchan *lchan;
DEBUGP(DL1P, "MPH_INFO meas ind chan_nr=%02x\n",
info_meas_ind->chan_nr);
lchan = &trx->ts[L1SAP_CHAN2TS(info_meas_ind->chan_nr)]
.lchan[l1sap_chan2ss(info_meas_ind->chan_nr)];
/* in the GPRS case we are not interested in measurement
* processing. The PCU will take care of it */
if (lchan->type == GSM_LCHAN_PDTCH)
return 0;
memset(&ulm, 0, sizeof(ulm));
ulm.ta_offs_qbits = info_meas_ind->ta_offs_qbits;
ulm.ber10k = info_meas_ind->ber10k;
ulm.inv_rssi = info_meas_ind->inv_rssi;
lchan_new_ul_meas(lchan, &ulm);
return 0;
}
/* any L1 MPH_INFO indication prim recevied from bts model */
static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
int rc = 0;
switch (info->type) {
case PRIM_INFO_TIME:
rc = l1sap_info_time_ind(trx->bts, l1sap, &info->u.time_ind);
break;
case PRIM_INFO_MEAS:
rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n",
info->type);
break;
}
return rc;
}
/* activation confirm received from bts model */
static int l1sap_info_act_cnf(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap,
struct info_act_cnf_param *info_act_cnf)
{
struct gsm_lchan *lchan;
LOGP(DL1P, LOGL_INFO, "activate confirm chan_nr=%02x trx=%d\n",
info_act_cnf->chan_nr, trx->nr);
lchan = &trx->ts[L1SAP_CHAN2TS(info_act_cnf->chan_nr)]
.lchan[l1sap_chan2ss(info_act_cnf->chan_nr)];
if (info_act_cnf->cause)
rsl_tx_chan_act_nack(lchan, info_act_cnf->cause);
else
rsl_tx_chan_act_ack(lchan);
return 0;
}
/* activation confirm received from bts model */
static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap,
struct info_act_cnf_param *info_act_cnf)
{
struct gsm_lchan *lchan;
LOGP(DL1P, LOGL_INFO, "deactivate confirm chan_nr=%02x trx=%d\n",
info_act_cnf->chan_nr, trx->nr);
lchan = &trx->ts[L1SAP_CHAN2TS(info_act_cnf->chan_nr)]
.lchan[l1sap_chan2ss(info_act_cnf->chan_nr)];
rsl_tx_rf_rel_ack(lchan);
return 0;
}
/* any L1 MPH_INFO confirm prim recevied from bts model */
static int l1sap_mph_info_cnf(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
int rc = 0;
switch (info->type) {
case PRIM_INFO_ACTIVATE:
rc = l1sap_info_act_cnf(trx, l1sap, &info->u.act_cnf);
break;
case PRIM_INFO_DEACTIVATE:
rc = l1sap_info_rel_cnf(trx, l1sap, &info->u.act_cnf);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO cnf type %d\n",
info->type);
break;
}
return rc;
}
/* PH-RTS-IND prim recevied from bts model */
static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_data_param *rts_ind)
{
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr, link_id;
uint8_t tn, ss;
uint32_t fn;
uint8_t *p, *si;
struct lapdm_entity *le;
struct osmo_phsap_prim pp;
int rc;
chan_nr = rts_ind->chan_nr;
link_id = rts_ind->link_id;
fn = rts_ind->fn;
tn = L1SAP_CHAN2TS(chan_nr);
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "Rx PH-RTS.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
if (L1SAP_IS_PTCCH(rts_ind->fn)) {
pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */,
L1SAP_FN2PTCCHBLOCK(fn));
return 0;
}
pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
L1SAP_FN2MACBLOCK(fn));
return 0;
}
/* reuse PH-RTS.ind for PH-DATA.req */
if (!msg) {
LOGP(DL1P, LOGL_FATAL, "RTS without msg to be reused. Please "
"fix!\n");
abort();
}
msgb_trim(msg, sizeof(*l1sap));
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST,
msg);
msg->l2h = msg->l1h + sizeof(*l1sap);
if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
/* get them from bts->si_buf[] */
si = bts_sysinfo_get(trx->bts, &g_time);
if (si)
memcpy(p, si, GSM_MACBLOCK_LEN);
else
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
} else if (!(chan_nr & 0x80)) { /* only TCH/F, TCH/H, SDCCH/4 and SDCCH/8 have C5 bit cleared */
if (L1SAP_IS_CHAN_TCHH(chan_nr))
ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */
else if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
ss = L1SAP_CHAN2SS_SDCCH4(chan_nr); /* SDCCH/4 */
else if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
ss = L1SAP_CHAN2SS_SDCCH8(chan_nr); /* SDCCH/8 */
else
ss = 0; /* TCH/F */
lchan = &trx->ts[tn].lchan[ss];
if (L1SAP_IS_LINK_SACCH(link_id)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
/* L1-header, if not set/modified by layer 1 */
p[0] = lchan->ms_power_ctrl.current;
p[1] = lchan->rqd_ta;
le = &lchan->lapdm_ch.lapdm_acch;
} else
le = &lchan->lapdm_ch.lapdm_dcch;
rc = lapdm_phsap_dequeue_prim(le, &pp);
if (rc < 0) {
if (L1SAP_IS_LINK_SACCH(link_id)) {
/* No SACCH data from LAPDM pending, send SACCH filling */
uint8_t *si = lchan_sacch_get(lchan);
if (si) {
/* The +2 is empty space where the DSP inserts the L1 hdr */
memcpy(p + 2, si, GSM_MACBLOCK_LEN - 2);
} else
memcpy(p + 2, fill_frame, GSM_MACBLOCK_LEN - 2);
} else if ((!L1SAP_IS_CHAN_TCHF(chan_nr) && !L1SAP_IS_CHAN_TCHH(chan_nr))
|| lchan->rsl_cmode == RSL_CMOD_SPD_SIGN) {
/* send fill frame only, if not TCH/x != Signalling, otherwise send empty frame */
p = msgb_put(msg, GSM_MACBLOCK_LEN);
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
} /* else the message remains empty, so TCH frames are sent */
} else {
/* The +2 is empty space where the DSP inserts the L1 hdr */
if (L1SAP_IS_LINK_SACCH(link_id))
memcpy(p + 2, pp.oph.msg->data + 2, GSM_MACBLOCK_LEN - 2);
else {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN);
/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr);
}
msgb_free(pp.oph.msg);
}
} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
#warning "TODO: Yet another assumption that BS_AG_BLKS_RES=1"
/* if CCCH block is 0, it is AGCH */
rc = bts_ccch_copy_msg(trx->bts, p, &g_time,
(L1SAP_FN2CCCHBLOCK(fn) < 1));
if (rc <= 0)
memcpy(p, fill_frame, GSM_MACBLOCK_LEN);
}
DEBUGP(DL1P, "Tx PH-DATA.req %02u/%02u/%02u chan_nr=%d link_id=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
l1sap_down(trx, l1sap);
/* don't free, because we forwarded data */
return 1;
}
static int check_acc_delay(struct ph_rach_ind_param *rach_ind,
struct gsm_bts_role_bts *btsb, uint8_t *acc_delay)
{
*acc_delay = rach_ind->acc_delay;
return *acc_delay <= btsb->max_ta;
}
/* special case where handover RACH is detected */
static int l1sap_handover_rach(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
{
struct gsm_lchan *lchan;
uint8_t chan_nr;
uint8_t tn, ss;
chan_nr = rach_ind->chan_nr;
tn = L1SAP_CHAN2TS(chan_nr);
ss = l1sap_chan2ss(chan_nr);
lchan = &trx->ts[tn].lchan[ss];
handover_rach(lchan, rach_ind->ra, rach_ind->acc_delay);
/* must return 0, so in case of msg at l1sap, it will be freed */
return 0;
}
/* TCH-RTS-IND prim recevied from bts model */
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
{
struct msgb *resp_msg;
struct osmo_phsap_prim *resp_l1sap, empty_l1sap;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t chan_nr;
uint8_t tn, ss;
uint32_t fn;
chan_nr = rts_ind->chan_nr;
fn = rts_ind->fn;
tn = L1SAP_CHAN2TS(chan_nr);
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "Rx TCH-RTS.ind %02u/%02u/%02u chan_nr=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr);
/* get timeslot and subslot */
tn = L1SAP_CHAN2TS(chan_nr);
if (L1SAP_IS_CHAN_TCHH(chan_nr))
ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */
else
ss = 0; /* TCH/F */
lchan = &trx->ts[tn].lchan[ss];
if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
/* FIXME: we _assume_ that we never miss TDMA
* frames and that we always get to this point
* for every to-be-transmitted voice frame. A
* better solution would be to compute
* rx_user_ts based on how many TDMA frames have
* elapsed since the last call */
lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
}
/* get a msgb from the dl_tx_queue */
resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
if (!resp_msg) {
LOGP(DL1P, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
gsm_lchan_name(lchan));
resp_l1sap = &empty_l1sap;
} else {
resp_msg->l2h = resp_msg->data;
msgb_push(resp_msg, sizeof(*resp_l1sap));
resp_msg->l1h = resp_msg->data;
resp_l1sap = msgb_l1sap_prim(resp_msg);
}
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg);
resp_l1sap->u.tch.chan_nr = chan_nr;
resp_l1sap->u.tch.fn = fn;
DEBUGP(DL1P, "Tx TCH.req %02u/%02u/%02u chan_nr=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr);
l1sap_down(trx, resp_l1sap);
return 0;
}
/* process radio link timeout counter S */
static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
{
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
/* if link loss criterion already reached */
if (lchan->s == 0) {
DEBUGP(DMEAS, "%s radio link counter S already 0.\n",
gsm_lchan_name(lchan));
return;
}
if (bad_frame) {
/* count down radio link counter S */
lchan->s--;
DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n",
gsm_lchan_name(lchan), lchan->s);
if (lchan->s == 0)
rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL);
return;
}
if (lchan->s < btsb->radio_link_timeout) {
/* count up radio link counter S */
lchan->s += 2;
if (lchan->s > btsb->radio_link_timeout)
lchan->s = btsb->radio_link_timeout;
DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n",
gsm_lchan_name(lchan), lchan->s);
}
}
static inline int check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len)
{
uint8_t n_s;
/* if this is the first valid message after enabling Rx
* decryption, we have to enable Tx encryption */
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF)
return 0;
/* HACK: check if it's an I frame, in order to
* ignore some still buffered/queued UI frames received
* before decryption was enabled */
if (data[0] != 0x01)
return 0;
if ((data[1] & 0x01) != 0)
return 0;
n_s = data[1] >> 5;
if (lchan->ciph_ns != n_s)
return 0;
return 1;
}
/* public helper for the test */
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
uint8_t *data, int len)
{
return check_for_first_ciphrd(lchan, data, len);
}
/* DATA received from bts model */
static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind)
{
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
struct lapdm_entity *le;
uint8_t *data = msg->l2h;
int len = msgb_l2len(msg);
uint8_t chan_nr, link_id;
uint8_t tn, ss;
uint32_t fn;
int8_t rssi;
rssi = data_ind->rssi;
chan_nr = data_ind->chan_nr;
link_id = data_ind->link_id;
fn = data_ind->fn;
tn = L1SAP_CHAN2TS(chan_nr);
ss = l1sap_chan2ss(chan_nr);
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "Rx PH-DATA.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
if (len == 0)
return -EINVAL;
if (L1SAP_IS_PTCCH(fn)) {
pcu_tx_data_ind(&trx->ts[tn], 1, fn,
0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn),
data, len, rssi);
return 0;
}
/* drop incomplete UL block */
if (data[0] != 7)
return 0;
/* PDTCH / PACCH frame handling */
pcu_tx_data_ind(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
L1SAP_FN2MACBLOCK(fn), data + 1, len - 1, rssi);
return 0;
}
lchan = &trx->ts[tn].lchan[ss];
/* bad frame */
if (len == 0) {
if (L1SAP_IS_LINK_SACCH(link_id))
radio_link_timeout(lchan, 1);
return -EINVAL;
}
/* report first valid received frame to handover process */
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
handover_frame(lchan);
if (L1SAP_IS_LINK_SACCH(link_id)) {
radio_link_timeout(lchan, 0);
le = &lchan->lapdm_ch.lapdm_acch;
/* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
if (len < 2) {
LOGP(DL1P, LOGL_NOTICE, "SACCH with size %u<2 !?!\n",
len);
return -EINVAL;
}
/* Some brilliant engineer decided that the ordering of
* fields on the Um interface is different from the
* order of fields in RLS. See TS 04.04 (Chapter 7.2)
* vs. TS 08.58 (Chapter 9.3.10). */
lchan->meas.l1_info[0] = data[0] << 3;
lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2;
lchan->meas.l1_info[1] = data[1];
lchan->meas.flags |= LC_UL_M_F_L1_VALID;
lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
} else
le = &lchan->lapdm_ch.lapdm_dcch;
if (check_for_first_ciphrd(lchan, data, len))
l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 1, 0);
/* SDCCH, SACCH and FACCH all go to LAPDm */
msgb_pull(msg, (msg->l2h - msg->data));
msg->l1h = NULL;
lapdm_phsap_up(&l1sap->oph, le);
/* don't free, because we forwarded data */
return 1;
}
/* TCH received from bts model */
static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
struct ph_tch_param *tch_ind)
{
struct msgb *msg = l1sap->oph.msg;
struct gsm_time g_time;
struct gsm_lchan *lchan;
uint8_t tn, ss, chan_nr;
uint32_t fn;
chan_nr = tch_ind->chan_nr;
fn = tch_ind->fn;
tn = L1SAP_CHAN2TS(chan_nr);
if (L1SAP_IS_CHAN_TCHH(chan_nr))
ss = L1SAP_CHAN2SS_TCHH(chan_nr);
else
ss = 0;
lchan = &trx->ts[tn].lchan[ss];
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "Rx TCH.ind %02u/%02u/%02u chan_nr=%d\n",
g_time.t1, g_time.t2, g_time.t3, chan_nr);
msgb_pull(msg, sizeof(*l1sap));
/* hand msg to RTP code for transmission */
if (lchan->abis_ip.rtp_socket)
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
msg->data, msg->len, 160);
/* if loopback is enabled, also queue received RTP data */
if (lchan->loopback) {
struct msgb *tmp;
int count = 0;
/* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 1) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
msgb_enqueue(&lchan->dl_tch_queue, msg);
}
return 0;
}
/* RACH received from bts model */
static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
{
struct gsm_bts *bts = trx->bts;
struct gsm_bts_role_bts *btsb = bts->role;
struct lapdm_channel *lc;
uint8_t acc_delay;
DEBUGP(DL1P, "Rx PH-RA.ind");
lc = &trx->ts[0].lchan[4].lapdm_ch;
/* check for under/overflow / sign */
if (!check_acc_delay(rach_ind, btsb, &acc_delay)) {
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
acc_delay, btsb->max_ta);
return 0;
}
/* check for handover rach */
if (!L1SAP_IS_CHAN_RACH(rach_ind->chan_nr))
return l1sap_handover_rach(trx, l1sap, rach_ind);
/* check for packet access */
if (trx == bts->c0 && L1SAP_IS_PACKET_RACH(rach_ind->ra)) {
LOGP(DL1P, LOGL_INFO, "RACH for packet access\n");
pcu_tx_rach_ind(bts, rach_ind->acc_delay << 2,
rach_ind->ra, rach_ind->fn);
return 0;
}
LOGP(DL1P, LOGL_INFO, "RACH for RR access (toa=%d, ra=%d)\n",
rach_ind->acc_delay, rach_ind->ra);
lapdm_phsap_up(&l1sap->oph, &lc->lapdm_dcch);
return 0;
}
/* any L1 prim received from bts model */
int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
struct msgb *msg = l1sap->oph.msg;
int rc = 0;
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_INDICATION):
rc = l1sap_mph_info_ind(trx, l1sap, &l1sap->u.info);
break;
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_CONFIRM):
rc = l1sap_mph_info_cnf(trx, l1sap, &l1sap->u.info);
break;
case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
break;
case OSMO_PRIM(PRIM_TCH_RTS, PRIM_OP_INDICATION):
rc = l1sap_tch_rts_ind(trx, l1sap, &l1sap->u.tch);
break;
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
to_gsmtap(trx, l1sap);
rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
break;
case OSMO_PRIM(PRIM_TCH, PRIM_OP_INDICATION):
rc = l1sap_tch_ind(trx, l1sap, &l1sap->u.tch);
break;
case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
to_gsmtap(trx, l1sap);
rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown prim %d op %d\n",
l1sap->oph.primitive, l1sap->oph.operation);
break;
}
/* Special return value '1' means: do not free */
if (rc != 1)
msgb_free(msg);
return rc;
}
/* any L1 prim sent to bts model */
static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
if (OSMO_PRIM_HDR(&l1sap->oph) ==
OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST))
to_gsmtap(trx, l1sap);
return bts_model_l1sap_down(trx, l1sap);
}
/* pcu (socket interface) sends us a data request primitive */
int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
{
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
struct gsm_time g_time;
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1P, "TX packet data %02u/%02u/%02u is_ptcch=%d trx=%d ts=%d "
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
g_time.t3, is_ptcch, ts->trx->nr, ts->nr, block_nr, arfcn, len);
msg = l1sap_msgb_alloc(len);
l1sap = msgb_l1sap_prim(msg);
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST,
msg);
l1sap->u.data.chan_nr = 0x08 | ts->nr;
l1sap->u.data.link_id = 0x00;
l1sap->u.data.fn = fn;
msg->l2h = msgb_put(msg, len);
memcpy(msg->l2h, data, len);
return l1sap_down(ts->trx, l1sap);
}
/*! \brief call-back function for incoming RTP */
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
unsigned int rtp_pl_len)
{
struct gsm_lchan *lchan = rs->priv;
struct msgb *msg, *tmp;
struct osmo_phsap_prim *l1sap;
int count = 0;
msg = l1sap_msgb_alloc(rtp_pl_len);
if (!msg)
return;
memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
msgb_pull(msg, sizeof(*l1sap));
/* make sure the queue doesn't get too long */
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
count++;
while (count >= 2) {
tmp = msgb_dequeue(&lchan->dl_tch_queue);
msgb_free(tmp);
count--;
}
msgb_enqueue(&lchan->dl_tch_queue, msg);
}
static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr,
enum osmo_mph_info_type type, uint8_t sacch_only)
{
struct osmo_phsap_prim l1sap;
memset(&l1sap, 0, sizeof(l1sap));
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_REQUEST,
NULL);
l1sap.u.info.type = type;
l1sap.u.info.u.act_req.chan_nr = chan_nr;
l1sap.u.info.u.act_req.sacch_only = sacch_only;
return l1sap_down(trx, &l1sap);
}
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp)
{
struct gsm_bts_role_bts *btsb = trx->bts->role;
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
struct gsm48_chan_desc *cd;
int rc;
LOGP(DL1P, LOGL_INFO, "activating channel chan_nr=%02x trx=%d\n",
chan_nr, trx->nr);
/* osmo-pcu calls this without a valid 'tp' parameter, so we
* need to make sure ew don't crash here */
if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) &&
TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) {
cd = (struct gsm48_chan_desc *)
TLVP_VAL(tp, GSM48_IE_CHANDESC_2);
/* our L1 only supports one global TSC for all channels
* one one TRX, so we need to make sure not to activate
* channels with a different TSC!! */
if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) {
LOGP(DRSL, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
cd->h0.tsc, lchan->ts->trx->bts->bsic & 7);
return -RSL_ERR_SERV_OPT_UNIMPL;
}
}
lchan->sacch_deact = 0;
lchan->s = btsb->radio_link_timeout;
rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0);
if (rc)
return -RSL_ERR_EQUIPMENT_FAIL;
return 0;
}
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
LOGP(DL1P, LOGL_INFO, "deactivating channel chan_nr=%02x trx=%d\n",
chan_nr, trx->nr);
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
0);
}
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
LOGP(DL1P, LOGL_INFO, "deactivating sacch chan_nr=%02x trx=%d\n",
chan_nr, trx->nr);
lchan->sacch_deact = 1;
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
1);
}
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
LOGP(DL1P, LOGL_INFO, "modifying channel chan_nr=%02x trx=%d\n",
chan_nr, trx->nr);
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
}
osmo-bts-0.4.0/src/common/lchan.c 0000664 0000000 0000000 00000001607 12600264262 0016572 0 ustar 00root root 0000000 0000000 /* OsmoBTS lchan interface */
/* (C) 2012 by Holger Hans Peter Freyther
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state)
{
lchan->state = state;
}
osmo-bts-0.4.0/src/common/load_indication.c 0000664 0000000 0000000 00000005573 12600264262 0020633 0 ustar 00root root 0000000 0000000 /* Support for generating RSL Load Indication */
/* (C) 2011 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
static void reset_load_counters(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
/* re-set the counters */
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
}
static void load_timer_cb(void *data)
{
struct gsm_bts *bts = data;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
unsigned int pch_percent, rach_percent;
/* compute percentages */
if (btsb->load.ccch.pch_total == 0)
pch_percent = 0;
else
pch_percent = (btsb->load.ccch.pch_used * 100) /
btsb->load.ccch.pch_total;
if (pch_percent >= btsb->load.ccch.load_ind_thresh) {
/* send RSL load indication message to BSC */
uint16_t buffer_space = paging_buffer_space(btsb->paging_state);
rsl_tx_ccch_load_ind_pch(bts, buffer_space);
} else {
/* This is an extenstion of TS 08.58. We don't only
* send load indications if the load is above threshold,
* but we also explicitly indicate that we are below
* threshold by using the magic value 0xffff */
rsl_tx_ccch_load_ind_pch(bts, 0xffff);
}
if (btsb->load.rach.total == 0)
rach_percent = 0;
else
rach_percent = (btsb->load.rach.busy * 100) /
btsb->load.rach.total;
if (rach_percent >= btsb->load.ccch.load_ind_thresh) {
/* send RSL load indication message to BSC */
rsl_tx_ccch_load_ind_rach(bts, btsb->load.rach.total,
btsb->load.rach.busy,
btsb->load.rach.access);
}
reset_load_counters(bts);
/* re-schedule the timer */
osmo_timer_schedule(&btsb->load.ccch.timer,
btsb->load.ccch.load_ind_period, 0);
}
void load_timer_start(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
btsb->load.ccch.timer.data = bts;
btsb->load.ccch.timer.cb = load_timer_cb;
reset_load_counters(bts);
osmo_timer_schedule(&btsb->load.ccch.timer,
btsb->load.ccch.load_ind_period, 0);
}
void load_timer_stop(struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
osmo_timer_del(&btsb->load.ccch.timer);
}
osmo-bts-0.4.0/src/common/logging.c 0000664 0000000 0000000 00000007175 12600264262 0017141 0 ustar 00root root 0000000 0000000 /* libosmocore logging support */
/* (C) 2011 by Andreas Eversberg
* (C) 2011 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
static struct log_info_cat bts_log_info_cat[] = {
[DRSL] = {
.name = "DRSL",
.description = "A-bis Radio Siganlling Link (RSL)",
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DOML] = {
.name = "DOML",
.description = "A-bis Network Management / O&M (NM/OML)",
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DRLL] = {
.name = "DRLL",
.description = "A-bis Radio Link Layer (RLL)",
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRR] = {
.name = "DRR",
.description = "Layer3 Radio Resource (RR)",
.color = "\033[1;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
.description = "Radio Measurement Processing",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DL1C] = {
.name = "DL1C",
.description = "Layer 1",
.loglevel = LOGL_INFO,
.enabled = 1,
},
[DL1P] = {
.name = "DL1P",
.description = "Layer 1 Primitives",
.loglevel = LOGL_INFO,
.enabled = 0,
},
[DDSP] = {
.name = "DDSP",
.description = "DSP Trace Messages",
.loglevel = LOGL_DEBUG,
.enabled = 1,
},
[DABIS] = {
.name = "DABIS",
.description = "A-bis Intput Subsystem",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRTP] = {
.name = "DRTP",
.description = "Realtime Transfer Protocol",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DPCU] = {
.name = "DPCU",
.description = "PCU interface",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DHO] = {
.name = "DHO",
.description = "Handover",
.color = "\033[0;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRX] = {
.name = "DTRX",
.description = "TRX interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DLOOP] = {
.name = "DLOOP",
.description = "Control loops",
.color = "\033[0;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
#if 0
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DLLC] = {
.name = "DLLC",
.description = "GPRS Logical Link Control Protocol (LLC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
#endif
};
const struct log_info bts_log_info = {
.cat = bts_log_info_cat,
.num_cat = ARRAY_SIZE(bts_log_info_cat),
};
int bts_log_init(const char *category_mask)
{
osmo_init_logging(&bts_log_info);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
return 0;
}
osmo-bts-0.4.0/src/common/measurement.c 0000664 0000000 0000000 00000013643 12600264262 0020035 0 ustar 00root root 0000000 0000000
#include
#include
#include
#include
#include
#include
/* TS 05.08, Chapter 8.4.1 */
/* measurement period ends at fn % 104 == ? */
static const uint8_t tchf_meas_rep_fn104[] = {
[0] = 103,
[1] = 12,
[2] = 25,
[3] = 38,
[4] = 51,
[5] = 64,
[6] = 77,
[7] = 90,
};
static const uint8_t tchh0_meas_rep_fn104[] = {
[0] = 103,
[1] = 103,
[2] = 25,
[3] = 25,
[4] = 51,
[5] = 51,
[6] = 77,
[7] = 77,
};
static const uint8_t tchh1_meas_rep_fn104[] = {
[0] = 12,
[1] = 12,
[2] = 38,
[3] = 38,
[4] = 64,
[5] = 64,
[6] = 90,
[7] = 90,
};
/* determine if a measurement period ends at the given frame number */
static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts,
unsigned int subch, uint32_t fn)
{
unsigned int fn_mod;
const uint8_t *tbl;
int rc = 0;
if (ts >= 8)
return -EINVAL;
if (pchan >= _GSM_PCHAN_MAX)
return -EINVAL;
switch (pchan) {
case GSM_PCHAN_TCH_F:
fn_mod = fn % 104;
if (tchf_meas_rep_fn104[ts] == fn_mod)
rc = 1;
break;
case GSM_PCHAN_TCH_H:
fn_mod = fn % 104;
if (subch == 0)
tbl = tchh0_meas_rep_fn104;
else
tbl = tchh1_meas_rep_fn104;
if (tbl[ts] == fn_mod)
rc = 1;
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
fn_mod = fn % 102;
if (fn_mod == 11)
rc = 1;
break;
case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
fn_mod = fn % 102;
if (fn_mod == 36)
rc = 1;
break;
default:
rc = 0;
break;
}
return rc;
}
/* receive a L1 uplink measurement from L1 */
int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm)
{
DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n",
gsm_lchan_name(lchan), lchan->meas.num_ul_meas);
if (lchan->state != LCHAN_S_ACTIVE) {
LOGP(DMEAS, LOGL_NOTICE, "%s measurement during state: %s\n",
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
}
if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) {
LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n",
gsm_lchan_name(lchan));
return -ENOSPC;
}
memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm,
sizeof(*ulm));
return 0;
}
/* input: BER in steps of .01%, i.e. percent/100 */
static uint8_t ber10k_to_rxqual(uint32_t ber10k)
{
/* 05.08 / 8.2.4 */
if (ber10k < 20)
return 0;
if (ber10k < 40)
return 1;
if (ber10k < 80)
return 2;
if (ber10k < 160)
return 3;
if (ber10k < 320)
return 4;
if (ber10k < 640)
return 5;
if (ber10k < 1280)
return 6;
return 7;
}
static int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
{
struct gsm_meas_rep_unidir *mru;
uint32_t ber_full_sum = 0;
uint32_t irssi_full_sum = 0;
uint32_t ber_sub_sum = 0;
uint32_t irssi_sub_sum = 0;
int32_t taqb_sum = 0;
unsigned int num_meas_sub = 0;
int i;
/* if measurement period is not complete, abort */
if (!is_meas_complete(lchan->ts->pchan, lchan->ts->nr,
lchan->nr, fn))
return 0;
/* if there are no measurements, skip computation */
if (lchan->meas.num_ul_meas == 0)
return 0;
/* compute the actual measurements */
/* step 1: add up */
for (i = 0; i < lchan->meas.num_ul_meas; i++) {
struct bts_ul_meas *m = &lchan->meas.uplink[i];
ber_full_sum += m->ber10k;
irssi_full_sum += m->inv_rssi;
taqb_sum += m->ta_offs_qbits;
if (m->is_sub) {
num_meas_sub++;
ber_sub_sum += m->ber10k;
irssi_sub_sum += m->inv_rssi;
}
}
/* step 2: divide */
ber_full_sum = ber_full_sum / lchan->meas.num_ul_meas;
irssi_full_sum = irssi_full_sum / lchan->meas.num_ul_meas;
taqb_sum = taqb_sum / lchan->meas.num_ul_meas;
if (num_meas_sub) {
ber_sub_sum = ber_sub_sum / num_meas_sub;
irssi_sub_sum = irssi_sub_sum / num_meas_sub;
}
DEBUGP(DMEAS, "%s Computed TA(% 4dqb) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), "
"BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n", gsm_lchan_name(lchan),
taqb_sum, ber_full_sum/100,
ber_full_sum%100, irssi_full_sum, ber_sub_sum/100, ber_sub_sum%100,
irssi_sub_sum);
/* store results */
mru = &lchan->meas.ul_res;
mru->full.rx_lev = dbm2rxlev((int)irssi_full_sum * -1);
mru->sub.rx_lev = dbm2rxlev((int)irssi_sub_sum * -1);
mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.flags |= LC_UL_M_F_RES_VALID;
lchan->meas.num_ul_meas = 0;
/* send a signal indicating computation is complete */
return 1;
}
/* build the 3 byte RSL uplinke measurement IE content */
int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf)
{
struct gsm_meas_rep_unidir *mru = &lchan->meas.ul_res;
buf[0] = (mru->full.rx_lev & 0x3f); /* FIXME: DTXu support */
buf[1] = (mru->sub.rx_lev & 0x3f);
buf[2] = ((mru->full.rx_qual & 7) << 3) | (mru->sub.rx_qual & 7);
return 3;
}
/* Copied from OpenBSC and enlarged to _GSM_PCHAN_MAX */
static const uint8_t subslots_per_pchan[_GSM_PCHAN_MAX] = {
[GSM_PCHAN_NONE] = 0,
[GSM_PCHAN_CCCH] = 0,
[GSM_PCHAN_CCCH_SDCCH4] = 4,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
/* FIXME: what about dynamic TCH_F_TCH_H ? */
[GSM_PCHAN_TCH_F_PDCH] = 1,
};
static int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
{
int i;
const int num_subslots = subslots_per_pchan[ts->pchan];
for (i = 0; i < num_subslots; ++i) {
struct gsm_lchan *lchan = &ts->lchan[i];
if (lchan->state != LCHAN_S_ACTIVE)
continue;
switch (lchan->type) {
case GSM_LCHAN_SDCCH:
case GSM_LCHAN_TCH_F:
case GSM_LCHAN_TCH_H:
case GSM_LCHAN_PDTCH:
lchan_meas_check_compute(lchan, fn);
break;
default:
break;
}
}
return 0;
}
/* needs to be called once every TDMA frame ! */
int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn)
{
int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
ts_meas_check_compute(ts, fn);
}
return 0;
}
osmo-bts-0.4.0/src/common/msg_utils.c 0000664 0000000 0000000 00000011655 12600264262 0017517 0 ustar 00root root 0000000 0000000 /* (C) 2014 by sysmocom s.f.m.c. GmbH
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
static int check_fom(struct abis_om_hdr *omh, size_t len)
{
if (omh->length != len) {
LOGP(DL1C, LOGL_ERROR, "Incorrect OM hdr length value %d %zu\n",
omh->length, len);
return -1;
}
if (len < sizeof(struct abis_om_fom_hdr)) {
LOGP(DL1C, LOGL_ERROR, "FOM header insufficient space %zu %zu\n",
len, sizeof(struct abis_om_fom_hdr));
return -1;
}
return 0;
}
static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_size)
{
int type;
size_t size;
if (msg_size < 1) {
LOGP(DL1C, LOGL_ERROR, "No ManId Length Indicator %zu\n",
msg_size);
return -1;
}
if (omh->data[0] >= msg_size - 1) {
LOGP(DL1C, LOGL_ERROR,
"Insufficient message space for this ManId Length %d %zu\n",
omh->data[0], msg_size - 1);
return -1;
}
if (omh->data[0] == sizeof(abis_nm_ipa_magic) &&
strncmp(abis_nm_ipa_magic, (const char *)omh->data + 1,
sizeof(abis_nm_ipa_magic)) == 0) {
type = OML_MSG_TYPE_IPA;
size = sizeof(abis_nm_ipa_magic) + 1;
} else if (omh->data[0] == sizeof(abis_nm_osmo_magic) &&
strncmp(abis_nm_osmo_magic, (const char *) omh->data + 1,
sizeof(abis_nm_osmo_magic)) == 0) {
type = OML_MSG_TYPE_OSMO;
size = sizeof(abis_nm_osmo_magic) + 1;
} else {
LOGP(DL1C, LOGL_ERROR, "Manuf Label Unknown\n");
return -1;
}
/* we have verified that the vendor string fits */
msg->l3h = omh->data + size;
if (check_fom(omh, msgb_l3len(msg)) != 0)
return -1;
return type;
}
/**
* Return 0 in case the IPA structure is okay and in this
* case the l2h will be set to the beginning of the data.
*/
int msg_verify_ipa_structure(struct msgb *msg)
{
struct ipaccess_head *hh;
if (msgb_l1len(msg) < sizeof(struct ipaccess_head)) {
LOGP(DL1C, LOGL_ERROR,
"Ipa header insufficient space %d %d\n",
msgb_l1len(msg), sizeof(struct ipaccess_head));
return -1;
}
hh = (struct ipaccess_head *) msg->l1h;
if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
LOGP(DL1C, LOGL_ERROR,
"Incorrect ipa header msg size %d %d\n",
ntohs(hh->len), msgb_l1len(msg) - sizeof(struct ipaccess_head));
return -1;
}
if (hh->proto == IPAC_PROTO_OSMO) {
struct ipaccess_head_ext *hh_ext = (struct ipaccess_head_ext *) hh->data;
if (ntohs(hh->len) < sizeof(*hh_ext)) {
LOGP(DL1C, LOGL_ERROR, "IPA length shorter than OSMO header\n");
return -1;
}
msg->l2h = hh_ext->data;
} else
msg->l2h = hh->data;
return 0;
}
/**
* \brief Verify the structure of the OML message and set l3h
*
* This function verifies that the data in \param in msg is a proper
* OML message. This code assumes that msg->l2h points to the
* beginning of the OML message. In the successful case the msg->l3h
* will be set and will point to the FOM header. The value is undefined
* in all other cases.
*
* \param msg The message to analyze starting from msg->l2h.
* \return In case the structure is correct a positive number will be
* returned and msg->l3h will point to the FOM. The number is a
* classification of the vendor type of the message.
*/
int msg_verify_oml_structure(struct msgb *msg)
{
struct abis_om_hdr *omh;
if (msgb_l2len(msg) < sizeof(*omh)) {
LOGP(DL1C, LOGL_ERROR, "Om header insufficient space %d %d\n",
msgb_l2len(msg), sizeof(*omh));
return -1;
}
omh = (struct abis_om_hdr *) msg->l2h;
if (omh->mdisc != ABIS_OM_MDISC_FOM &&
omh->mdisc != ABIS_OM_MDISC_MANUF) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om mdisc value %x\n",
omh->mdisc);
return -1;
}
if (omh->placement != ABIS_OM_PLACEMENT_ONLY) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om placement value %x %x\n",
omh->placement, ABIS_OM_PLACEMENT_ONLY);
return -1;
}
if (omh->sequence != 0) {
LOGP(DL1C, LOGL_ERROR, "Incorrect om sequence value %d\n",
omh->sequence);
return -1;
}
if (omh->mdisc == ABIS_OM_MDISC_MANUF)
return check_manuf(msg, omh, msgb_l2len(msg) - sizeof(*omh));
msg->l3h = omh->data;
if (check_fom(omh, msgb_l3len(msg)) != 0)
return -1;
return OML_MSG_TYPE_ETSI;
}
osmo-bts-0.4.0/src/common/oml.c 0000664 0000000 0000000 00000100653 12600264262 0016275 0 ustar 00root root 0000000 0000000 /* GSM TS 12.21 O&M / OML, BTS side */
/* (C) 2011 by Andreas Eversberg
* (C) 2011-2013 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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 .
*
*/
/*
* Operation and Maintainance Messages
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* FIXME: move this to libosmocore */
static struct tlv_definition abis_nm_att_tlvdef_ipa = {
.def = {
/* ip.access specifics */
[NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
[NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
[NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
[NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
[NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
[NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
[NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
[NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
[NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
[NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
[NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
[NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
[NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
},
};
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
/*
* support
*/
struct tlv_parsed *tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
{
struct tlv_parsed *tp_out;
unsigned int i;
tp_out = talloc_zero(ctx, struct tlv_parsed);
if (!tp_out)
return NULL;
/* if the original is NULL, return empty tlvp */
if (!tp_orig)
return tp_out;
for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
unsigned int len = tp_orig->lv[i].len;
tp_out->lv[i].len = len;
if (len && tp_out->lv[i].val) {
tp_out->lv[i].val = talloc_zero_size(tp_out, len);
if (!tp_out->lv[i].val) {
talloc_free(tp_out);
return NULL;
}
memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val, len);
}
}
return tp_out;
}
/* merge all attributes of 'new' into 'out' */
int tlvp_merge(struct tlv_parsed *out, const struct tlv_parsed *new)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(out->lv); i++) {
unsigned int len = new->lv[i].len;
if (len == 0 || new->lv[i].val == NULL)
continue;
if (out->lv[i].val) {
talloc_free((uint8_t *) out->lv[i].val);
out->lv[i].len = 0;
}
out->lv[i].val = talloc_zero_size(out, len);
if (!out->lv[i].val)
return -ENOMEM;
memcpy((uint8_t *) out->lv[i].val, new->lv[i].val, len);
}
return 0;
}
static int oml_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len)
{
return tlv_parse(tp, &abis_nm_att_tlvdef_ipa, buf, len, 0, 0);
}
struct msgb *oml_msgb_alloc(void)
{
return msgb_alloc_headroom(1024, 128, "OML");
}
int oml_send_msg(struct msgb *msg, int is_manuf)
{
struct abis_om_hdr *omh;
if (is_manuf) {
/* length byte, string + 0 termination */
uint8_t *manuf = msgb_push(msg, 1 + sizeof(abis_nm_ipa_magic));
manuf[0] = strlen(abis_nm_ipa_magic)+1;
memcpy(manuf+1, abis_nm_ipa_magic, strlen(abis_nm_ipa_magic));
}
/* Push the main OML header and send it off */
omh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*omh));
if (is_manuf)
omh->mdisc = ABIS_OM_MDISC_MANUF;
else
omh->mdisc = ABIS_OM_MDISC_FOM;
omh->placement = ABIS_OM_PLACEMENT_ONLY;
omh->sequence = 0;
omh->length = msgb_l3len(msg);
msg->l2h = (uint8_t *)omh;
return abis_oml_sendmsg(msg);
}
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type)
{
struct abis_om_fom_hdr *foh;
msg->l3h = msgb_push(msg, sizeof(*foh));
foh = (struct abis_om_fom_hdr *) msg->l3h;
foh->msg_type = msg_type;
foh->obj_class = mo->obj_class;
memcpy(&foh->obj_inst, &mo->obj_inst, sizeof(foh->obj_inst));
/* FIXME: This assumption may not always be correct */
msg->trx = mo->bts->c0;
return oml_send_msg(msg, 0);
}
/* FIXME: move to gsm_data_shared */
static char mo_buf[128];
char *gsm_abis_mo_name(const struct gsm_abis_mo *mo)
{
snprintf(mo_buf, sizeof(mo_buf), "OC=%s INST=(%02x,%02x,%02x)",
get_value_string(abis_nm_obj_class_names, mo->obj_class),
mo->obj_inst.bts_nr, mo->obj_inst.trx_nr, mo->obj_inst.ts_nr);
return mo_buf;
}
/* 8.8.1 sending State Changed Event Report */
int oml_tx_state_changed(struct gsm_abis_mo *mo)
{
struct msgb *nmsg;
LOGP(DOML, LOGL_INFO, "%s Tx STATE CHG REP\n", gsm_abis_mo_name(mo));
nmsg = oml_msgb_alloc();
if (!nmsg)
return -ENOMEM;
/* 9.4.38 Operational State */
msgb_tv_put(nmsg, NM_ATT_OPER_STATE, mo->nm_state.operational);
/* 9.4.7 Availability Status */
msgb_tl16v_put(nmsg, NM_ATT_AVAIL_STATUS, 1, &mo->nm_state.availability);
return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP);
}
/* First initialization of MO, does _not_ generate state changes */
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state)
{
mo->nm_state.availability = avail_state;
mo->nm_state.operational = op_state;
}
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
{
int rc = 0;
if ((op_state != -1 && mo->nm_state.operational != op_state) ||
(avail_state != -1 && mo->nm_state.availability != avail_state)) {
if (avail_state != -1) {
LOGP(DOML, LOGL_INFO, "%s AVAIL STATE %s -> %s\n",
gsm_abis_mo_name(mo),
abis_nm_avail_name(mo->nm_state.availability),
abis_nm_avail_name(avail_state));
mo->nm_state.availability = avail_state;
}
if (op_state != -1) {
LOGP(DOML, LOGL_INFO, "%s OPER STATE %s -> %s\n",
gsm_abis_mo_name(mo),
abis_nm_opstate_name(mo->nm_state.operational),
abis_nm_opstate_name(op_state));
mo->nm_state.operational = op_state;
osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
}
/* send state change report */
rc = oml_tx_state_changed(mo);
}
return rc;
}
int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type,
uint8_t cause)
{
struct msgb *msg;
uint8_t new_msg_type;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
if (cause) {
new_msg_type = orig_msg_type + 2;
msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause);
} else {
new_msg_type = orig_msg_type + 1;
}
return oml_mo_send_msg(mo, msg, new_msg_type);
}
int oml_mo_statechg_ack(struct gsm_abis_mo *mo)
{
struct msgb *msg;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
msgb_tv_put(msg, NM_ATT_ADM_STATE, mo->nm_state.administrative);
return oml_mo_send_msg(mo, msg, NM_MT_CHG_ADM_STATE_ACK);
}
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause)
{
return oml_mo_fom_ack_nack(mo, NM_MT_CHG_ADM_STATE, nack_cause);
}
int oml_mo_opstart_ack(struct gsm_abis_mo *mo)
{
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0);
}
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause)
{
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, nack_cause);
}
int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
{
struct abis_om_hdr *old_oh = msgb_l2(old_msg);
struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg);
struct msgb *msg;
struct abis_om_fom_hdr *foh;
int is_manuf = 0;
msg = oml_msgb_alloc();
if (!msg)
return -ENOMEM;
/* make sure to respond with MANUF if request was MANUF */
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
is_manuf = 1;
msg->trx = old_msg->trx;
/* copy over old FOM-Header and later only change the msg_type */
msg->l3h = msgb_push(msg, sizeof(*foh));
foh = (struct abis_om_fom_hdr *) msg->l3h;
memcpy(foh, old_foh, sizeof(*foh));
/* alter message type */
if (cause) {
LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %s.\n",
abis_nm_nack_cause_name(cause));
foh->msg_type += 2; /* nack */
/* add cause */
msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause);
} else {
LOGP(DOML, LOGL_DEBUG, "Sending FOM ACK.\n");
foh->msg_type++; /* ack */
}
return oml_send_msg(msg, is_manuf);
}
/*
* Formatted O&M messages
*/
/* 8.3.7 sending SW Activated Report */
int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo)
{
struct msgb *nmsg;
LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo));
nmsg = oml_msgb_alloc();
if (!nmsg)
return -ENOMEM;
msgb_put(nmsg, sizeof(struct abis_om_fom_hdr));
return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP);
}
/* TS 12.21 9.4.53 */
enum abis_nm_t200_idx {
T200_SDCCH = 0,
T200_FACCH_F = 1,
T200_FACCH_H = 2,
T200_SACCH_TCH_SAPI0 = 3,
T200_SACCH_SDCCH = 4,
T200_SDCCH_SAPI3 = 5,
T200_SACCH_TCH_SAPI3 = 6
};
/* TS 12.21 9.4.53 */
static const uint8_t abis_nm_t200_mult[] = {
[T200_SDCCH] = 5,
[T200_FACCH_F] = 5,
[T200_FACCH_H] = 5,
[T200_SACCH_TCH_SAPI0] = 10,
[T200_SACCH_SDCCH] = 10,
[T200_SDCCH_SAPI3] = 5,
[T200_SACCH_TCH_SAPI3] = 10
};
/* 8.6.1 Set BTS Attributes has been received */
static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct tlv_parsed tp, *tp_merged;
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
int rc, i;
const uint8_t *payload;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx SET BTS ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* Test for globally unsupported stuff here */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
const uint16_t *value = (const uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
uint16_t arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
LOGP(DOML, LOGL_NOTICE, "MSG: %s\n", osmo_hexdump(msgb_l3(msg), msgb_l3len(msg)));
LOGP(DOML, LOGL_NOTICE, "L3=%p, VAL=%p, DIF=%tu\n", msgb_l3(msg), value,
(void *)value - (void *) msgb_l3(msg));
if (arfcn > 1024) {
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
}
}
/* 9.4.52 Starting Time */
if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) {
return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP);
}
/* merge existing BTS attributes with new attributes */
tp_merged = tlvp_copy(bts->mo.nm_attr, bts);
tlvp_merge(tp_merged, &tp);
/* Ask BTS driver to validate new merged attributes */
rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts);
if (rc < 0) {
talloc_free(tp_merged);
return oml_fom_ack_nack(msg, -rc);
}
/* Success: replace old BTS attributes with new */
talloc_free(bts->mo.nm_attr);
bts->mo.nm_attr = tp_merged;
/* ... and actually still parse them */
/* 9.4.25 Interference Level Boundaries */
if (TLVP_PRESENT(&tp, NM_ATT_INTERF_BOUND)) {
payload = TLVP_VAL(&tp, NM_ATT_INTERF_BOUND);
for (i = 0; i < 6; i++) {
int16_t boundary = *payload;
btsb->interference.boundary[i] = -1 * boundary;
}
}
/* 9.4.24 Intave Parameter */
if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM))
btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM);
/* 9.4.14 Connection Failure Criterion */
if (TLVP_PRESENT(&tp, NM_ATT_CONN_FAIL_CRIT)) {
const uint8_t *val = TLVP_VAL(&tp, NM_ATT_CONN_FAIL_CRIT);
if (TLVP_LEN(&tp, NM_ATT_CONN_FAIL_CRIT) < 2
|| val[0] != 0x01 || val[1] < 4 || val[1] > 64) {
LOGP(DOML, LOGL_NOTICE, "Given Conn. Failure Criterion "
"not supported. Please use critetion 0x01 with "
"RADIO_LINK_TIMEOUT value of 4..64\n");
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
}
btsb->radio_link_timeout = val[1];
}
/* if val[0] != 0x01: can be 'operator dependent' and needs to
* be parsed by bts driver */
/* 9.4.53 T200 */
if (TLVP_PRESENT(&tp, NM_ATT_T200)) {
payload = TLVP_VAL(&tp, NM_ATT_T200);
for (i = 0; i < ARRAY_SIZE(btsb->t200_ms); i++)
btsb->t200_ms[i] = payload[i] * abis_nm_t200_mult[i];
}
/* 9.4.31 Maximum Timing Advance */
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA))
btsb->max_ta = *TLVP_VAL(&tp, NM_ATT_MAX_TA);
/* 9.4.39 Overload Period */
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
btsb->load.overload_period = *TLVP_VAL(&tp, NM_ATT_OVERL_PERIOD);
/* 9.4.12 CCCH Load Threshold */
if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_T))
btsb->load.ccch.load_ind_thresh = *TLVP_VAL(&tp, NM_ATT_CCCH_L_T);
/* 9.4.11 CCCH Load Indication Period */
if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_I_P))
btsb->load.ccch.load_ind_period = *TLVP_VAL(&tp, NM_ATT_CCCH_L_I_P);
/* 9.4.44 RACH Busy Threshold */
if (TLVP_PRESENT(&tp, NM_ATT_RACH_B_THRESH)) {
int16_t thresh = *TLVP_VAL(&tp, NM_ATT_RACH_B_THRESH);
btsb->load.rach.busy_thresh = -1 * thresh;
}
/* 9.4.45 RACH Load Averaging Slots */
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) {
btsb->load.rach.averaging_slots =
ntohs(tlvp_val16_unal(&tp, NM_ATT_LDAVG_SLOTS));
}
/* 9.4.10 BTS Air Timer */
if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) {
uint8_t t3105 = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER);
if (t3105 == 0) {
LOGP(DOML, LOGL_NOTICE,
"T3105 must have a value != 0.\n");
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
}
btsb->t3105_ms = t3105 * 10;
}
/* 9.4.37 NY1 */
if (TLVP_PRESENT(&tp, NM_ATT_NY1))
btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1);
/* 9.4.8 BCCH ARFCN */
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN))
bts->c0->arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
/* 9.4.9 BSIC */
if (TLVP_PRESENT(&tp, NM_ATT_BSIC))
bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC);
/* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_BTS, bts);
}
/* 8.6.2 Set Radio Attributes has been received */
static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct tlv_parsed tp, *tp_merged;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx SET RADIO CARRIER ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* merge existing BTS attributes with new attributes */
tp_merged = tlvp_copy(trx->mo.nm_attr, trx->bts);
tlvp_merge(tp_merged, &tp);
/* Ask BTS driver to validate new merged attributes */
rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx);
if (rc < 0) {
talloc_free(tp_merged);
return oml_fom_ack_nack(msg, -rc);
}
/* Success: replace old BTS attributes with new */
talloc_free(trx->mo.nm_attr);
trx->mo.nm_attr = tp_merged;
/* ... and actually still parse them */
/* 9.4.47 RF Max Power Reduction */
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R) * 2;
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d dBm\n",
trx->max_power_red);
}
/* 9.4.5 ARFCN List */
#if 0
if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
uint16_t _value;
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
uint16_t arfcn;
int i;
for (i = 0; i < length; i++) {
memcpy(&_value, value, 2);
arfcn = ntohs(_value);
value += 2;
if (arfcn > 1024)
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
trx->arfcn_list[i] = arfcn;
LOGP(DOML, LOGL_INFO, " ARFCN list = %d\n", trx->arfcn_list[i]);
}
trx->arfcn_num = length;
} else
trx->arfcn_num = 0;
#else
if (trx != trx->bts->c0 && TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
const uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
uint16_t _value;
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
uint16_t arfcn;
if (length != 2) {
LOGP(DOML, LOGL_ERROR, "Expecting only one ARFCN, "
"because hopping not supported\n");
/* FIXME: send NACK */
return -ENOTSUP;
}
memcpy(&_value, value, 2);
arfcn = ntohs(_value);
value += 2;
if (arfcn > 1024)
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
trx->arfcn = arfcn;
}
#endif
/* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx);
}
static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
{
struct gsm_lchan *lchan;
unsigned int i;
switch (ts->pchan) {
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
/* fallthrough */
case GSM_PCHAN_CCCH_SDCCH4:
for (i = 0; i < 4; i++) {
lchan = &ts->lchan[i];
if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH
&& i == 2) {
lchan->type = GSM_LCHAN_CBCH;
} else {
lchan->type = GSM_LCHAN_SDCCH;
}
}
/* fallthrough */
case GSM_PCHAN_CCCH:
lchan = &ts->lchan[4];
lchan->type = GSM_LCHAN_CCCH;
break;
case GSM_PCHAN_TCH_F:
lchan = &ts->lchan[0];
lchan->type = GSM_LCHAN_TCH_F;
break;
case GSM_PCHAN_TCH_H:
for (i = 0; i < 2; i++) {
lchan = &ts->lchan[i];
lchan->type = GSM_LCHAN_TCH_H;
}
break;
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
/* fallthrough */
case GSM_PCHAN_SDCCH8_SACCH8C:
for (i = 0; i < 8; i++) {
lchan = &ts->lchan[i];
if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH
&& i == 2) {
lchan->type = GSM_LCHAN_CBCH;
} else {
lchan->type = GSM_LCHAN_SDCCH;
}
}
break;
case GSM_PCHAN_PDCH:
lchan = &ts->lchan[0];
lchan->type = GSM_LCHAN_PDTCH;
break;
default:
/* FIXME */
break;
}
return 0;
}
/* 8.6.3 Set Channel Attributes has been received */
static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_bts *bts = ts->trx->bts;
struct tlv_parsed tp, *tp_merged;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx SET CHAN ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* 9.4.21 HSN... */
/* 9.4.27 MAIO */
if (TLVP_PRESENT(&tp, NM_ATT_HSN) || TLVP_PRESENT(&tp, NM_ATT_MAIO)) {
LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Frequency hopping not supported.\n");
return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP);
}
/* 9.4.52 Starting Time */
if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) {
LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Starting time not supported.\n");
return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP);
}
/* merge existing BTS attributes with new attributes */
tp_merged = tlvp_copy(ts->mo.nm_attr, bts);
tlvp_merge(tp_merged, &tp);
/* Call into BTS driver to check attribute values */
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
if (rc < 0) {
talloc_free(tp_merged);
/* Send NACK */
return oml_fom_ack_nack(msg, -rc);
}
/* Success: replace old BTS attributes with new */
talloc_free(ts->mo.nm_attr);
ts->mo.nm_attr = tp_merged;
/* 9.4.13 Channel Combination */
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
ts->pchan = abis_nm_pchan4chcomb(comb);
conf_lchans_for_pchan(ts);
}
/* 9.4.5 ARFCN List */
/* 9.4.60 TSC */
if (TLVP_PRESENT(&tp, NM_ATT_TSC) && TLVP_LEN(&tp, NM_ATT_TSC) >= 1) {
ts->tsc = *TLVP_VAL(&tp, NM_ATT_TSC);
} else {
/* If there is no TSC specified, use the BCC */
ts->tsc = bts->bsic & 0x3;
}
LOGP(DOML, LOGL_INFO, "%s SET CHAN ATTR (TSC = %u)\n",
gsm_abis_mo_name(&ts->mo), ts->tsc);
/* call into BTS driver to apply new attributes to hardware */
return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_CHANNEL, ts);
}
/* 8.9.2 Opstart has been received */
static int oml_rx_opstart(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_abis_mo *mo;
void *obj;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx OPSTART\n");
/* Step 1: Resolve MO by obj_class/obj_inst */
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
if (!mo || !obj)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
/* Step 2: Do some global dependency/consistency checking */
if (mo->nm_state.operational == NM_OPSTATE_ENABLED) {
DEBUGP(DOML, "... automatic ACK, OP state already was Enabled\n");
return oml_mo_opstart_ack(mo);
}
/* Step 3: Ask BTS driver to apply the opstart */
return bts_model_opstart(bts, mo, obj);
}
static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct tlv_parsed tp;
struct gsm_abis_mo *mo;
uint8_t adm_state;
void *obj;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx CHG ADM STATE\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0) {
LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: error during TLV parse\n");
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
}
if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: no ADM state attribute\n");
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
}
adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
/* Step 1: Resolve MO by obj_class/obj_inst */
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
if (!mo || !obj)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
/* Step 2: Do some global dependency/consistency checking */
if (mo->nm_state.administrative == adm_state)
LOGP(DOML, LOGL_NOTICE,
"ADM state already was %s\n",
get_value_string(abis_nm_adm_state_names, adm_state));
/* Step 3: Ask BTS driver to apply the state chg */
return bts_model_chg_adm_state(bts, mo, obj, adm_state);
}
static int down_fom(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_bts_trx *trx;
int ret;
if (msgb_l2len(msg) < sizeof(*foh)) {
LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
return -EIO;
}
if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
LOGP(DOML, LOGL_INFO, "Formatted O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN);
}
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
ret = oml_rx_set_bts_attr(bts, msg);
break;
case NM_MT_SET_RADIO_ATTR:
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
if (!trx)
return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN);
ret = oml_rx_set_radio_attr(trx, msg);
break;
case NM_MT_SET_CHAN_ATTR:
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
if (!trx)
return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN);
if (foh->obj_inst.ts_nr >= ARRAY_SIZE(trx->ts))
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
ret = oml_rx_set_chan_attr(&trx->ts[foh->obj_inst.ts_nr], msg);
break;
case NM_MT_OPSTART:
ret = oml_rx_opstart(bts, msg);
break;
case NM_MT_CHG_ADM_STATE:
ret = oml_rx_chg_adm_state(bts, msg);
break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default:
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
foh->msg_type);
ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL);
}
return ret;
}
/*
* manufacturer related messages
*/
#ifndef TLVP_PRES_LEN /* old libosmocore */
#define TLVP_PRES_LEN(tp, tag, min_len) \
(TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
#endif
static int oml_ipa_mo_set_attr_nse(void *obj, struct tlv_parsed *tp)
{
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.nse);
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSEI, 2))
bts->gprs.nse.nsei =
ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSEI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_CFG, 7)) {
memcpy(&bts->gprs.nse.timer,
TLVP_VAL(tp, NM_ATT_IPACC_NS_CFG), 7);
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BSSGP_CFG, 11)) {
memcpy(&bts->gprs.cell.timer,
TLVP_VAL(tp, NM_ATT_IPACC_BSSGP_CFG), 11);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSE_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_cell(void *obj, struct tlv_parsed *tp)
{
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.cell);
struct gprs_rlc_cfg *rlcc = &bts->gprs.cell.rlc_cfg;
const uint8_t *cur;
uint16_t _cur_s;
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RAC, 1))
bts->gprs.rac = *TLVP_VAL(tp, NM_ATT_IPACC_RAC);
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_GPRS_PAGING_CFG, 2)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_GPRS_PAGING_CFG);
rlcc->paging.repeat_time = cur[0] * 50;
rlcc->paging.repeat_count = cur[1];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BVCI, 2))
bts->gprs.cell.bvci =
htons(tlvp_val16_unal(tp, NM_ATT_IPACC_BVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG, 9)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG);
rlcc->parameter[RLC_T3142] = cur[0];
rlcc->parameter[RLC_T3169] = cur[1];
rlcc->parameter[RLC_T3191] = cur[2];
rlcc->parameter[RLC_T3193] = cur[3];
rlcc->parameter[RLC_T3195] = cur[4];
rlcc->parameter[RLC_N3101] = cur[5];
rlcc->parameter[RLC_N3103] = cur[6];
rlcc->parameter[RLC_N3105] = cur[7];
rlcc->parameter[CV_COUNTDOWN] = cur[8];
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_CODING_SCHEMES, 2)) {
int i;
rlcc->cs_mask = 0;
cur = TLVP_VAL(tp, NM_ATT_IPACC_CODING_SCHEMES);
for (i = 0; i < 4; i++) {
if (cur[0] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_CS1+i));
}
if (cur[0] & 0x80)
rlcc->cs_mask |= (1 << GPRS_MCS9);
for (i = 0; i < 8; i++) {
if (cur[1] & (1 << i))
rlcc->cs_mask |= (1 << (GPRS_MCS1+i));
}
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_2, 5)) {
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_2);
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_DL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
memcpy(&_cur_s, cur, 2);
rlcc->parameter[T_UL_TBF_EXT] = ntohs(_cur_s) * 10;
cur += 2;
rlcc->initial_cs = *cur;
}
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_3, 1)) {
rlcc->initial_mcs = *TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_3);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_CELL_ATTR, bts);
return 0;
}
static int oml_ipa_mo_set_attr_nsvc(struct gsm_bts_gprs_nsvc *nsvc,
struct tlv_parsed *tp)
{
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSVCI, 2))
nsvc->nsvci = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSVCI));
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_LINK_CFG, 8)) {
const uint8_t *cur = TLVP_VAL(tp, NM_ATT_IPACC_NS_LINK_CFG);
uint16_t _cur_s;
uint32_t _cur_l;
memcpy(&_cur_s, cur, 2);
nsvc->remote_port = ntohs(_cur_s);
cur += 2;
memcpy(&_cur_l, cur, 4);
nsvc->remote_ip = ntohl(_cur_l);
cur += 4;
memcpy(&_cur_s, cur, 2);
nsvc->local_port = ntohs(_cur_s);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSVC_ATTR, nsvc);
return 0;
}
static int oml_ipa_mo_set_attr(struct gsm_bts *bts, struct gsm_abis_mo *mo,
void *obj, struct tlv_parsed *tp)
{
int rc;
switch (mo->obj_class) {
case NM_OC_GPRS_NSE:
rc = oml_ipa_mo_set_attr_nse(obj, tp);
break;
case NM_OC_GPRS_CELL:
rc = oml_ipa_mo_set_attr_cell(obj, tp);
break;
case NM_OC_GPRS_NSVC:
rc = oml_ipa_mo_set_attr_nsvc(obj, tp);
break;
default:
rc = NM_NACK_OBJINST_UNKN;
}
return rc;
}
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
struct gsm_abis_mo *mo;
struct tlv_parsed tp;
void *obj;
int rc;
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx IPA SET ATTR\n");
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
if (rc < 0)
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
/* Resolve MO by obj_class/obj_inst */
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
if (!mo || !obj)
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
rc = oml_ipa_mo_set_attr(bts, mo, obj, &tp);
return oml_fom_ack_nack(msg, rc);
}
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
struct tlv_parsed *tp)
{
struct e1inp_sign_link *oml_link = trx->bts->oml_link;
uint16_t port = IPA_TCP_PORT_RSL;
uint32_t ip = get_signlink_remote_ip(oml_link);
struct in_addr in;
int rc;
uint8_t stream_id = 0;
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) {
ip = ntohl(tlvp_val32_unal(tp, NM_ATT_IPACC_DST_IP));
}
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) {
port = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_DST_IP_PORT));
}
if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) {
stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID);
}
in.s_addr = htonl(ip);
LOGP(DOML, LOGL_INFO, "Rx IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
inet_ntoa(in), port, stream_id);
rc = e1inp_ipa_bts_rsl_connect(oml_link->ts->line, inet_ntoa(in), port);
if (rc < 0) {
LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc);
return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM);
}
return oml_fom_ack_nack(msg, 0);
}
static int down_mom(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_hdr *oh = msgb_l2(msg);
struct abis_om_fom_hdr *foh;
struct gsm_bts_trx *trx;
uint8_t idstrlen = oh->data[0];
struct tlv_parsed tp;
int ret;
if (msgb_l2len(msg) < sizeof(*foh)) {
LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
return -EIO;
}
if (strncmp((char *)&oh->data[1], abis_nm_ipa_magic, idstrlen)) {
LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n");
return -EINVAL;
}
msg->l3h = oh->data + 1 + idstrlen;
foh = (struct abis_om_fom_hdr *) msg->l3h;
if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
LOGP(DOML, LOGL_INFO, "Manufacturer O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN);
}
ret = oml_tlv_parse(&tp, foh->data, oh->length - sizeof(*foh));
if (ret < 0) {
LOGP(DOML, LOGL_ERROR, "TLV parse error %d\n", ret);
return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN);
}
abis_nm_debugp_foh(DOML, foh);
DEBUGPC(DOML, "Rx IPACCESS(0x%02x): ", foh->msg_type);
switch (foh->msg_type) {
case NM_MT_IPACC_RSL_CONNECT:
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
break;
case NM_MT_IPACC_SET_ATTR:
ret = oml_ipa_set_attr(bts, msg);
break;
default:
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
foh->msg_type);
ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL);
}
return ret;
}
/* incoming OML message from BSC */
int down_oml(struct gsm_bts *bts, struct msgb *msg)
{
struct abis_om_hdr *oh = msgb_l2(msg);
int ret = 0;
if (msgb_l2len(msg) < 1) {
LOGP(DOML, LOGL_NOTICE, "OML message too short\n");
msgb_free(msg);
return -EIO;
}
msg->l3h = (unsigned char *)oh + sizeof(*oh);
switch (oh->mdisc) {
case ABIS_OM_MDISC_FOM:
if (msgb_l2len(msg) < sizeof(*oh)) {
LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
ret = -EIO;
break;
}
ret = down_fom(bts, msg);
break;
case ABIS_OM_MDISC_MANUF:
if (msgb_l2len(msg) < sizeof(*oh)) {
LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
ret = -EIO;
break;
}
ret = down_mom(bts, msg);
break;
default:
LOGP(DOML, LOGL_NOTICE, "unknown OML msg_discr 0x%02x\n",
oh->mdisc);
ret = -EINVAL;
}
msgb_free(msg);
return ret;
}
int oml_init(void)
{
DEBUGP(DOML, "Initializing OML attribute definitions\n");
tlv_def_patch(&abis_nm_att_tlvdef_ipa, &abis_nm_att_tlvdef);
return 0;
}
osmo-bts-0.4.0/src/common/paging.c 0000664 0000000 0000000 00000037036 12600264262 0016757 0 ustar 00root root 0000000 0000000 /* Paging message encoding + queue management */
/* (C) 2011-2012 by Harald Welte
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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 .
*
*/
/* TODO:
* eMLPP priprity
* add P1/P2/P3 rest octets
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 9
enum paging_record_type {
PAGING_RECORD_PAGING,
PAGING_RECORD_IMM_ASS
};
struct paging_record {
struct llist_head list;
enum paging_record_type type;
union {
struct {
time_t expiration_time;
uint8_t chan_needed;
uint8_t identity_lv[9];
} paging;
struct {
uint8_t msg[GSM_MACBLOCK_LEN];
} imm_ass;
} u;
};
struct paging_state {
struct gsm_bts_role_bts *btsb;
/* parameters taken / interpreted from BCCH/CCCH configuration */
struct gsm48_control_channel_descr chan_desc;
/* configured otherwise */
unsigned int paging_lifetime; /* in seconds */
unsigned int num_paging_max;
/* total number of currently active paging records in queue */
unsigned int num_paging;
struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
};
unsigned int paging_get_lifetime(struct paging_state *ps)
{
return ps->paging_lifetime;
}
unsigned int paging_get_queue_max(struct paging_state *ps)
{
return ps->num_paging_max;
}
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime)
{
ps->paging_lifetime = lifetime;
}
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max)
{
ps->num_paging_max = queue_max;
}
static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv)
{
if (tmsi_lv[0] < 5)
return -EINVAL;
if ((tmsi_lv[1] & 7) != GSM_MI_TYPE_TMSI)
return -EINVAL;
*out = *((uint32_t *)(tmsi_lv+2));
return 0;
}
/* paging block numbers in a simple non-combined CCCH */
static const uint8_t block_by_tdma51[51] = {
255, 255, /* FCCH, SCH */
255, 255, 255, 255, /* BCCH */
0, 0, 0, 0, /* B0(6..9) */
255, 255, /* FCCH, SCH */
1, 1, 1, 1, /* B1(12..15) */
2, 2, 2, 2, /* B2(16..19) */
255, 255, /* FCCH, SCH */
3, 3, 3, 3, /* B3(22..25) */
4, 4, 4, 4, /* B3(26..29) */
255, 255, /* FCCH, SCH */
5, 5, 5, 5, /* B3(32..35) */
6, 6, 6, 6, /* B3(36..39) */
255, 255, /* FCCH, SCH */
7, 7, 7, 7, /* B3(42..45) */
8, 8, 8, 8, /* B3(46..49) */
255, /* empty */
};
/* get the paging block number _within_ current 51 multiframe */
static int get_pag_idx_n(struct paging_state *ps, struct gsm_time *gt)
{
int blk_n = block_by_tdma51[gt->t3];
int blk_idx;
if (blk_n == 255)
return -EINVAL;
blk_idx = blk_n - ps->chan_desc.bs_ag_blks_res;
if (blk_idx < 0)
return -EINVAL;
return blk_idx;
}
/* get paging block index over multiple 51 multiframes */
static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt)
{
int pag_idx = get_pag_idx_n(ps, gt);
unsigned int n_pag_blks_51 = gsm0502_get_n_pag_blocks(&ps->chan_desc);
unsigned int mfrm_part;
if (pag_idx < 0)
return pag_idx;
mfrm_part = ((gt->fn / 51) % (ps->chan_desc.bs_pa_mfrms+2)) * n_pag_blks_51;
return pag_idx + mfrm_part;
}
int paging_buffer_space(struct paging_state *ps)
{
if (ps->num_paging >= ps->num_paging_max)
return 0;
else
return ps->num_paging_max - ps->num_paging;
}
/* Add an identity to the paging queue */
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
const uint8_t *identity_lv, uint8_t chan_needed)
{
struct llist_head *group_q = &ps->paging_queue[paging_group];
struct paging_record *pr;
if (ps->num_paging >= ps->num_paging_max) {
LOGP(DPAG, LOGL_NOTICE, "Dropping paging, queue full (%u)\n",
ps->num_paging);
return -ENOSPC;
}
/* Check if we already have this identity */
llist_for_each_entry(pr, group_q, list) {
if (pr->type != PAGING_RECORD_PAGING)
continue;
if (identity_lv[0] == pr->u.paging.identity_lv[0] &&
!memcmp(identity_lv+1, pr->u.paging.identity_lv+1,
identity_lv[0])) {
LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n");
pr->u.paging.expiration_time =
time(NULL) + ps->paging_lifetime;
return -EEXIST;
}
}
pr = talloc_zero(ps, struct paging_record);
if (!pr)
return -ENOMEM;
pr->type = PAGING_RECORD_PAGING;
if (*identity_lv + 1 > sizeof(pr->u.paging.identity_lv)) {
talloc_free(pr);
return -E2BIG;
}
LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n",
paging_group, ps->num_paging+1);
pr->u.paging.expiration_time = time(NULL) + ps->paging_lifetime;
pr->u.paging.chan_needed = chan_needed;
memcpy(&pr->u.paging.identity_lv, identity_lv, identity_lv[0]+1);
/* enqueue the new identity to the HEAD of the queue,
* to ensure it will be paged quickly at least once. */
llist_add(&pr->list, group_q);
ps->num_paging++;
return 0;
}
/* Add an IMM.ASS message to the paging queue */
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
uint8_t len)
{
struct llist_head *group_q;
struct paging_record *pr;
uint16_t imsi, paging_group;
if (len != GSM_MACBLOCK_LEN + 3) {
LOGP(DPAG, LOGL_ERROR, "IMM.ASS invalid length %d\n", len);
return -EINVAL;
}
len -= 3;
imsi = 100 * ((*(data++)) - '0');
imsi += 10 * ((*(data++)) - '0');
imsi += (*(data++)) - '0';
paging_group = gsm0502_calc_paging_group(&ps->chan_desc, imsi);
group_q = &ps->paging_queue[paging_group];
pr = talloc_zero(ps, struct paging_record);
if (!pr)
return -ENOMEM;
pr->type = PAGING_RECORD_IMM_ASS;
LOGP(DPAG, LOGL_INFO, "Add IMM.ASS to queue (group=%u)\n",
paging_group);
memcpy(pr->u.imm_ass.msg, data, GSM_MACBLOCK_LEN);
/* enqueue the new message to the HEAD of the queue */
llist_add(&pr->list, group_q);
return 0;
}
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
uint8_t chan1, const uint8_t *identity2_lv,
uint8_t chan2)
{
struct gsm48_paging1 *pt1 = (struct gsm48_paging1 *) out_buf;
uint8_t *cur;
memset(out_buf, 0, sizeof(*pt1));
pt1->proto_discr = GSM48_PDISC_RR;
pt1->msg_type = GSM48_MT_RR_PAG_REQ_1;
pt1->pag_mode = GSM48_PM_NORMAL;
pt1->cneed1 = chan1 & 3;
pt1->cneed2 = chan2 & 3;
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
if (identity2_lv)
cur = lv_put(cur, identity2_lv[0], identity2_lv+1);
pt1->l2_plen = L2_PLEN(cur - out_buf);
return cur - out_buf;
}
static int fill_paging_type_2(uint8_t *out_buf, const uint8_t *tmsi1_lv,
uint8_t cneed1, const uint8_t *tmsi2_lv,
uint8_t cneed2, const uint8_t *identity3_lv)
{
struct gsm48_paging2 *pt2 = (struct gsm48_paging2 *) out_buf;
uint8_t *cur;
memset(out_buf, 0, sizeof(*pt2));
pt2->proto_discr = GSM48_PDISC_RR;
pt2->msg_type = GSM48_MT_RR_PAG_REQ_2;
pt2->pag_mode = GSM48_PM_NORMAL;
pt2->cneed1 = cneed1;
pt2->cneed2 = cneed2;
tmsi_mi_to_uint(&pt2->tmsi1, tmsi1_lv);
tmsi_mi_to_uint(&pt2->tmsi2, tmsi2_lv);
cur = out_buf + sizeof(*pt2);
if (identity3_lv)
cur = lv_put(pt2->data, identity3_lv[0], identity3_lv+1);
pt2->l2_plen = L2_PLEN(cur - out_buf);
return cur - out_buf;
}
static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
uint8_t cneed1, const uint8_t *tmsi2_lv,
uint8_t cneed2, const uint8_t *tmsi3_lv,
const uint8_t *tmsi4_lv)
{
struct gsm48_paging3 *pt3 = (struct gsm48_paging3 *) out_buf;
uint8_t *cur;
memset(out_buf, 0, sizeof(*pt3));
pt3->proto_discr = GSM48_PDISC_RR;
pt3->msg_type = GSM48_MT_RR_PAG_REQ_3;
pt3->pag_mode = GSM48_PM_NORMAL;
pt3->cneed1 = cneed1;
pt3->cneed2 = cneed2;
tmsi_mi_to_uint(&pt3->tmsi1, tmsi1_lv);
tmsi_mi_to_uint(&pt3->tmsi2, tmsi2_lv);
tmsi_mi_to_uint(&pt3->tmsi3, tmsi3_lv);
tmsi_mi_to_uint(&pt3->tmsi4, tmsi4_lv);
cur = out_buf + sizeof(*pt3);
return cur - out_buf;
}
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
static struct paging_record *dequeue_pr(struct llist_head *group_q)
{
struct paging_record *pr;
pr = llist_entry(group_q->next, struct paging_record, list);
llist_del(&pr->list);
return pr;
}
static int pr_is_imsi(struct paging_record *pr)
{
if ((pr->u.paging.identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
return 1;
else
return 0;
}
static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n)
{
int i, j;
struct paging_record *t;
if (n < 2)
return;
/* simple bubble sort */
for (i = n-2; i >= 0; i--) {
for (j=0; j<=i ; j++) {
if (pr_is_imsi(pr[j]) > pr_is_imsi(pr[j+1])) {
t = pr[j];
pr[j] = pr[j+1];
pr[j+1] = t;
}
}
}
}
/* generate paging message for given gsm time */
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
int *is_empty)
{
struct llist_head *group_q;
int group;
int len;
*is_empty = 0;
ps->btsb->load.ccch.pch_total += 1;
group = get_pag_subch_nr(ps, gt);
if (group < 0) {
LOGP(DPAG, LOGL_ERROR,
"Paging called for GSM wrong time: FN %d/%d/%d/%d.\n",
gt->fn, gt->t1, gt->t2, gt->t3);
return -1;
}
group_q = &ps->paging_queue[group];
/* There is nobody to be paged, send Type1 with two empty ID */
if (llist_empty(group_q)) {
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
len = fill_paging_type_1(out_buf, empty_id_lv, 0,
NULL, 0);
*is_empty = 1;
} else {
struct paging_record *pr[4];
unsigned int num_pr = 0, imm_ass = 0;
time_t now = time(NULL);
unsigned int i, num_imsi = 0;
ps->btsb->load.ccch.pch_used += 1;
/* get (if we have) up to four paging records */
for (i = 0; i < ARRAY_SIZE(pr); i++) {
if (llist_empty(group_q))
break;
pr[i] = dequeue_pr(group_q);
/* check for IMM.ASS */
if (pr[i]->type == PAGING_RECORD_IMM_ASS) {
imm_ass = 1;
break;
}
num_pr++;
/* count how many IMSIs are among them */
if (pr_is_imsi(pr[i]))
num_imsi++;
}
/* if we have an IMMEDIATE ASSIGNMENT */
if (imm_ass) {
/* re-add paging records */
for (i = 0; i < num_pr; i++)
llist_add(&pr[i]->list, group_q);
/* get message and free record */
memcpy(out_buf, pr[num_pr]->u.imm_ass.msg,
GSM_MACBLOCK_LEN);
pcu_tx_pch_data_cnf(gt->fn, pr[num_pr]->u.imm_ass.msg,
GSM_MACBLOCK_LEN);
talloc_free(pr[num_pr]);
return GSM_MACBLOCK_LEN;
}
/* make sure the TMSIs are ahead of the IMSIs in the array */
sort_pr_tmsi_imsi(pr, num_pr);
if (num_pr == 4 && num_imsi == 0) {
/* No IMSI: easy case, can use TYPE 3 */
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
len = fill_paging_type_3(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed,
pr[2]->u.paging.identity_lv,
pr[3]->u.paging.identity_lv);
} else if (num_pr >= 3 && num_imsi <= 1) {
/* 3 or 4, of which only up to 1 is IMSI */
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
len = fill_paging_type_2(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed,
pr[2]->u.paging.identity_lv);
if (num_pr == 4) {
/* re-add #4 for next time */
llist_add(&pr[3]->list, group_q);
pr[3] = NULL;
}
} else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
NULL, 0);
} else {
/* 2 (any type) or
* 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
len = fill_paging_type_1(out_buf,
pr[0]->u.paging.identity_lv,
pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv,
pr[1]->u.paging.chan_needed);
if (num_pr >= 3) {
/* re-add #4 for next time */
llist_add(&pr[2]->list, group_q);
pr[2] = NULL;
}
if (num_pr == 4) {
/* re-add #4 for next time */
llist_add(&pr[3]->list, group_q);
pr[3] = NULL;
}
}
for (i = 0; i < num_pr; i++) {
/* skip those that we might have re-added above */
if (pr[i] == NULL)
continue;
/* check if we can expire the paging record,
* or if we need to re-queue it */
if (pr[i]->u.paging.expiration_time <= now) {
talloc_free(pr[i]);
ps->num_paging--;
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
ps->num_paging);
} else
llist_add_tail(&pr[i]->list, group_q);
}
}
memset(out_buf+len, 0x2B, GSM_MACBLOCK_LEN-len);
return len;
}
int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc)
{
LOGP(DPAG, LOGL_INFO, "Paging SI update\n");
ps->chan_desc = *chan_desc;
/* FIXME: do we need to re-sort the old paging_records? */
return 0;
}
static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hdlr_data,
void *signal_data)
{
if (subsys == SS_GLOBAL && signal == S_NEW_SYSINFO) {
struct gsm_bts *bts = signal_data;
struct gsm_bts_role_bts *btsb = bts->role;
struct paging_state *ps = btsb->paging_state;
struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3];
#warning "TODO: Remove this when setting u8NbrOfAgch is implemented properly"
if (si3->control_channel_desc.bs_ag_blks_res != 1)
LOGP(DPAG, LOGL_ERROR,
"Paging: BS_AG_BLKS_RES = %d != 1 not fully supported\n",
si3->control_channel_desc.bs_ag_blks_res);
paging_si_update(ps, &si3->control_channel_desc);
}
return 0;
}
static int initialized = 0;
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
unsigned int num_paging_max,
unsigned int paging_lifetime)
{
struct paging_state *ps;
unsigned int i;
ps = talloc_zero(btsb, struct paging_state);
if (!ps)
return NULL;
ps->btsb = btsb;
ps->paging_lifetime = paging_lifetime;
ps->num_paging_max = num_paging_max;
for (i = 0; i < ARRAY_SIZE(ps->paging_queue); i++)
INIT_LLIST_HEAD(&ps->paging_queue[i]);
if (!initialized) {
osmo_signal_register_handler(SS_GLOBAL, paging_signal_cbfn, NULL);
initialized = 1;
}
return ps;
}
void paging_config(struct paging_state *ps,
unsigned int num_paging_max,
unsigned int paging_lifetime)
{
ps->num_paging_max = num_paging_max;
ps->paging_lifetime = paging_lifetime;
}
void paging_reset(struct paging_state *ps)
{
int i;
for (i = 0; i < ARRAY_SIZE(ps->paging_queue); i++) {
struct llist_head *queue = &ps->paging_queue[i];
struct paging_record *pr, *pr2;
llist_for_each_entry_safe(pr, pr2, queue, list) {
llist_del(&pr->list);
talloc_free(pr);
ps->num_paging--;
}
}
if (ps->num_paging != 0)
LOGP(DPAG, LOGL_NOTICE, "num_paging != 0 after flushing all records?!?\n");
ps->num_paging = 0;
}
/**
* \brief Helper for the unit tests
*/
int paging_group_queue_empty(struct paging_state *ps, uint8_t grp)
{
if (grp >= ARRAY_SIZE(ps->paging_queue))
return 1;
return llist_empty(&ps->paging_queue[grp]);
}
int paging_queue_length(struct paging_state *ps)
{
return ps->num_paging;
}
osmo-bts-0.4.0/src/common/pcu_sock.c 0000664 0000000 0000000 00000056052 12600264262 0017317 0 ustar 00root root 0000000 0000000 /* pcu_sock.c: Connect from PCU via unix domain socket */
/* (C) 2008-2010 by Harald Welte
* (C) 2009-2012 by Andreas Eversberg
* (C) 2012 by Holger Hans Peter Freyther
* All Rights Reserved
*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
extern struct gsm_network bts_gsmnet;
extern int pcu_direct;
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
static const char *sapi_string[] = {
[PCU_IF_SAPI_RACH] = "RACH",
[PCU_IF_SAPI_AGCH] = "AGCH",
[PCU_IF_SAPI_PCH] = "PCH",
[PCU_IF_SAPI_BCCH] = "BCCH",
[PCU_IF_SAPI_PDTCH] = "PDTCH",
[PCU_IF_SAPI_PRACH] = "PRACH",
[PCU_IF_SAPI_PTCCH] = "PTCCH",
};
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr)
{
struct gsm_bts_trx *trx;
llist_for_each_entry(trx, &bts->trx_list, list) {
if (trx->nr == trx_nr)
return trx;
}
return NULL;
}
/*
* PCU messages
*/
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct gsm_pcu_if));
pcu_prim = (struct gsm_pcu_if *) msg->data;
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
int pcu_tx_info_ind(void)
{
struct gsm_network *net = &bts_gsmnet;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_info_ind *info_ind;
struct gsm_bts *bts;
struct gprs_rlc_cfg *rlcc;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
int i, j;
LOGP(DPCU, LOGL_INFO, "Sending info\n");
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
rlcc = &bts->gprs.cell.rlc_cfg;
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
info_ind = &pcu_prim->u.info_ind;
info_ind->version = PCU_IF_VERSION;
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) {
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
LOGP(DPCU, LOGL_INFO, "BTS is up\n");
} else
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
if (pcu_direct)
info_ind->flags |= PCU_IF_FLAG_SYSMO;
/* RAI */
info_ind->mcc = net->mcc;
info_ind->mnc = net->mnc;
info_ind->lac = bts->location_area_code;
info_ind->rac = bts->gprs.rac;
/* NSE */
info_ind->nsei = bts->gprs.nse.nsei;
memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
/* cell attributes */
info_ind->cell_id = bts->cell_identity;
info_ind->repeat_time = rlcc->paging.repeat_time;
info_ind->repeat_count = rlcc->paging.repeat_count;
info_ind->bvci = bts->gprs.cell.bvci;
info_ind->t3142 = rlcc->parameter[RLC_T3142];
info_ind->t3169 = rlcc->parameter[RLC_T3169];
info_ind->t3191 = rlcc->parameter[RLC_T3191];
info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
info_ind->t3195 = rlcc->parameter[RLC_T3195];
info_ind->n3101 = rlcc->parameter[RLC_N3101];
info_ind->n3103 = rlcc->parameter[RLC_N3103];
info_ind->n3105 = rlcc->parameter[RLC_N3105];
info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
if (rlcc->cs_mask & (1 << GPRS_CS1))
info_ind->flags |= PCU_IF_FLAG_CS1;
if (rlcc->cs_mask & (1 << GPRS_CS2))
info_ind->flags |= PCU_IF_FLAG_CS2;
if (rlcc->cs_mask & (1 << GPRS_CS3))
info_ind->flags |= PCU_IF_FLAG_CS3;
if (rlcc->cs_mask & (1 << GPRS_CS4))
info_ind->flags |= PCU_IF_FLAG_CS4;
if (rlcc->cs_mask & (1 << GPRS_MCS1))
info_ind->flags |= PCU_IF_FLAG_MCS1;
if (rlcc->cs_mask & (1 << GPRS_MCS2))
info_ind->flags |= PCU_IF_FLAG_MCS2;
if (rlcc->cs_mask & (1 << GPRS_MCS3))
info_ind->flags |= PCU_IF_FLAG_MCS3;
if (rlcc->cs_mask & (1 << GPRS_MCS4))
info_ind->flags |= PCU_IF_FLAG_MCS4;
if (rlcc->cs_mask & (1 << GPRS_MCS5))
info_ind->flags |= PCU_IF_FLAG_MCS5;
if (rlcc->cs_mask & (1 << GPRS_MCS6))
info_ind->flags |= PCU_IF_FLAG_MCS6;
if (rlcc->cs_mask & (1 << GPRS_MCS7))
info_ind->flags |= PCU_IF_FLAG_MCS7;
if (rlcc->cs_mask & (1 << GPRS_MCS8))
info_ind->flags |= PCU_IF_FLAG_MCS8;
if (rlcc->cs_mask & (1 << GPRS_MCS9))
info_ind->flags |= PCU_IF_FLAG_MCS9;
#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
info_ind->initial_cs = rlcc->initial_cs;
info_ind->initial_mcs = rlcc->initial_mcs;
/* NSVC */
for (i = 0; i < 2; i++) {
nsvc = &bts->gprs.nsvc[i];
info_ind->nsvci[i] = nsvc->nsvci;
info_ind->local_port[i] = nsvc->local_port;
info_ind->remote_port[i] = nsvc->remote_port;
info_ind->remote_ip[i] = nsvc->remote_ip;
}
for (i = 0; i < 8; i++) {
trx = trx_by_nr(bts, i);
if (!trx)
break;
info_ind->trx[i].pdch_mask = 0;
info_ind->trx[i].arfcn = trx->arfcn;
info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx);
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
info_ind->trx[i].pdch_mask |= (1 << j);
info_ind->trx[i].tsc[j] =
(ts->tsc >= 0) ? ts->tsc : bts->tsc;
LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
"available (tsc=%d arfcn=%d)\n",
trx->nr, ts->nr,
info_ind->trx[i].tsc[j],
info_ind->trx[i].arfcn);
}
}
}
return pcu_sock_send(net, msg);
}
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts_gprs_nsvc *nsvc;
struct gsm_bts *bts;
struct gsm48_system_information_type_3 *si3;
int id;
if (subsys != SS_GLOBAL)
return -EINVAL;
switch(signal) {
case S_NEW_SYSINFO:
bts = signal_data;
if (!(bts->si_valid & (1 << SYSINFO_TYPE_3)))
break;
si3 = (struct gsm48_system_information_type_3 *)
bts->si_buf[SYSINFO_TYPE_3];
net->mcc = ((si3->lai.digits[0] & 0x0f) << 8)
| (si3->lai.digits[0] & 0xf0)
| (si3->lai.digits[1] & 0x0f);
net->mnc = ((si3->lai.digits[2] & 0x0f) << 8)
| (si3->lai.digits[2] & 0xf0)
| ((si3->lai.digits[1] & 0xf0) >> 4);
if ((net->mnc & 0x00f) == 0x00f)
net->mnc >>= 4;
bts->location_area_code = ntohs(si3->lai.lac);
bts->cell_identity = si3->cell_identity;
avail_lai = 1;
break;
case S_NEW_NSE_ATTR:
bts = signal_data;
avail_nse = 1;
break;
case S_NEW_CELL_ATTR:
bts = signal_data;
avail_cell = 1;
break;
case S_NEW_NSVC_ATTR:
nsvc = signal_data;
id = nsvc->id;
if (id < 0 || id > 1)
return -EINVAL;
avail_nsvc[id] = 1;
break;
case S_NEW_OP_STATE:
break;
default:
return -EINVAL;
}
/* If all infos have been received, of if one info is updated after
* all infos have been received, transmit info update. */
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0])
pcu_tx_info_ind();
return 0;
}
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rts_req *rts_req;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending rts request: is_ptcch=%d arfcn=%d "
"block=%d\n", is_ptcch, arfcn, block_nr);
msg = pcu_msgb_alloc(PCU_IF_MSG_RTS_REQ, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rts_req = &pcu_prim->u.rts_req;
rts_req->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
rts_req->fn = fn;
rts_req->arfcn = arfcn;
rts_req->trx_nr = ts->trx->nr;
rts_req->ts_nr = ts->nr;
rts_req->block_nr = block_nr;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
int8_t rssi)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_ind;
struct gsm_bts *bts = ts->trx->bts;
LOGP(DPCU, LOGL_DEBUG, "Sending data indication: is_ptcch=%d arfcn=%d "
"block=%d data=%s\n", is_ptcch, arfcn, block_nr,
osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_ind = &pcu_prim->u.data_ind;
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
data_ind->rssi = rssi;
data_ind->fn = fn;
data_ind->arfcn = arfcn;
data_ind->trx_nr = ts->trx->nr;
data_ind->ts_nr = ts->nr;
data_ind->block_nr = block_nr;
data_ind->rssi = rssi;
memcpy(data_ind->data, data, len);
data_ind->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_rach_ind *rach_ind;
LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
"fn=%d\n", qta, ra, fn);
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
rach_ind = &pcu_prim->u.rach_ind;
rach_ind->sapi = PCU_IF_SAPI_RACH;
rach_ind->ra = ra;
rach_ind->qta = qta;
rach_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_time_ind(uint32_t fn)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_time_ind *time_ind;
uint8_t fn13 = fn % 13;
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
msg = pcu_msgb_alloc(PCU_IF_MSG_TIME_IND, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
time_ind = &pcu_prim->u.time_ind;
time_ind->fn = fn;
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_pag_req *pag_req;
/* check if identity does not fit: length > sizeof(lv) - 1 */
if (identity_lv[0] >= sizeof(pag_req->identity_lv)) {
LOGP(DPCU, LOGL_ERROR, "Paging identity too large (%d)\n",
identity_lv[0]);
return -EINVAL;
}
/* socket not created */
if (!state) {
LOGP(DPCU, LOGL_DEBUG, "PCU socket not created, ignoring "
"paging message\n");
return 0;
}
msg = pcu_msgb_alloc(PCU_IF_MSG_PAG_REQ, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
pag_req = &pcu_prim->u.pag_req;
pag_req->chan_needed = chan_needed;
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
return pcu_sock_send(&bts_gsmnet, msg);
}
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
{
struct gsm_network *net = &bts_gsmnet;
struct gsm_bts *bts;
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_cnf;
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_INFO, "Sending PCH confirm\n");
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
data_cnf = &pcu_prim->u.data_cnf;
data_cnf->sapi = PCU_IF_SAPI_PCH;
data_cnf->fn = fn;
memcpy(data_cnf->data, data, len);
data_cnf->len = len;
return pcu_sock_send(&bts_gsmnet, msg);
}
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
struct gsm_pcu_if_data *data_req)
{
uint8_t is_ptcch;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
struct msgb *msg;
int rc = 0;
LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
"block=%d data=%s\n", sapi_string[data_req->sapi],
data_req->arfcn, data_req->block_nr,
osmo_hexdump(data_req->data, data_req->len));
switch (data_req->sapi) {
case PCU_IF_SAPI_BCCH:
if (data_req->len == 23) {
bts->si_valid |= (1 << SYSINFO_TYPE_13);
memcpy(bts->si_buf[SYSINFO_TYPE_13], data_req->data,
data_req->len);
} else {
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
}
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
break;
case PCU_IF_SAPI_PCH:
if (msg_type == PCU_IF_MSG_PAG_REQ) {
/* FIXME: Add function to schedule paging request.
* This might not be required, if PCU_IF_MSG_DATA_REQ
* is used instead. */
} else {
struct gsm_bts_role_bts *btsb = bts->role;
paging_add_imm_ass(btsb->paging_state, data_req->data,
data_req->len);
}
break;
case PCU_IF_SAPI_AGCH:
msg = msgb_alloc(data_req->len, "pcu_agch");
if (!msg) {
rc = -ENOMEM;
break;
}
msg->l3h = msgb_put(msg, data_req->len);
memcpy(msg->l3h, data_req->data, data_req->len);
if (bts_agch_enqueue(bts, msg) < 0) {
msgb_free(msg);
rc = -EIO;
}
break;
case PCU_IF_SAPI_PDTCH:
case PCU_IF_SAPI_PTCCH:
trx = trx_by_nr(bts, data_req->trx_nr);
if (!trx) {
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
"not existing TRX %d\n", data_req->trx_nr);
rc = -EINVAL;
break;
}
ts = &trx->ts[data_req->ts_nr];
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
rc = l1sap_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
data_req->block_nr, data_req->data, data_req->len);
break;
default:
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
"unsupported sapi %d\n", data_req->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_act_req(struct gsm_bts *bts,
struct gsm_pcu_if_act_req *act_req)
{
struct gsm_bts_trx *trx;
struct gsm_lchan *lchan;
LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TX=%d\n",
(act_req->activate) ? "Activate" : "Deactivate",
act_req->trx_nr, act_req->ts_nr);
trx = trx_by_nr(bts, act_req->trx_nr);
if (!trx || act_req->ts_nr >= 8)
return -EINVAL;
lchan = trx->ts[act_req->ts_nr].lchan;
lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
if (lchan->type != GSM_LCHAN_PDTCH) {
LOGP(DPCU, LOGL_ERROR, "Lchan is not of type PDCH, but %d.\n",
lchan->type);
return -EINVAL;
}
if (act_req->activate)
l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan), NULL);
else
l1sap_chan_rel(trx, gsm_lchan2chan_nr(lchan));
return 0;
}
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
struct gsm_bts *bts;
/* FIXME: allow multiple BTS */
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
case PCU_IF_MSG_PAG_REQ:
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break;
case PCU_IF_MSG_ACT_REQ:
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return rc;
}
/*
* PCU socket interface
*/
struct pcu_sock_state {
struct gsm_network *net;
struct osmo_fd listen_bfd; /* fd for listen socket */
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct llist_head upqueue; /* queue for sending messages */
};
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
{
struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *conn_bfd;
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
if (!state) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
"dropping message\n");
msgb_free(msg);
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
struct gsm_bts_trx_ts *ts;
int i, j;
/* FIXME: allow multiple BTS */
bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
state->listen_bfd.when |= BSC_FD_READ;
#if 0
/* remove si13, ... */
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
#endif
/* release PDCH */
for (i = 0; i < 8; i++) {
trx = trx_by_nr(bts, i);
if (!trx)
break;
for (j = 0; j < 8; j++) {
ts = &trx->ts[j];
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
&& ts->pchan == GSM_PCHAN_PDCH) {
ts->lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
l1sap_chan_rel(trx,
gsm_lchan2chan_nr(ts->lchan));
}
}
}
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
struct msgb *msg = msgb_dequeue(&state->upqueue);
msgb_free(msg);
}
}
static int pcu_sock_read(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN)
return 0;
goto close;
}
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
pcu_sock_close(state);
return -1;
}
static int pcu_sock_write(struct osmo_fd *bfd)
{
struct pcu_sock_state *state = bfd->data;
int rc;
while (!llist_empty(&state->upqueue)) {
struct msgb *msg, *msg2;
struct gsm_pcu_if *pcu_prim;
/* peek at the beginning of the queue */
msg = llist_entry(state->upqueue.next, struct msgb, list);
pcu_prim = (struct gsm_pcu_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", pcu_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
pcu_sock_close(state);
return -1;
}
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = pcu_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = pcu_sock_write(bfd);
return rc;
}
/* accept connection comming from PCU */
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
struct osmo_fd *conn_bfd = &state->conn_bfd;
struct sockaddr_un un_addr;
socklen_t len;
int rc;
len = sizeof(un_addr);
rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
"another active connection ?!?\n");
/* We already have one PCU connected, this is all we support */
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = pcu_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
}
LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
/* send current info */
pcu_tx_info_ind();
return 0;
}
int pcu_sock_init(void)
{
struct pcu_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
state->net = &bts_gsmnet;
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/pcu_bts");
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
strerror(errno));
talloc_free(state);
return rc;
}
bfd->when = BSC_FD_READ;
bfd->cb = pcu_sock_accept;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
bts_gsmnet.pcu_state = state;
return 0;
}
void pcu_sock_exit(void)
{
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
struct osmo_fd *bfd, *conn_bfd;
if (!state)
return;
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd > 0)
pcu_sock_close(state);
bfd = &state->listen_bfd;
close(bfd->fd);
osmo_fd_unregister(bfd);
talloc_free(state);
bts_gsmnet.pcu_state = NULL;
}
/* FIXME: move this to libosmocore */
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path)
{
struct sockaddr_un local;
unsigned int namelen;
int rc;
bfd->fd = socket(AF_UNIX, type, 0);
if (bfd->fd < 0) {
fprintf(stderr, "Failed to create Unix Domain Socket.\n");
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, path, sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
unlink(local.sun_path);
/* we use the same magic that X11 uses in Xtranssock.c for
* calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
namelen = SUN_LEN(&local);
#else
namelen = strlen(local.sun_path) +
offsetof(struct sockaddr_un, sun_path);
#endif
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
local.sun_path);
close(bfd->fd);
bfd->fd = -1;
return -1;
}
if (listen(bfd->fd, 0) != 0) {
fprintf(stderr, "Failed to listen.\n");
close(bfd->fd);
bfd->fd = -1;
return -1;
}
return 0;
}
osmo-bts-0.4.0/src/common/power_control.c 0000664 0000000 0000000 00000005276 12600264262 0020407 0 ustar 00root root 0000000 0000000 /* MS Power Control Loop L1 */
/* (C) 2014 by Holger Hans Peter Freyther
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include