Compare commits

...

8 Commits

Author SHA1 Message Date
Harald Welte
c328371b54 cbch: add debug statements 2014-12-30 00:24:52 +01:00
Harald Welte
ffcdd0da7e CBCH: Implement CBCH block segmentation and RSL_MT_SMS_BC_CMD
* CBCH load indications are not yet sent
* The queue length is not yet limited!
2014-12-30 00:24:45 +01:00
Harald Welte
265d0635f2 Initial CBCH support
This should handle OML channel combinations with CBCH and activate the
CBCH SAPI towards the DSP correspondingly.  What is still missing is
sending any actual information over the CBCH in respons to the
PH-RTS.ind coming up from L1.
2014-12-30 00:24:40 +01:00
Harald Welte
03483cf0f2 etws_p1: Add missing 'Group Call Information' to P1 Rest Octets 2014-12-27 23:43:09 +01:00
Holger Hans Peter Freyther
7f12860f08 wip... encoding and stuff 2014-12-27 23:05:51 +01:00
Holger Hans Peter Freyther
96efd189cc wip... 2014-12-27 19:33:23 +01:00
Holger Hans Peter Freyther
1f27fda194 wip... handle 2014-12-27 19:05:09 +01:00
Holger Hans Peter Freyther
5a906c7b72 sysmobts: WIP: allow to use the serial number as unit id 2014-12-26 16:27:36 +01:00
17 changed files with 564 additions and 15 deletions

View File

@@ -1,3 +1,3 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \ 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 \ 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
View 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);

View File

@@ -14,6 +14,7 @@
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91 #define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
struct pcu_sock_state; struct pcu_sock_state;
struct smscb_msg;
struct gsm_network { struct gsm_network {
struct llist_head bts_list; struct llist_head bts_list;
@@ -84,6 +85,12 @@ struct gsm_bts_role_bts {
/* used by the sysmoBTS to adjust band */ /* used by the sysmoBTS to adjust band */
uint8_t auto_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 { enum lchan_ciph_state {

View File

@@ -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_queue_length(struct paging_state *ps);
int paging_buffer_space(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 #endif

View File

@@ -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 \ 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 \
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

View File

@@ -156,6 +156,8 @@ int bts_init(struct gsm_bts *bts)
initialized = 1; initialized = 1;
} }
INIT_LLIST_HEAD(&btsb->smscb_state.queue);
return rc; return rc;
} }

202
src/common/cbch.c Normal file
View 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
View 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;
}

View File

@@ -70,11 +70,13 @@ static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts,
rc = 1; rc = 1;
break; break;
case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
fn_mod = fn % 102; fn_mod = fn % 102;
if (fn_mod == 11) if (fn_mod == 11)
rc = 1; rc = 1;
break; break;
case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
fn_mod = fn % 102; fn_mod = fn % 102;
if (fn_mod == 36) if (fn_mod == 36)
rc = 1; rc = 1;
@@ -214,9 +216,11 @@ static const uint8_t subslots_per_pchan[_GSM_PCHAN_MAX] = {
[GSM_PCHAN_NONE] = 0, [GSM_PCHAN_NONE] = 0,
[GSM_PCHAN_CCCH] = 0, [GSM_PCHAN_CCCH] = 0,
[GSM_PCHAN_CCCH_SDCCH4] = 4, [GSM_PCHAN_CCCH_SDCCH4] = 4,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
[GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_F] = 1,
[GSM_PCHAN_TCH_H] = 2, [GSM_PCHAN_TCH_H] = 2,
[GSM_PCHAN_SDCCH8_SACCH8C] = 8, [GSM_PCHAN_SDCCH8_SACCH8C] = 8,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
/* FIXME: what about dynamic TCH_F_TCH_H ? */ /* FIXME: what about dynamic TCH_F_TCH_H ? */
[GSM_PCHAN_TCH_F_PDCH] = 1, [GSM_PCHAN_TCH_F_PDCH] = 1,
}; };

