diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index df7d37af..4b0255e3 100644 --- a/src/gprs_bssgp_pcu.cpp +++ b/src/gprs_bssgp_pcu.cpp @@ -143,7 +143,9 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) tbf->llc_length = len; memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset rlc states */ - tbf->state_flags = 0; + tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep + to flags */ + tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); if (!tbf->ms_class && ms_class) tbf->ms_class = ms_class; tbf_update(tbf); diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index 46439911..27cf825d 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -98,7 +98,7 @@ extern struct gprs_rlcmac_bts *gprs_rlcmac_bts; #define RLC_MAX_WS 64 /* max window size */ #define RLC_MAX_LEN 54 /* CS-4 including spare bits */ -#define Tassign_agch 0,800000 /* FIXME: we need a confirm from BTS */ +#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */ #define Tassign_pacch 2,0 /* timeout for pacch assigment */ enum gprs_rlcmac_tbf_state { @@ -146,6 +146,7 @@ enum gprs_rlcmac_tbf_direction { #define GPRS_RLCMAC_FLAG_TO_DL_ACK 5 #define GPRS_RLCMAC_FLAG_TO_UL_ASS 6 #define GPRS_RLCMAC_FLAG_TO_DL_ASS 7 +#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */ struct gprs_rlcmac_tbf { struct llist_head list; @@ -192,6 +193,8 @@ struct gprs_rlcmac_tbf { uint16_t v_a; /* ack state */ char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */ int32_t tx_counter; /* count all transmitted blocks */ + char imsi[16]; /* store IMSI for PCH retransmission */ + uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */ } dl; struct { uint16_t bsn; /* block sequence number */ @@ -349,6 +352,8 @@ 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_imm_ass_cnf(uint8_t *data, uint32_t fn); + int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv); struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging( diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp index dedf98a8..66d29c6d 100644 --- a/src/gprs_rlcmac_data.cpp +++ b/src/gprs_rlcmac_data.cpp @@ -73,6 +73,9 @@ struct rlc_li_field { } __attribute__ ((packed)); } +static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll, + char *imsi); + static int gprs_rlcmac_diag(struct gprs_rlcmac_tbf *tbf) { if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) @@ -179,6 +182,16 @@ int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf) tbf_timer_start(tbf, 3195, bts->t3195, 0); return 0; } + /* resend IMM.ASS on CCCH on timeout */ + if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) + && !(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) { + LOGP(DRLCMAC, LOGL_DEBUG, "Re-send dowlink assignment " + "for TBF=%d on PCH (IMSI=%s)\n", tbf->tfi, + tbf->dir.dl.imsi); + /* send immediate assignment */ + gprs_rlcmac_downlink_assignment(tbf, 0, tbf->dir.dl.imsi); + tbf->dir.dl.wait_confirm = 1; + } } else LOGP(DRLCMAC, LOGL_ERROR, "- Poll Timeout, but no event!\n"); @@ -477,12 +490,13 @@ void tbf_timer_cb(void *_tbf) } if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { /* change state to FLOW, so scheduler will start transmission */ + tbf->dir.dl.wait_confirm = 0; if (tbf->state == GPRS_RLCMAC_ASSIGN) { tbf_new_state(tbf, GPRS_RLCMAC_FLOW); tbf_assign_control_ts(tbf); } else - LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not " - "in assign state\n"); + LOGP(DRLCMAC, LOGL_NOTICE, "Continue flow after " + "IMM.ASS confirm\n"); } break; case 3169: @@ -1621,7 +1635,8 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, LOGP(DRLCMAC, LOGL_DEBUG, "Trigger dowlink assignment on PACCH, " "because another LLC PDU has arrived in between\n"); memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset RLC states */ - tbf->state_flags = 0; + tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep TO flags */ + tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); tbf_update(tbf); gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL); @@ -1778,12 +1793,50 @@ void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf, /* change state */ tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); + strncpy(tbf->dir.dl.imsi, imsi, sizeof(tbf->dir.dl.imsi)); /* send immediate assignment */ gprs_rlcmac_downlink_assignment(tbf, 0, imsi); - /* send immediate assignment */ - gprs_rlcmac_downlink_assignment(tbf, 0, imsi); - /* start timer */ + tbf->dir.dl.wait_confirm = 1; + } +} + +int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn) +{ + struct gprs_rlcmac_tbf *tbf; + uint8_t plen; + uint32_t tlli; + + /* move to IA Rest Octets */ + plen = data[0] >> 2; + data += 1 + plen; + + if ((*data & 0xf0) != 0xd0) { + LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest " + "octets do not start with bit sequence 'HH01' " + "(Packet Downlink Assignment)\n"); + return -EINVAL; + } + + /* get TLLI from downlink assignment */ + tlli = (*data++) << 28; + tlli |= (*data++) << 20; + tlli |= (*data++) << 12; + tlli |= (*data++) << 4; + tlli |= (*data++) >> 4; + + tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF); + if (!tbf) { + LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x " + "does not exit\n", tlli); + return -EINVAL; + } + + LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli); + + if (tbf->dir.dl.wait_confirm) { tbf_timer_start(tbf, 0, Tassign_agch); } - } + + return 0; +} diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp index b5deeb0e..f588c47d 100644 --- a/src/gprs_rlcmac_sched.cpp +++ b/src/gprs_rlcmac_sched.cpp @@ -188,6 +188,10 @@ struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn, && tbf->state != GPRS_RLCMAC_FINISHED) continue; + /* waiting for CCCH IMM.ASS confirm */ + if (tbf->dir.dl.wait_confirm) + continue; + LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at " "RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts); /* next TBF to handle ressource is the next one */ diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp index 1eeacfaf..9e5e2f20 100644 --- a/src/pcu_l1_if.cpp +++ b/src/pcu_l1_if.cpp @@ -194,6 +194,26 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind) return rc; } +static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf) +{ + int rc = 0; + + LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d\n", + data_cnf->sapi, data_cnf->fn); + + switch (data_cnf->sapi) { + case PCU_IF_SAPI_PCH: + rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data, data_cnf->fn); + break; + default: + LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with " + "unsupported sapi %d\n", data_cnf->sapi); + rc = -EINVAL; + } + + return rc; +} + static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req) { int rc = 0; @@ -468,6 +488,9 @@ int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim) case PCU_IF_MSG_DATA_IND: rc = pcu_rx_data_ind(&pcu_prim->u.data_ind); break; + case PCU_IF_MSG_DATA_CNF: + rc = pcu_rx_data_cnf(&pcu_prim->u.data_cnf); + break; case PCU_IF_MSG_RTS_REQ: rc = pcu_rx_rts_req(&pcu_prim->u.rts_req); break; diff --git a/src/pcuif_proto.h b/src/pcuif_proto.h index 493f058c..c27bb7dc 100644 --- a/src/pcuif_proto.h +++ b/src/pcuif_proto.h @@ -1,10 +1,11 @@ #ifndef _PCUIF_PROTO_H #define _PCUIF_PROTO_H -#define PCU_IF_VERSION 0x03 +#define PCU_IF_VERSION 0x04 /* msg_type */ #define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */ +#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */ #define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */ #define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */ #define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */ @@ -137,6 +138,7 @@ struct gsm_pcu_if { union { struct gsm_pcu_if_data data_req; + struct gsm_pcu_if_data data_cnf; struct gsm_pcu_if_data data_ind; struct gsm_pcu_if_rts_req rts_req; struct gsm_pcu_if_rach_ind rach_ind;