aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2012-07-19 13:06:26 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2012-07-19 13:06:26 +0200
commit2b91464862270d72800b6dcc5a521f933fbbd489 (patch)
tree4624e0a8df07923872fc68ec9bad2cf442ff6da8
parent8389fd0513d05d6b2776627e910d582910ff17ce (diff)
Added paging for RR connection on PACCH of active TBFs
Untested
-rw-r--r--src/gprs_rlcmac.cpp209
-rw-r--r--src/gprs_rlcmac.h23
-rw-r--r--src/gprs_rlcmac_sched.cpp4
-rw-r--r--src/pcu_l1_if.cpp73
4 files changed, 282 insertions, 27 deletions
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 7273362c..27cc908a 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -73,6 +73,11 @@ struct gprs_ms_multislot_class gprs_ms_multislot_class[32] = {
LLIST_HEAD(gprs_rlcmac_ul_tbfs);
LLIST_HEAD(gprs_rlcmac_dl_tbfs);
+llist_head *gprs_rlcmac_tbfs_lists[] = {
+ &gprs_rlcmac_ul_tbfs,
+ &gprs_rlcmac_dl_tbfs,
+ NULL
+};
void *rlcmac_tall_ctx;
/* FIXME: spread ressources over multiple TRX. Also add option to use same
@@ -950,6 +955,172 @@ int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
return rc;
}
+/* add paging to paging queue(s) */
+int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv)
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ 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 CS paging\n");
+
+ /* 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] & (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, ts);
+ slot_mask[tbf->trx] |= (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, 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(rlcmac_tall_ctx,
+ struct gprs_rlcmac_paging);
+ if (!pag)
+ return -ENOMEM;
+ pag->chan_needed = chan_needed;
+ memcpy(pag->identity_lv, identity_lv,
+ identity_lv[0] + 1);
+ llist_add(&pag->list,
+ &bts->trx[trx].pdch[ts].paging_list);
+ LOGP(DRLCMAC, LOGL_INFO, "Paging on TRX=%d"
+ "TS=%d\n", trx, ts);
+ }
+ }
+ }
+
+ if (!any_tbf) {
+ LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n");
+ }
+
+ return 0;
+}
+
+struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
+ struct gprs_rlcmac_pdch *pdch)
+{
+ struct gprs_rlcmac_paging *pag;
+
+ pag = llist_entry(pdch->paging_list.next,
+ struct gprs_rlcmac_paging, list);
+ llist_del(&pag->list);
+
+ return pag;
+}
+
+struct msgb *gprs_rlcmac_send_packet_paging_request(
+ struct gprs_rlcmac_pdch *pdch)
+{
+ struct gprs_rlcmac_paging *pag;
+ struct msgb *msg;
+ unsigned wp = 0, len;
+
+ /* no paging, no message */
+ pag = gprs_rlcmac_dequeue_paging(pdch);
+ if (!pag)
+ return NULL;
+
+ LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
+
+ /* alloc message */
+ msg = msgb_alloc(23, "pag ctrl block");
+ if (!msg)
+ return NULL;
+ bitvec *pag_vec = bitvec_alloc(23);
+ if (!pag_vec) {
+ msgb_free(msg);
+ 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 + 32 + 2 + 1;
+ if (pag->identity_lv[0] != 5) {
+ LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
+ "MI != 5 octets!\n");
+ break;
+ }
+ } else {
+ /* MI */
+ LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
+ osmo_hexdump(pag->identity_lv + 1,
+ pag->identity_lv[0]));
+ len = 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");
+ break;
+ }
+ }
+ 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, &pdch->paging_list);
+ break;
+ }
+ write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
+ pag->identity_lv + 1, pag->chan_needed);
+
+ pag = gprs_rlcmac_dequeue_paging(pdch);
+ }
+
+ bitvec_pack(pag_vec, msgb_put(msg, 23));
+ RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(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);
+
+ return msg;
+}
+
// GSM 04.08 9.1.18 Immediate assignment
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
@@ -1248,6 +1419,44 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0;
}
+unsigned write_packet_paging_request(bitvec * dest)
+{
+ unsigned wp = 0;
+
+ bitvec_write_field(dest, wp,0x1,2); // Payload Type
+ bitvec_write_field(dest, wp,0x0,3); // No polling
+ bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
+ bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE
+
+ bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL
+ bitvec_write_field(dest, wp,0x0,1); // No NLN
+
+ return wp;
+}
+
+unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
+ uint8_t *identity, uint8_t chan_needed)
+{
+ bitvec_write_field(dest, wp,0x1,1); // RR connection paging
+
+ if ((identity[0] & 0x07) == 4) {
+ bitvec_write_field(dest, wp,0x0,1); // TMSI
+ identity++;
+ len--;
+ } else {
+ bitvec_write_field(dest, wp,0x0,1); // MI
+ bitvec_write_field(dest, wp,len,4); // MI len
+ }
+ while (len) {
+ bitvec_write_field(dest, wp,*identity++,8); // MI data
+ len--;
+ }
+ bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED
+ bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY
+
+ return wp;
+}
+
/* Send Uplink unit-data to SGSN. */
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf)
{
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index ae188f13..ce278796 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -48,6 +48,7 @@ struct gprs_rlcmac_pdch {
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
+ struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
};
@@ -199,6 +200,15 @@ struct gprs_rlcmac_tbf {
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
+/*
+ * paging entry
+ */
+struct gprs_rlcmac_paging {
+ struct llist_head list;
+ uint8_t chan_needed;
+ uint8_t identity_lv[9];
+};
+
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
uint8_t use_trx, uint8_t first_ts);
@@ -285,6 +295,11 @@ void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
uint8_t ssn, uint8_t *rbb);
+unsigned write_packet_paging_request(bitvec * dest);
+
+unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
+ uint8_t *identity, uint8_t chan_needed);
+
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
uint8_t *data, uint8_t len);
@@ -297,4 +312,12 @@ struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
+int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
+
+struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
+ struct gprs_rlcmac_pdch *pdch);
+
+struct msgb *gprs_rlcmac_send_packet_paging_request(
+ struct gprs_rlcmac_pdch *pdch);
+
#endif // GPRS_RLCMAC_H
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index cd995166..661dfbc1 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -143,6 +143,10 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
tbf = ul_ack_tbf;
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
}
+ /* schedule PACKET PAGING REQUEST */
+ if (!msg && !llist_empty(&pdch->paging_list)) {
+ msg = gprs_rlcmac_send_packet_paging_request(pdch);
+ }
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index ee0d9bed..615d8944 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -247,12 +247,34 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
return rc;
}
+int flush_pdch(struct gprs_rlcmac_pdch *pdch)
+{
+ uint8_t tfi;
+ struct gprs_rlcmac_tbf *tbf;
+ struct gprs_rlcmac_paging *pag;
+
+ /* kick all TBF on slot */
+ for (tfi = 0; tfi < 32; tfi++) {
+ tbf = pdch->ul_tbf[tfi];
+ if (tbf)
+ tbf_free(tbf);
+ tbf = pdch->dl_tbf[tfi];
+ if (tbf)
+ tbf_free(tbf);
+ }
+ /* flush all pending paging messages */
+ while ((pag = gprs_rlcmac_dequeue_paging(pdch)))
+ talloc_free(pag);
+
+ return 0;
+}
+
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ struct gprs_rlcmac_pdch *pdch;
int rc = 0;
- int trx, ts, tfi;
- struct gprs_rlcmac_tbf *tbf;
+ int trx, ts;
int i;
if (info_ind->version != PCU_IF_VERSION) {
@@ -270,16 +292,8 @@ bssgp_failed:
/* free all TBF */
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
- for (ts = 0; ts < 8; ts++) {
- for (tfi = 0; tfi < 32; tfi++) {
- tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
- if (tbf)
- tbf_free(tbf);
- tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
- if (tbf)
- tbf_free(tbf);
- }
- }
+ for (ts = 0; ts < 8; ts++)
+ flush_pdch(&bts->trx[trx].pdch[ts]);
}
gprs_bssgp_destroy();
return 0;
@@ -366,28 +380,21 @@ bssgp_failed:
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
+ pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
- if (!bts->trx[trx].pdch[ts].enable)
+ if (!pdch->enable)
pcu_tx_act_req(trx, ts, 1);
- bts->trx[trx].pdch[ts].enable = 1;
- bts->trx[trx].pdch[ts].tsc =
- info_ind->trx[trx].tsc[ts];
+ pdch->enable = 1;
+ pdch->tsc = info_ind->trx[trx].tsc[ts];
+ INIT_LLIST_HEAD(&pdch->paging_list);
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
- if (bts->trx[trx].pdch[ts].enable)
+ if (pdch->enable)
pcu_tx_act_req(trx, ts, 0);
- bts->trx[trx].pdch[ts].enable = 0;
- /* kick all TBF on slot */
- for (tfi = 0; tfi < 32; tfi++) {
- tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
- if (tbf)
- tbf_free(tbf);
- tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
- if (tbf)
- tbf_free(tbf);
- }
+ pdch->enable = 0;
+ flush_pdch(pdch);
}
}
}
@@ -429,6 +436,15 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
return 0;
}
+static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req)
+{
+ LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d "
+ "length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]);
+
+ return gprs_rlcmac_add_paging(pag_req->chan_needed,
+ pag_req->identity_lv);
+}
+
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
@@ -449,6 +465,9 @@ int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
case PCU_IF_MSG_TIME_IND:
rc = pcu_rx_time_ind(&pcu_prim->u.time_ind);
break;
+ case PCU_IF_MSG_PAG_REQ:
+ rc = pcu_rx_pag_req(&pcu_prim->u.pag_req);
+ break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);