View File

@@ -601,11 +601,18 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
unsigned int i; unsigned int i;
switch (ts->pchan) { switch (ts->pchan) {
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
/* fallthrough */
case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4:
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
lchan = &ts->lchan[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; lchan->type = GSM_LCHAN_SDCCH;
} }
}
/* fallthrough */ /* fallthrough */
case GSM_PCHAN_CCCH: case GSM_PCHAN_CCCH:
lchan = &ts->lchan[4]; lchan = &ts->lchan[4];
@@ -621,11 +628,18 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
lchan->type = GSM_LCHAN_TCH_H; lchan->type = GSM_LCHAN_TCH_H;
} }
break; break;
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
/* fallthrough */
case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C:
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
lchan = &ts->lchan[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; lchan->type = GSM_LCHAN_SDCCH;
} }
}
break; break;
case GSM_PCHAN_PDCH: case GSM_PCHAN_PDCH:
lchan = &ts->lchan[0]; lchan = &ts->lchan[0];

View File

@@ -46,6 +46,9 @@
#define MAX_PAGING_BLOCKS_CCCH 9 #define MAX_PAGING_BLOCKS_CCCH 9
#define MAX_BS_PA_MFRMS 9 #define MAX_BS_PA_MFRMS 9
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
enum paging_record_type { enum paging_record_type {
PAGING_RECORD_PAGING, PAGING_RECORD_PAGING,
PAGING_RECORD_IMM_ASS 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) #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 chan1, const uint8_t *identity2_lv,
uint8_t chan2) 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); cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
if (identity2_lv) if (identity2_lv)
cur = lv_put(cur, identity2_lv[0], identity2_lv+1); 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); pt1->l2_plen = L2_PLEN(cur - out_buf);
return 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; return cur - out_buf;
} }
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
static struct paging_record *dequeue_pr(struct llist_head *group_q) static struct paging_record *dequeue_pr(struct llist_head *group_q)
{ {
struct paging_record *pr; 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 */ /* There is nobody to be paged, send Type1 with two empty ID */
if (llist_empty(group_q)) { if (llist_empty(group_q)) {
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n"); //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); NULL, 0);
*is_empty = 1; *is_empty = 1;
} else { } 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) { } else if (num_pr == 1) {
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n"); 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.identity_lv,
pr[0]->u.paging.chan_needed, pr[0]->u.paging.chan_needed,
NULL, 0); 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 /* 2 (any type) or
* 3 or 4, of which only 2 will be sent */ * 3 or 4, of which only 2 will be sent */
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n"); 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.identity_lv,
pr[0]->u.paging.chan_needed, pr[0]->u.paging.chan_needed,
pr[1]->u.paging.identity_lv, pr[1]->u.paging.identity_lv,

View File

@@ -1,7 +1,7 @@
/* GSM TS 08.58 RSL, BTS Side */ /* GSM TS 08.58 RSL, BTS Side */
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> /* (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 * All Rights Reserved
* *
@@ -46,6 +46,7 @@
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h> #include <osmo-bts/pcu_if.h>
#include <osmo-bts/handover.h> #include <osmo-bts/handover.h>
#include <osmo-bts/cbch.h>
//#define FAKE_CIPH_MODE_COMPL //#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); chan_nr, ts->pchan);
} else if ((cbits & 0x1c) == 0x04) { } else if ((cbits & 0x1c) == 0x04) {
lch_idx = cbits & 0x3; /* SDCCH/4 */ 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", LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan); chan_nr, ts->pchan);
} else if ((cbits & 0x18) == 0x08) { } else if ((cbits & 0x18) == 0x08) {
lch_idx = cbits & 0x7; /* SDCCH/8 */ 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", LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan); chan_nr, ts->pchan);
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { } 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; 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 */ /* 8.6.2 SACCH FILLING */
static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) 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: case RSL_MT_PAGING_CMD:
ret = rsl_rx_paging_cmd(trx, msg); ret = rsl_rx_paging_cmd(trx, msg);
break; break;
case RSL_MT_SMS_BC_REQ:
case RSL_MT_SMS_BC_CMD: 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: case RSL_MT_NOT_CMD:
LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n", LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n",
rsl_msg_name(cch->c.msg_type)); rsl_msg_name(cch->c.msg_type));

