mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-05 22:53:28 +00:00
Compare commits
8 Commits
jolly/test
...
shared/31c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c328371b54 | ||
|
|
ffcdd0da7e | ||
|
|
265d0635f2 | ||
|
|
03483cf0f2 | ||
|
|
7f12860f08 | ||
|
|
96efd189cc | ||
|
|
1f27fda194 | ||
|
|
5a906c7b72 |
@@ -1,3 +1,3 @@
|
||||
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
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h
|
||||
|
||||
16
include/osmo-bts/cbch.h
Normal file
16
include/osmo-bts/cbch.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
/* 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);
|
||||
@@ -14,6 +14,7 @@
|
||||
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
|
||||
|
||||
struct pcu_sock_state;
|
||||
struct smscb_msg;
|
||||
|
||||
struct gsm_network {
|
||||
struct llist_head bts_list;
|
||||
@@ -84,6 +85,12 @@ struct gsm_bts_role_bts {
|
||||
|
||||
/* used by the sysmoBTS to adjust band */
|
||||
uint8_t auto_band;
|
||||
uint8_t unitid_use_eeprom;
|
||||
|
||||
struct {
|
||||
struct llist_head queue; /* list of struct smscb_msg */
|
||||
struct smscb_msg *cur_msg; /* current SMS-CB */
|
||||
} smscb_state;
|
||||
};
|
||||
|
||||
enum lchan_ciph_state {
|
||||
|
||||
@@ -49,4 +49,10 @@ 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);
|
||||
|
||||
extern uint8_t etws_segment_data[5][17];
|
||||
extern uint8_t etws_segment_len[5];
|
||||
extern uint8_t etws_nr_seg;
|
||||
extern uint8_t etws_data[60];
|
||||
extern size_t etws_len;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,4 +7,5 @@ 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
|
||||
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
|
||||
etws_p1.c cbch.c
|
||||
|
||||
@@ -156,6 +156,8 @@ int bts_init(struct gsm_bts *bts)
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&btsb->smscb_state.queue);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
202
src/common/cbch.c
Normal file
202
src/common/cbch.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* Cell Broadcast routines */
|
||||
|
||||
/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#define SMS_CB_MSG_LEN 88 /* TS 04.12 Section 3.1 */
|
||||
#define SMS_CB_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */
|
||||
|
||||
struct smscb_msg {
|
||||
struct llist_head list; /* list in smscb_state.queue */
|
||||
|
||||
uint8_t msg[SMS_CB_MSG_LEN]; /* message buffer */
|
||||
uint8_t next_seg; /* next segment number */
|
||||
uint8_t num_segs; /* total number of segments */
|
||||
};
|
||||
|
||||
/* Figure 3/3GPP TS 04.12 */
|
||||
struct sms_cb_block_type {
|
||||
uint8_t seq_nr:4, /* 0=first, 1=2nd, ... f=null */
|
||||
lb:1, /* last block */
|
||||
lpd:2, /* always 01 */
|
||||
spare:1;
|
||||
};
|
||||
|
||||
/* 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 sms_cb_block_type *block_type = (struct sms_cb_block_type *) out++;
|
||||
struct smscb_msg *msg = btsb->smscb_state.cur_msg;
|
||||
|
||||
/* LPD is always 01 */
|
||||
block_type->lpd = 1;
|
||||
|
||||
if (!msg) {
|
||||
/* No message: Send NULL mesage */
|
||||
block_type->seq_nr = 0xf;
|
||||
block_type->lb = 0;
|
||||
/* padding */
|
||||
memset(out, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Current SMS-CB %s: ",
|
||||
osmo_hexdump_nospc(msg->msg, sizeof(msg->msg)));
|
||||
/* determine how much data to copy */
|
||||
to_copy = SMS_CB_MSG_LEN - (msg->next_seg * SMS_CB_BLOCK_LEN);
|
||||
if (to_copy > SMS_CB_BLOCK_LEN)
|
||||
to_copy = SMS_CB_BLOCK_LEN;
|
||||
|
||||
/* copy data and increment index */
|
||||
memcpy(out, &msg->msg[msg->next_seg * SMS_CB_BLOCK_LEN], to_copy);
|
||||
|
||||
/* set + increment sequence number */
|
||||
block_type->seq_nr = msg->next_seg++;
|
||||
|
||||
DEBUGP(DLSMS, "sending block %u: %s\n",
|
||||
block_type->seq_nr, osmo_hexdump_nospc(out, to_copy));
|
||||
|
||||
/* 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 %u)\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;
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Enqueuing SMS-CB %s from RSL",
|
||||
osmo_hexdump_nospc(scm->msg, sizeof(scm->msg)));
|
||||
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 */
|
||||
outbuf[0] = 0x2f;
|
||||
memset(outbuf+1, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
111
src/common/etws_p1.c
Normal file
111
src/common/etws_p1.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* Paging Rest Octets / ETWS support code */
|
||||
/*
|
||||
* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 <osmocom/core/bits.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
|
||||
/*! \brief construct the Paging 1 Rest Octets
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] seg_nr segment number 0...n
|
||||
* \param[in] num_segs total number of segments
|
||||
* \param[in] payload binary payload for this segment
|
||||
* \param[in] len_of_seg length of the segment
|
||||
*
|
||||
* Put together the P1 Rest Octets according to 10.5.2.23
|
||||
*/
|
||||
int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow)
|
||||
{
|
||||
|
||||
/* no NLN */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Group Call Information */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel6 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel7 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* Rel8 extensions */
|
||||
bitvec_set_bit(bv, H);
|
||||
|
||||
/* priority_uplink_access bit: Use RACH */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
if (etws_will_follow) {
|
||||
/* We do have ETWS Primary Notification */
|
||||
bitvec_set_bit(bv, ONE);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief construct a ETWS Primary Notification struct
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] pni Primary Notification Identifier
|
||||
* \param[in] seg_nr Segment Number 0...n
|
||||
* \param[in] num_segs Total Number of Segments
|
||||
* \param[in] payload Binary Payload for this Segment
|
||||
* \param[in] num_payload_bits Length of the Segment
|
||||
*/
|
||||
int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits)
|
||||
{
|
||||
uint8_t payload_ubits[num_payload_bits];
|
||||
|
||||
/* Put together a "ETWS Primary Notification struct" as per TS
|
||||
* 44.018 */
|
||||
|
||||
if (seg_nr == 0) {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
bitvec_set_uint(bv, num_segs, 4);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ONE);
|
||||
/* seg_nr is coounting 1..n, not 0..n */
|
||||
bitvec_set_uint(bv, seg_nr + 1, 4);
|
||||
}
|
||||
bitvec_set_bit(bv, pni ? ONE : ZERO);
|
||||
bitvec_set_uint(bv, num_payload_bits, 7);
|
||||
|
||||
/* expand packed payload bits to unpacked bits and set them in
|
||||
* the bit vector */
|
||||
osmo_pbit2ubit(payload_ubits, payload, num_payload_bits);
|
||||
int i;
|
||||
for (i = 0; i < num_payload_bits; ++i)
|
||||
bitvec_set_bit(bv, payload_ubits[i]);
|
||||
// bitvec_set_bits(bv, (enum bit_value *) payload_ubits, num_payload_bits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -70,11 +70,13 @@ static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts,
|
||||
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;
|
||||
@@ -214,9 +216,11 @@ 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,
|
||||
};
|
||||
|
||||
@@ -601,10 +601,17 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
||||
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];
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
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:
|
||||
@@ -621,10 +628,17 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
||||
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];
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
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:
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
#define MAX_PAGING_BLOCKS_CCCH 9
|
||||
#define MAX_BS_PA_MFRMS 9
|
||||
|
||||
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
|
||||
|
||||
|
||||
enum paging_record_type {
|
||||
PAGING_RECORD_PAGING,
|
||||
PAGING_RECORD_IMM_ASS
|
||||
@@ -262,7 +265,14 @@ int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||
|
||||
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
|
||||
|
||||
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
static int current_segment[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
|
||||
uint8_t etws_segment_data[5][17];
|
||||
uint8_t etws_segment_len[5];
|
||||
uint8_t etws_nr_seg;
|
||||
uint8_t etws_data[60];
|
||||
size_t etws_len;
|
||||
|
||||
static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
uint8_t chan1, const uint8_t *identity2_lv,
|
||||
uint8_t chan2)
|
||||
{
|
||||
@@ -279,7 +289,20 @@ static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
|
||||
if (identity2_lv)
|
||||
cur = lv_put(cur, identity2_lv[0], identity2_lv+1);
|
||||
else if (identity1_lv == empty_id_lv && etws_nr_seg > 0) {
|
||||
int cur_segment = current_segment[group];
|
||||
current_segment[group] += 1;
|
||||
current_segment[group] %= etws_nr_seg;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "paging group(%d) segment(%d)\n",
|
||||
group, cur_segment);
|
||||
|
||||
/* move the pointer */
|
||||
memcpy(cur, etws_segment_data[cur_segment], etws_segment_len[cur_segment]);
|
||||
cur += etws_segment_len[cur_segment];
|
||||
}
|
||||
|
||||
/* do we need to include it */
|
||||
pt1->l2_plen = L2_PLEN(cur - out_buf);
|
||||
|
||||
return cur - out_buf;
|
||||
@@ -336,8 +359,6 @@ static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
|
||||
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;
|
||||
@@ -400,7 +421,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
/* 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,
|
||||
len = fill_paging_type_1(group, out_buf, empty_id_lv, 0,
|
||||
NULL, 0);
|
||||
*is_empty = 1;
|
||||
} else {
|
||||
@@ -474,7 +495,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
}
|
||||
} else if (num_pr == 1) {
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
|
||||
len = fill_paging_type_1(out_buf,
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
NULL, 0);
|
||||
@@ -482,7 +503,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
/* 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,
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
pr[1]->u.paging.identity_lv,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* GSM TS 08.58 RSL, BTS Side */
|
||||
|
||||
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
|
||||
//#define FAKE_CIPH_MODE_COMPL
|
||||
|
||||
@@ -162,12 +163,14 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr)
|
||||
chan_nr, ts->pchan);
|
||||
} else if ((cbits & 0x1c) == 0x04) {
|
||||
lch_idx = cbits & 0x3; /* SDCCH/4 */
|
||||
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
|
||||
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
|
||||
ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if ((cbits & 0x18) == 0x08) {
|
||||
lch_idx = cbits & 0x7; /* SDCCH/8 */
|
||||
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
|
||||
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C &&
|
||||
ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
|
||||
@@ -412,6 +415,26 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 8.5.8 SMS BROADCAST COMMAND */
|
||||
static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
{
|
||||
struct tlv_parsed tp;
|
||||
struct rsl_ie_cb_cmd_type *cb_cmd_type;
|
||||
|
||||
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
||||
|
||||
if (!TLVP_PRESENT(&tp, RSL_IE_CB_CMD_TYPE) ||
|
||||
!TLVP_PRESENT(&tp, RSL_IE_SMSCB_MSG))
|
||||
return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR);
|
||||
|
||||
cb_cmd_type = (struct rsl_ie_cb_cmd_type *)
|
||||
TLVP_VAL(&tp, RSL_IE_CB_CMD_TYPE);
|
||||
|
||||
return bts_process_smscb_cmd(trx->bts, *cb_cmd_type,
|
||||
TLVP_LEN(&tp, RSL_IE_SMSCB_MSG),
|
||||
TLVP_VAL(&tp, RSL_IE_SMSCB_MSG));
|
||||
}
|
||||
|
||||
/* 8.6.2 SACCH FILLING */
|
||||
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
{
|
||||
@@ -1686,8 +1709,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
case RSL_MT_PAGING_CMD:
|
||||
ret = rsl_rx_paging_cmd(trx, msg);
|
||||
break;
|
||||
case RSL_MT_SMS_BC_REQ:
|
||||
case RSL_MT_SMS_BC_CMD:
|
||||
ret = rsl_rx_sms_bcast_cmd(trx, msg);
|
||||
break;
|
||||
case RSL_MT_SMS_BC_REQ:
|
||||
case RSL_MT_NOT_CMD:
|
||||
LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n",
|
||||
rsl_msg_name(cch->c.msg_type));
|
||||
|
||||
@@ -70,8 +70,12 @@ char *bts_support_comb_name(uint8_t chan_comb)
|
||||
return("BCCH");
|
||||
if (chan_comb == NM_CHANC_BCCHComb)
|
||||
return("BCCH+SDCCH/4");
|
||||
if (chan_comb == NM_CHANC_BCCH_CBCH)
|
||||
return("BCCH+CBCH+SDCCH/4");
|
||||
if (chan_comb == NM_CHANC_SDCCH)
|
||||
return("SDCCH/8");
|
||||
if (chan_comb == NM_CHANC_SDCCH_CBCH)
|
||||
return("SDCCH/8+CBCH");
|
||||
if (chan_comb == NM_CHANC_TCHFull)
|
||||
return("TCH/F");
|
||||
if (chan_comb == NM_CHANC_TCHHalf)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* OsmoBTS VTY interface */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/paging.h>
|
||||
|
||||
|
||||
enum node_type bts_vty_go_parent(struct vty *vty)
|
||||
@@ -473,6 +474,17 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
|
||||
abis_nm_avail_name(nms->availability), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static unsigned int llist_length(struct llist_head *list)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
struct llist_head *pos;
|
||||
|
||||
llist_for_each(pos, list)
|
||||
len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
@@ -503,6 +515,8 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
btsb->agch_queue_rejected_msgs, btsb->agch_queue_agch_msgs,
|
||||
btsb->agch_queue_pch_msgs,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " CBCH backlog queue length: %u%s",
|
||||
llist_length(&btsb->smscb_state.queue), VTY_NEWLINE);
|
||||
#if 0
|
||||
vty_out(vty, " Paging: %u pending requests, %u free slots%s",
|
||||
paging_pending_requests_nr(bts),
|
||||
@@ -614,6 +628,59 @@ DEFUN(bts_t_t_l_jitter_buf,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
extern int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow);
|
||||
extern int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits);
|
||||
|
||||
DEFUN(bts_etsw_idle, bts_etsw_idle_cmd,
|
||||
"etsw-message MESSAGE",
|
||||
"ETSW Message\nMessage in Hex\n")
|
||||
{
|
||||
int rc;
|
||||
int segment = 0, rest = 56;
|
||||
|
||||
rc = osmo_hexparse(argv[0], etws_data, sizeof(etws_data));
|
||||
if (rc != 56) {
|
||||
vty_out(vty, "%%we expect 56 bytes of the data.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty_out(vty, "%% parsed: %s%s",
|
||||
osmo_hexdump(etws_data, rc), VTY_NEWLINE);
|
||||
etws_len = rc;
|
||||
|
||||
|
||||
for (segment = 0; segment < 5; ++segment) {
|
||||
struct bitvec bv = { 0, };
|
||||
bv.data_len = 14;
|
||||
bv.data = &etws_segment_data[segment][0];
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "Goint to create segment(%d) offset %d len %d\n",
|
||||
segment, segment * 12,
|
||||
rest >= 12 ? 12 : rest);
|
||||
construct_p1_rest_octets(&bv, 1);
|
||||
printf("CUR BIT: %d %s\n", bv.cur_bit,
|
||||
osmo_hexdump(&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 : rest));
|
||||
construct_etws_prim_notif(&bv, 1, segment, 5,
|
||||
&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 * 8 : rest * 8);
|
||||
etws_segment_len[segment] = (bv.cur_bit + 7) / 8;
|
||||
rest -= 12;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE,
|
||||
"Created segment(%d) with len %d %s\n",
|
||||
segment, etws_segment_len[segment],
|
||||
osmo_hexdump(etws_segment_data[segment], etws_segment_len[segment]));
|
||||
}
|
||||
|
||||
etws_nr_seg = 5;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int bts_vty_init(const struct log_info *cat)
|
||||
{
|
||||
install_element_ve(&show_bts_cmd);
|
||||
@@ -646,6 +713,7 @@ int bts_vty_init(const struct log_info *cat)
|
||||
install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
|
||||
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
@@ -587,6 +588,10 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
|
||||
case GsmL1_Sapi_Prach:
|
||||
goto empty_frame;
|
||||
break;
|
||||
case GsmL1_Sapi_Cbch:
|
||||
/* get them from bts->si_buf[] */
|
||||
bts_cbch_get(bts, msu_param->u8Buffer, &g_time);
|
||||
break;
|
||||
default:
|
||||
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
|
||||
break;
|
||||
|
||||
@@ -63,9 +63,11 @@ static const enum GsmL1_LogChComb_t pchan_to_logChComb[_GSM_PCHAN_MAX] = {
|
||||
[GSM_PCHAN_NONE] = GsmL1_LogChComb_0,
|
||||
[GSM_PCHAN_CCCH] = GsmL1_LogChComb_IV,
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = GsmL1_LogChComb_V,
|
||||
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = GsmL1_LogChComb_V,
|
||||
[GSM_PCHAN_TCH_F] = GsmL1_LogChComb_I,
|
||||
[GSM_PCHAN_TCH_H] = GsmL1_LogChComb_II,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = GsmL1_LogChComb_VII,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = GsmL1_LogChComb_VII,
|
||||
[GSM_PCHAN_PDCH] = GsmL1_LogChComb_XIII,
|
||||
//[GSM_PCHAN_TCH_F_PDCH] = FIXME,
|
||||
[GSM_PCHAN_UNKNOWN] = GsmL1_LogChComb_0,
|
||||
@@ -207,9 +209,12 @@ static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
|
||||
/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
|
||||
if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
|
||||
mo->obj_inst.ts_nr == 0) {
|
||||
struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
|
||||
DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
|
||||
mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
|
||||
lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
|
||||
if (cbch)
|
||||
lchan_activate(cbch);
|
||||
}
|
||||
|
||||
/* Send OPSTART ack */
|
||||
@@ -430,11 +435,13 @@ GsmL1_SubCh_t lchan_to_GsmL1_SubCh_t(const struct gsm_lchan *lchan)
|
||||
{
|
||||
switch (lchan->ts->pchan) {
|
||||
case GSM_PCHAN_CCCH_SDCCH4:
|
||||
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
|
||||
if (lchan->type == GSM_LCHAN_CCCH)
|
||||
return GsmL1_SubCh_NA;
|
||||
/* fall-through */
|
||||
case GSM_PCHAN_TCH_H:
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
|
||||
return lchan->nr;
|
||||
case GSM_PCHAN_NONE:
|
||||
case GSM_PCHAN_CCCH:
|
||||
@@ -487,6 +494,12 @@ static const struct sapi_dir sdcch_sapis[] = {
|
||||
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink },
|
||||
};
|
||||
|
||||
static const struct sapi_dir cbch_sapis[] = {
|
||||
{ GsmL1_Sapi_Cbch, GsmL1_Dir_TxDownlink },
|
||||
/* Does the CBCH really have a SACCH in Downlink? */
|
||||
{ GsmL1_Sapi_Sacch, GsmL1_Dir_TxDownlink },
|
||||
};
|
||||
|
||||
static const struct sapi_dir pdtch_sapis[] = {
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
|
||||
@@ -528,6 +541,10 @@ static const struct lchan_sapis sapis_for_lchan[_GSM_LCHAN_MAX] = {
|
||||
.sapis = pdtch_sapis,
|
||||
.num_sapis = ARRAY_SIZE(pdtch_sapis),
|
||||
},
|
||||
[GSM_LCHAN_CBCH] = {
|
||||
.sapis = cbch_sapis,
|
||||
.num_sapis = ARRAY_SIZE(cbch_sapis),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct lchan_sapis sapis_for_ho = {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
|
||||
#include "misc/sysmobts_par.h"
|
||||
#include "femtobts.h"
|
||||
#include "l1_if.h"
|
||||
#include "utils.h"
|
||||
@@ -84,6 +85,47 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_unit_id, cfg_bts_unit_id_cmd,
|
||||
"ipa unit-id eeprom",
|
||||
"ip.access RSL commands\n"
|
||||
"Set the Unit ID of this BTS\n"
|
||||
"Use serial number as Unit ID\n")
|
||||
{
|
||||
int serial_nr, rc;
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_SERNR, &serial_nr);
|
||||
if (rc != 0) {
|
||||
vty_out(vty, "Failed to read serial number%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (serial_nr < 0) {
|
||||
vty_out(vty, "Serial number(%d) not valid%s",
|
||||
serial_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
btsb->unitid_use_eeprom = 1;
|
||||
bts->ip_access.bts_id = 0;
|
||||
bts->ip_access.site_id = serial_nr;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_no_unit_id, cfg_bts_no_unit_id_cmd,
|
||||
"no ipa unit-id eeprom",
|
||||
NO_STR "ip.access RSL commands\n"
|
||||
"Set the Unit ID of this BTS\n"
|
||||
"Use serial number as Unit ID\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->unitid_use_eeprom = 0;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
|
||||
"HIDDEN", "HIDDEN")
|
||||
{
|
||||
@@ -495,6 +537,8 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
|
||||
|
||||
if (btsb->auto_band)
|
||||
vty_out(vty, " auto-band%s", VTY_NEWLINE);
|
||||
if (btsb->unitid_use_eeprom)
|
||||
vty_out(vty, " ipa unit-id eeprom%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
/* FIXME: move to libosmocore ? */
|
||||
@@ -608,6 +652,8 @@ int bts_model_vty_init(struct gsm_bts *bts)
|
||||
|
||||
install_element(BTS_NODE, &cfg_bts_auto_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_auto_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_unit_id_cmd);
|
||||
|
||||
install_element(TRX_NODE, &cfg_trx_clkcal_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_clkcal_eeprom_cmd);
|
||||
|
||||
Reference in New Issue
Block a user