286 lines
7.0 KiB
C++
286 lines
7.0 KiB
C++
/*
|
|
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <bts.h>
|
|
#include <poll_controller.h>
|
|
#include <tbf.h>
|
|
|
|
#include <gprs_rlcmac.h>
|
|
#include <gprs_debug.h>
|
|
|
|
extern "C" {
|
|
#include <osmocom/core/talloc.h>
|
|
#include <osmocom/core/msgb.h>
|
|
}
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
extern void *tall_pcu_ctx;
|
|
|
|
static llist_head *gprs_rlcmac_tbfs_lists[] = {
|
|
&gprs_rlcmac_ul_tbfs,
|
|
&gprs_rlcmac_dl_tbfs,
|
|
NULL
|
|
};
|
|
|
|
static BTS s_bts;
|
|
|
|
BTS* BTS::main_bts()
|
|
{
|
|
return &s_bts;
|
|
}
|
|
|
|
struct gprs_rlcmac_bts *BTS::bts_data()
|
|
{
|
|
return &m_bts;
|
|
}
|
|
|
|
struct gprs_rlcmac_bts *bts_main_data()
|
|
{
|
|
return BTS::main_bts()->bts_data();
|
|
}
|
|
|
|
BTS::BTS()
|
|
: m_cur_fn(0)
|
|
, m_pollController(*this)
|
|
{
|
|
memset(&m_bts, 0, sizeof(m_bts));
|
|
m_bts.bts = this;
|
|
}
|
|
|
|
void BTS::set_current_frame_number(int fn)
|
|
{
|
|
m_cur_fn = fn;
|
|
m_pollController.expireTimedout(m_cur_fn);
|
|
}
|
|
|
|
int BTS::add_paging(uint8_t chan_needed, uint8_t *identity_lv)
|
|
{
|
|
uint8_t l, trx, ts, any_tbf = 0;
|
|
struct gprs_rlcmac_tbf *tbf;
|
|
struct gprs_rlcmac_paging *pag;
|
|
uint8_t slot_mask[8];
|
|
int8_t first_ts; /* must be signed */
|
|
|
|
LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n",
|
|
chan_needed, osmo_hexdump(identity_lv + 1, identity_lv[0]));
|
|
|
|
/* collect slots to page
|
|
* Mark slots for every TBF, but only mark one of it.
|
|
* Mark only the first slot found.
|
|
* Don't mark, if TBF uses a different slot that is already marked. */
|
|
memset(slot_mask, 0, sizeof(slot_mask));
|
|
for (l = 0; gprs_rlcmac_tbfs_lists[l]; l++) {
|
|
llist_for_each_entry(tbf, gprs_rlcmac_tbfs_lists[l], list) {
|
|
first_ts = -1;
|
|
for (ts = 0; ts < 8; ts++) {
|
|
if (tbf->pdch[ts]) {
|
|
/* remember the first slot found */
|
|
if (first_ts < 0)
|
|
first_ts = ts;
|
|
/* break, if we already marked a slot */
|
|
if ((slot_mask[tbf->trx_no] & (1 << ts)))
|
|
break;
|
|
}
|
|
}
|
|
/* mark first slot found, if none is marked already */
|
|
if (ts == 8 && first_ts >= 0) {
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses "
|
|
"TRX=%d TS=%d, so we mark\n",
|
|
(tbf->direction == GPRS_RLCMAC_UL_TBF)
|
|
? "UL" : "DL",
|
|
tbf->tfi, tbf->trx_no, first_ts);
|
|
slot_mask[tbf->trx_no] |= (1 << first_ts);
|
|
} else
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- %s TBF=%d uses "
|
|
"already marked TRX=%d TS=%d\n",
|
|
(tbf->direction == GPRS_RLCMAC_UL_TBF)
|
|
? "UL" : "DL",
|
|
tbf->tfi, tbf->trx_no, ts);
|
|
}
|
|
}
|
|
|
|
/* Now we have a list of marked slots. Every TBF uses at least one
|
|
* of these slots. */
|
|
|
|
/* schedule paging to all marked slots */
|
|
for (trx = 0; trx < 8; trx++) {
|
|
if (slot_mask[trx] == 0)
|
|
continue;
|
|
any_tbf = 1;
|
|
for (ts = 0; ts < 8; ts++) {
|
|
if ((slot_mask[trx] & (1 << ts))) {
|
|
/* schedule */
|
|
pag = talloc_zero(tall_pcu_ctx,
|
|
struct gprs_rlcmac_paging);
|
|
if (!pag)
|
|
return -ENOMEM;
|
|
pag->chan_needed = chan_needed;
|
|
memcpy(pag->identity_lv, identity_lv,
|
|
identity_lv[0] + 1);
|
|
m_bts.trx[trx].pdch[ts].add_paging(pag);
|
|
LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of "
|
|
"TRX=%d TS=%d\n", trx, ts);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!any_tbf)
|
|
LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gprs_rlcmac_pdch::enable()
|
|
{
|
|
/* TODO: Check if there are still allocated resources.. */
|
|
INIT_LLIST_HEAD(&paging_list);
|
|
m_is_enabled = 1;
|
|
}
|
|
|
|
void gprs_rlcmac_pdch::disable()
|
|
{
|
|
/* TODO.. kick free_resources once we know the TRX/TS we are on */
|
|
m_is_enabled = 0;
|
|
}
|
|
|
|
void gprs_rlcmac_pdch::free_resources(uint8_t trx, uint8_t ts)
|
|
{
|
|
struct gprs_rlcmac_paging *pag;
|
|
struct gprs_rlcmac_sba *sba, *sba2;
|
|
|
|
/* we are not enabled. there should be no resources */
|
|
if (!is_enabled())
|
|
return;
|
|
|
|
/* kick all TBF on slot */
|
|
gprs_rlcmac_tbf::free_all(this);
|
|
|
|
/* flush all pending paging messages */
|
|
while ((pag = dequeue_paging()))
|
|
talloc_free(pag);
|
|
|
|
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
|
|
if (sba->trx == trx && sba->ts == ts) {
|
|
llist_del(&sba->list);
|
|
talloc_free(sba);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
|
|
{
|
|
struct gprs_rlcmac_paging *pag;
|
|
|
|
if (llist_empty(&paging_list))
|
|
return NULL;
|
|
pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list);
|
|
llist_del(&pag->list);
|
|
|
|
return pag;
|
|
}
|
|
|
|
struct msgb *gprs_rlcmac_pdch::packet_paging_request()
|
|
{
|
|
struct gprs_rlcmac_paging *pag;
|
|
struct msgb *msg;
|
|
unsigned wp = 0, len;
|
|
|
|
/* no paging, no message */
|
|
pag = dequeue_paging();
|
|
if (!pag)
|
|
return NULL;
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
|
|
|
|
/* alloc message */
|
|
msg = msgb_alloc(23, "pag ctrl block");
|
|
if (!msg) {
|
|
talloc_free(pag);
|
|
return NULL;
|
|
}
|
|
bitvec *pag_vec = bitvec_alloc(23);
|
|
if (!pag_vec) {
|
|
msgb_free(msg);
|
|
talloc_free(pag);
|
|
return NULL;
|
|
}
|
|
wp = write_packet_paging_request(pag_vec);
|
|
|
|
/* loop until message is full */
|
|
while (pag) {
|
|
/* try to add paging */
|
|
if ((pag->identity_lv[1] & 0x07) == 4) {
|
|
/* TMSI */
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n",
|
|
ntohl(*((uint32_t *)(pag->identity_lv + 1))));
|
|
len = 1 + 1 + 1 + 32 + 2 + 1;
|
|
if (pag->identity_lv[0] != 5) {
|
|
LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
|
|
"MI != 5 octets!\n");
|
|
goto continue_next;
|
|
}
|
|
} else {
|
|
/* MI */
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
|
|
osmo_hexdump(pag->identity_lv + 1,
|
|
pag->identity_lv[0]));
|
|
len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1;
|
|
if (pag->identity_lv[0] > 8) {
|
|
LOGP(DRLCMAC, LOGL_ERROR, "Paging with "
|
|
"MI > 8 octets!\n");
|
|
goto continue_next;
|
|
}
|
|
}
|
|
if (wp + len > 184) {
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule "
|
|
"next time\n");
|
|
/* put back paging record, because does not fit */
|
|
llist_add_tail(&pag->list, &paging_list);
|
|
break;
|
|
}
|
|
write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
|
|
pag->identity_lv + 1, pag->chan_needed);
|
|
|
|
continue_next:
|
|
talloc_free(pag);
|
|
pag = dequeue_paging();
|
|
}
|
|
|
|
bitvec_pack(pag_vec, msgb_put(msg, 23));
|
|
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n");
|
|
decode_gsm_rlcmac_downlink(pag_vec, mac_control_block);
|
|
LOGPC(DCSN1, LOGL_NOTICE, "\n");
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n");
|
|
bitvec_free(pag_vec);
|
|
talloc_free(mac_control_block);
|
|
|
|
return msg;
|
|
}
|
|
|
|
void gprs_rlcmac_pdch::add_paging(struct gprs_rlcmac_paging *pag)
|
|
{
|
|
llist_add(&pag->list, &paging_list);
|
|
}
|