View File

@@ -70,8 +70,12 @@ char *bts_support_comb_name(uint8_t chan_comb)
return("BCCH"); return("BCCH");
if (chan_comb == NM_CHANC_BCCHComb) if (chan_comb == NM_CHANC_BCCHComb)
return("BCCH+SDCCH/4"); return("BCCH+SDCCH/4");
if (chan_comb == NM_CHANC_BCCH_CBCH)
return("BCCH+CBCH+SDCCH/4");
if (chan_comb == NM_CHANC_SDCCH) if (chan_comb == NM_CHANC_SDCCH)
return("SDCCH/8"); return("SDCCH/8");
if (chan_comb == NM_CHANC_SDCCH_CBCH)
return("SDCCH/8+CBCH");
if (chan_comb == NM_CHANC_TCHFull) if (chan_comb == NM_CHANC_TCHFull)
return("TCH/F"); return("TCH/F");
if (chan_comb == NM_CHANC_TCHHalf) if (chan_comb == NM_CHANC_TCHHalf)

View File

@@ -1,6 +1,6 @@
/* OsmoBTS VTY interface */ /* OsmoBTS VTY interface */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org> /* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
* *
* All Rights Reserved * All Rights Reserved
* *
@@ -44,6 +44,7 @@
#include <osmo-bts/bts_model.h> #include <osmo-bts/bts_model.h>
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
#include <osmo-bts/vty.h> #include <osmo-bts/vty.h>
#include <osmo-bts/paging.h>
enum node_type bts_vty_go_parent(struct vty *vty) 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); 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) static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
{ {
struct gsm_bts_role_bts *btsb = bts->role; 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_rejected_msgs, btsb->agch_queue_agch_msgs,
btsb->agch_queue_pch_msgs, btsb->agch_queue_pch_msgs,
VTY_NEWLINE); VTY_NEWLINE);
vty_out(vty, " CBCH backlog queue length: %u%s",
llist_length(&btsb->smscb_state.queue), VTY_NEWLINE);
#if 0 #if 0
vty_out(vty, " Paging: %u pending requests, %u free slots%s", vty_out(vty, " Paging: %u pending requests, %u free slots%s",
paging_pending_requests_nr(bts), paging_pending_requests_nr(bts),
@@ -614,6 +628,59 @@ DEFUN(bts_t_t_l_jitter_buf,
return CMD_SUCCESS; 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) int bts_vty_init(const struct log_info *cat)
{ {
install_element_ve(&show_bts_cmd); 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(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd); install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
return 0; return 0;
} }

View File

@@ -49,6 +49,7 @@
#include <osmo-bts/measurement.h> #include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h> #include <osmo-bts/pcu_if.h>
#include <osmo-bts/handover.h> #include <osmo-bts/handover.h>
#include <osmo-bts/cbch.h>
#include <sysmocom/femtobts/superfemto.h> #include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h> #include <sysmocom/femtobts/gsml1prim.h>
@@ -587,6 +588,10 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
case GsmL1_Sapi_Prach: case GsmL1_Sapi_Prach:
goto empty_frame; goto empty_frame;
break; break;
case GsmL1_Sapi_Cbch:
/* get them from bts->si_buf[] */
bts_cbch_get(bts, msu_param->u8Buffer, &g_time);
break;
default: default:
memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN); memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
break; break;

View File

