From 660116fb9d7b382808377274ae4aed7c45e980e3 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 29 Dec 2014 01:02:29 +0100 Subject: 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! --- include/osmo-bts/Makefile.am | 2 +- include/osmo-bts/bts.h | 1 - include/osmo-bts/cbch.h | 16 ++++ include/osmo-bts/gsm_data.h | 6 ++ src/common/bts.c | 2 + src/common/cbch.c | 191 ++++++++++++++++++++++++++++++++++++++++++- src/common/rsl.c | 27 +++++- src/common/vty.c | 15 +++- src/osmo-bts-sysmo/l1_if.c | 1 + 9 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 include/osmo-bts/cbch.h diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am index a55e6421..67fd2bf1 100644 --- a/include/osmo-bts/Makefile.am +++ b/include/osmo-bts/Makefile.am @@ -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 diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h index de4870f9..e4899736 100644 --- a/include/osmo-bts/bts.h +++ b/include/osmo-bts/bts.h @@ -33,7 +33,6 @@ int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time); uint8_t *lchan_sacch_get(struct gsm_lchan *lchan); -int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time); int lchan_init_lapdm(struct gsm_lchan *lchan); void load_timer_start(struct gsm_bts *bts); diff --git a/include/osmo-bts/cbch.h b/include/osmo-bts/cbch.h new file mode 100644 index 00000000..b4ac409f --- /dev/null +++ b/include/osmo-bts/cbch.h @@ -0,0 +1,16 @@ +#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); diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h index 5e0af775..0562ecaf 100644 --- a/include/osmo-bts/gsm_data.h +++ b/include/osmo-bts/gsm_data.h @@ -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,11 @@ struct gsm_bts_role_bts { /* 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 { diff --git a/src/common/bts.c b/src/common/bts.c index c7d36df8..f6ddfaa0 100644 --- a/src/common/bts.c +++ b/src/common/bts.c @@ -156,6 +156,8 @@ int bts_init(struct gsm_bts *bts) initialized = 1; } + INIT_LLIST_HEAD(&btsb->smscb_state.queue); + return rc; } diff --git a/src/common/cbch.c b/src/common/cbch.c index e3ce8975..0981c483 100644 --- a/src/common/cbch.c +++ b/src/common/cbch.c @@ -1,8 +1,195 @@ +/* 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 -int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time) +#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; + } + + /* 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++; + + /* 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) { - /* FIXME: Actaully schedule + return CBCH messages */ + 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 */ + outbuf[0] = 0x2f; + memset(outbuf+1, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN); + break; + } + + return rc; +} diff --git a/src/common/rsl.c b/src/common/rsl.c index 000c1797..ff9833d4 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -1,7 +1,7 @@ /* GSM TS 08.58 RSL, BTS Side */ /* (C) 2011 by Andreas Eversberg - * (C) 2011-2013 by Harald Welte + * (C) 2011-2014 by Harald Welte * * All Rights Reserved * @@ -46,6 +46,7 @@ #include #include #include +#include //#define FAKE_CIPH_MODE_COMPL @@ -414,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) { @@ -1688,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)); diff --git a/src/common/vty.c b/src/common/vty.c index 6b57b0b6..d81a8980 100644 --- a/src/common/vty.c +++ b/src/common/vty.c @@ -1,6 +1,6 @@ /* OsmoBTS VTY interface */ -/* (C) 2011 by Harald Welte +/* (C) 2011-2014 by Harald Welte * * All Rights Reserved * @@ -473,6 +473,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 +514,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), diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c index 44eff967..32e82d83 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3