@@ -63,9 +63,11 @@ static const enum GsmL1_LogChComb_t pchan_to_logChComb[_GSM_PCHAN_MAX] = {
[GSM_PCHAN_NONE] = GsmL1_LogChComb_0, [GSM_PCHAN_NONE] = GsmL1_LogChComb_0,
[GSM_PCHAN_CCCH] = GsmL1_LogChComb_IV, [GSM_PCHAN_CCCH] = GsmL1_LogChComb_IV,
[GSM_PCHAN_CCCH_SDCCH4] = GsmL1_LogChComb_V, [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_F] = GsmL1_LogChComb_I,
[GSM_PCHAN_TCH_H] = GsmL1_LogChComb_II, [GSM_PCHAN_TCH_H] = GsmL1_LogChComb_II,
[GSM_PCHAN_SDCCH8_SACCH8C] = GsmL1_LogChComb_VII, [GSM_PCHAN_SDCCH8_SACCH8C] = GsmL1_LogChComb_VII,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = GsmL1_LogChComb_VII,
[GSM_PCHAN_PDCH] = GsmL1_LogChComb_XIII, [GSM_PCHAN_PDCH] = GsmL1_LogChComb_XIII,
//[GSM_PCHAN_TCH_F_PDCH] = FIXME, //[GSM_PCHAN_TCH_F_PDCH] = FIXME,
[GSM_PCHAN_UNKNOWN] = GsmL1_LogChComb_0, [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 */ /* 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 && if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
mo->obj_inst.ts_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"); DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML; mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
lchan_activate(&mo->bts->c0->ts[0].lchan[4]); lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
if (cbch)
lchan_activate(cbch);
} }
/* Send OPSTART ack */ /* Send OPSTART ack */
@@ -430,11 +435,13 @@ GsmL1_SubCh_t lchan_to_GsmL1_SubCh_t(const struct gsm_lchan *lchan)
{ {
switch (lchan->ts->pchan) { switch (lchan->ts->pchan) {
case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
if (lchan->type == GSM_LCHAN_CCCH) if (lchan->type == GSM_LCHAN_CCCH)
return GsmL1_SubCh_NA; return GsmL1_SubCh_NA;
/* fall-through */ /* fall-through */
case GSM_PCHAN_TCH_H: case GSM_PCHAN_TCH_H:
case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
return lchan->nr; return lchan->nr;
case GSM_PCHAN_NONE: case GSM_PCHAN_NONE:
case GSM_PCHAN_CCCH: case GSM_PCHAN_CCCH:
@@ -487,6 +494,12 @@ static const struct sapi_dir sdcch_sapis[] = {
{ GsmL1_Sapi_Sacch, GsmL1_Dir_RxUplink }, { 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[] = { static const struct sapi_dir pdtch_sapis[] = {
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink }, { GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink }, { GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
@@ -528,6 +541,10 @@ static const struct lchan_sapis sapis_for_lchan[_GSM_LCHAN_MAX] = {
.sapis = pdtch_sapis, .sapis = pdtch_sapis,
.num_sapis = ARRAY_SIZE(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 = { static const struct lchan_sapis sapis_for_ho = {

View File

@@ -43,6 +43,7 @@
#include <osmo-bts/logging.h> #include <osmo-bts/logging.h>
#include <osmo-bts/vty.h> #include <osmo-bts/vty.h>
#include "misc/sysmobts_par.h"
#include "femtobts.h" #include "femtobts.h"
#include "l1_if.h" #include "l1_if.h"
#include "utils.h" #include "utils.h"
@@ -84,6 +85,47 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
return CMD_SUCCESS; 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, DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
"HIDDEN", "HIDDEN") "HIDDEN", "HIDDEN")
{ {
@@ -495,6 +537,8 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
if (btsb->auto_band) if (btsb->auto_band)
vty_out(vty, " auto-band%s", VTY_NEWLINE); 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 ? */ /* 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_auto_band_cmd);
install_element(BTS_NODE, &cfg_bts_no_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_cmd);
install_element(TRX_NODE, &cfg_trx_clkcal_eeprom_cmd); install_element(TRX_NODE, &cfg_trx_clkcal_eeprom_cmd);