/* Data block transfer * * Copyright (C) 2012 Ivan Klyuchnikov * Copyright (C) 2012 Andreas Eversberg * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include static struct gprs_rlcmac_cs gprs_rlcmac_cs[] = { /* frame length data block max payload */ { 0, 0, 0 }, { 23, 23, 20 }, /* CS-1 */ { 34, 33, 30 }, /* CS-2 */ { 40, 39, 36 }, /* CS-3 */ { 54, 53, 50 }, /* CS-4 */ }; extern void *tall_pcu_ctx; /* After sending these frames, we poll for ack/nack. */ #define POLL_ACK_AFTER_FRAMES 20 /* If acknowledgement to uplink/downlink assignmentshould be polled */ #define POLLING_ASSIGNMENT_DL 1 #define POLLING_ASSIGNMENT_UL 1 static void gprs_rlcmac_downlink_assignment( gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi); static int gprs_rlcmac_diag(struct gprs_rlcmac_tbf *tbf) { return tbf->rlcmac_diag(); } int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf) { LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for %s TBF=%d\n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi); tbf->poll_state = GPRS_RLCMAC_POLL_NONE; if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) { if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) { LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " "PACKET CONTROL ACK for PACKET UPLINK ACK\n"); gprs_rlcmac_diag(tbf); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK); } tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; debug_diagram(bts->bts, tbf->diag, "timeout UL-ACK"); if (tbf->state_is(GPRS_RLCMAC_FINISHED)) { tbf->dir.ul.n3103++; if (tbf->dir.ul.n3103 == bts->n3103) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3103 exceeded\n"); debug_diagram(bts->bts, tbf->diag, "N3103 exceeded"); tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); tbf_timer_start(tbf, 3169, bts->t3169, 0); return 0; } /* reschedule UL ack */ tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; } } else if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) { if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) { LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " "PACKET CONTROL ACK for PACKET UPLINK " "ASSIGNMENT.\n"); gprs_rlcmac_diag(tbf); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS); } tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; debug_diagram(tbf->bts, tbf->diag, "timeout UL-ASS"); tbf->n3105++; if (tbf->n3105 == bts->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts->bts, tbf->diag, "N3105 exceeded"); tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); tbf_timer_start(tbf, 3195, bts->t3195, 0); return 0; } /* reschedule UL assignment */ tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS; } else if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) { if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) { LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " "PACKET CONTROL ACK for PACKET DOWNLINK " "ASSIGNMENT.\n"); gprs_rlcmac_diag(tbf); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS); } tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; debug_diagram(bts->bts, tbf->diag, "timeout DL-ASS"); tbf->n3105++; if (tbf->n3105 == bts->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts->bts, tbf->diag, "N3105 exceeded"); tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); tbf_timer_start(tbf, 3195, bts->t3195, 0); return 0; } /* reschedule DL assignment */ tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; } else if (tbf->direction == GPRS_RLCMAC_DL_TBF) { if (!(tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) { LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling " "PACKET DOWNLINK ACK.\n"); gprs_rlcmac_diag(tbf); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); } debug_diagram(bts->bts, tbf->diag, "timeout DL-ACK"); tbf->n3105++; if (tbf->n3105 == bts->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); debug_diagram(bts->bts, tbf->diag, "N3105 exceeded"); tbf_new_state(tbf, GPRS_RLCMAC_RELEASING); 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"); return 0; } #ifdef DEBUG_DL_ASS_IDLE char debug_imsi[16]; #endif /* * UL data block flow */ struct msgb *gprs_rlcmac_send_uplink_ack( struct gprs_rlcmac_tbf *tbf, uint32_t fn) { int final = (tbf->state_is(GPRS_RLCMAC_FINISHED)); gprs_rlcmac_bts *bts = tbf->bts->bts_data(); struct msgb *msg; if (final) { if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " "sheduled for TBF=%d, so we must wait for " "final uplink ack...\n", tbf->tfi); return NULL; } if (tbf->bts->sba()->find(tbf->trx_no, tbf->control_ts, (fn + 13) % 2715648)) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " "scheduled for single block allocation...\n"); return NULL; } } msg = msgb_alloc(23, "rlcmac_ul_ack"); if (!msg) return NULL; bitvec *ack_vec = bitvec_alloc(23); if (!ack_vec) { msgb_free(msg); return NULL; } bitvec_unhex(ack_vec, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); Encoding::write_packet_uplink_ack(bts, mac_control_block, tbf, final); encode_gsm_rlcmac_downlink(ack_vec, mac_control_block); bitvec_pack(ack_vec, msgb_put(msg, 23)); bitvec_free(ack_vec); talloc_free(mac_control_block); /* now we must set this flag, so we are allowed to assign downlink * TBF on PACCH. it is only allowed when TLLI is aknowledged. */ tbf->dir.ul.contention_resolution_done = 1; if (final) { tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; tbf->poll_fn = (fn + 13) % 2715648; /* waiting for final acknowledge */ tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK; tbf->dir.ul.final_ack_sent = 1; } else tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE; debug_diagram(bts->bts, tbf->diag, "send UL-ACK"); return msg; } struct msgb *gprs_rlcmac_send_packet_uplink_assignment( struct gprs_rlcmac_tbf *tbf, uint32_t fn) { struct msgb *msg; struct gprs_rlcmac_tbf *new_tbf; gprs_rlcmac_bts *bts = tbf->bts->bts_data(); #if POLLING_ASSIGNMENT_UL == 1 if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " "sheduled for TBF=%d, so we must wait for uplink " "assignment...\n", tbf->tfi); return NULL; } if (tbf->bts->sba()->find(tbf->trx_no, tbf->control_ts, (fn + 13) % 2715648)) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already scheduled for " "single block allocation...\n"); return NULL; } #endif /* on down TBF we get the uplink TBF to be assigned. */ if (tbf->direction == GPRS_RLCMAC_DL_TBF) new_tbf = tbf->bts->tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF); else new_tbf = tbf; if (!new_tbf) { LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink " "assignment at downlink TBF=%d, but there is no uplink " "TBF\n", tbf->tfi); tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; return NULL; } msg = msgb_alloc(23, "rlcmac_ul_ass"); if (!msg) return NULL; LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Uplink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli); bitvec *ass_vec = bitvec_alloc(23); if (!ass_vec) { msgb_free(msg); return NULL; } bitvec_unhex(ass_vec, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); Encoding::write_packet_uplink_assignment(bts, ass_vec, tbf->tfi, (tbf->direction == GPRS_RLCMAC_DL_TBF), tbf->tlli, tbf->tlli_valid, new_tbf, POLLING_ASSIGNMENT_UL, bts->alpha, bts->gamma, -1); bitvec_pack(ass_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 Uplink Assignment +++++++++++++++++++++++++\n"); decode_gsm_rlcmac_downlink(ass_vec, mac_control_block); LOGPC(DCSN1, LOGL_NOTICE, "\n"); LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n"); bitvec_free(ass_vec); talloc_free(mac_control_block); #if POLLING_ASSIGNMENT_UL == 1 tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; tbf->poll_fn = (fn + 13) % 2715648; tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK; #else tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE; tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW); tbf_assign_control_ts(new_tbf); #endif debug_diagram(bts->bts, tbf->diag, "send UL-ASS"); return msg; } int gprs_rlcmac_rcv_rach(struct gprs_rlcmac_bts *bts, uint8_t ra, uint32_t Fn, int16_t qta) { struct gprs_rlcmac_tbf *tbf; uint8_t trx, ts = 0; int8_t tfi; /* must be signed */ uint8_t sb = 0; uint32_t sb_fn = 0; int rc; uint8_t plen; LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide " "one:\n"); if ((ra & 0xf8) == 0x70) { LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block " "allocation\n"); sb = 1; } else if (bts->force_two_phase) { LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, " "but we force two phase access\n"); sb = 1; } if (qta < 0) qta = 0; if (qta > 252) qta = 252; if (sb) { rc = bts->bts->sba()->alloc(&trx, &ts, &sb_fn, qta >> 2); if (rc < 0) return rc; LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d " "ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink " "(AGCH)\n"); } else { // Create new TBF #warning "Copy and pate with other routines.." tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx, -1); if (tfi < 0) { LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); /* FIXME: send reject */ return -EBUSY; } /* set class to 0, since we don't know the multislot class yet */ tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx, 0, 1); if (!tbf) { LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n"); /* FIXME: send reject */ return -EBUSY; } tbf->ta = qta >> 2; tbf_new_state(tbf, GPRS_RLCMAC_FLOW); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); tbf_timer_start(tbf, 3169, bts->t3169, 0); LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] START TFI: %u\n", tbf->tfi); LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u RACH " "qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n", tbf->tfi, qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26); LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u Immediate " "Assignment Uplink (AGCH)\n", tbf->tfi); } bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */; bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); if (sb) plen = Encoding::write_immediate_assignment(bts, immediate_assignment, 0, ra, Fn, qta >> 2, bts->trx[trx].arfcn, ts, bts->trx[trx].pdch[ts].tsc, 0, 0, 0, 0, sb_fn, 1, bts->alpha, bts->gamma, -1); else plen = Encoding::write_immediate_assignment(bts, immediate_assignment, 0, ra, Fn, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0, bts->alpha, bts->gamma, -1); pcu_l1if_tx_agch(immediate_assignment, plen); bitvec_free(immediate_assignment); return 0; } /* send DL data block * * The messages are fragmented and forwarded as data blocks. */ struct msgb *gprs_rlcmac_send_data_block_acknowledged( struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts) { struct rlc_dl_header *rh; struct rlc_li_field *li; uint8_t block_length; /* total length of block, including spare bits */ uint8_t block_data; /* usable data of block, w/o spare bits, inc. MAC */ struct msgb *msg, *dl_msg; uint8_t bsn; uint16_t mod_sns = tbf->sns - 1; uint16_t mod_sns_half = (tbf->sns >> 1) - 1; uint16_t index; uint8_t *delimiter, *data, *e_pointer; uint8_t len; uint16_t space, chunk; int first_fin_ack = 0; gprs_rlcmac_bts *bts = tbf->bts->bts_data(); LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. " "V(S)==%d)\n", tbf->tfi, tbf->dir.dl.v_a, tbf->dir.dl.v_s); do_resend: /* check if there is a block with negative acknowledgement */ for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; bsn = (bsn + 1) & mod_sns) { index = (bsn & mod_sns_half); if (tbf->dir.dl.v_b[index] == 'N' || tbf->dir.dl.v_b[index] == 'X') { LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", bsn); /* re-send block with negative aknowlegement */ tbf->dir.dl.v_b[index] = 'U'; /* unacked */ goto tx_block; } } /* if the window has stalled, or transfer is complete, * send an unacknowledged block */ if (tbf->state_is(GPRS_RLCMAC_FINISHED) || ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns) == tbf->ws) { int resend = 0; if (tbf->state_is(GPRS_RLCMAC_FINISHED)) LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " "because all blocks have been transmitted.\n", tbf->dir.dl.v_a); else LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " "because all window is stalled.\n", tbf->dir.dl.v_a); /* If V(S) == V(A) and finished state, we would have received * acknowledgement of all transmitted block. In this case we * would have transmitted the final block, and received ack * from MS. But in this case we did not receive the final ack * indication from MS. This should never happen if MS works * correctly. */ if (tbf->dir.dl.v_s == tbf->dir.dl.v_a) { LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " "so we re-transmit final block!\n"); /* we just send final block again */ index = ((tbf->dir.dl.v_s - 1) & mod_sns_half); goto tx_block; } /* cycle through all unacked blocks */ for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; bsn = (bsn + 1) & mod_sns) { index = (bsn & mod_sns_half); if (tbf->dir.dl.v_b[index] == 'U') { /* mark to be re-send */ tbf->dir.dl.v_b[index] = 'X'; resend++; } } /* At this point there should be at leasst one unacked block * to be resent. If not, this is an software error. */ if (resend == 0) { LOGP(DRLCMACDL, LOGL_ERROR, "Software error: " "There are no unacknowledged blocks, but V(A) " " != V(S). PLEASE FIX!\n"); /* we just send final block again */ index = ((tbf->dir.dl.v_s - 1) & mod_sns_half); goto tx_block; } goto do_resend; } LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n", tbf->dir.dl.v_s); /* now we still have untransmitted LLC data, so we fill mac block */ index = tbf->dir.dl.v_s & mod_sns_half; data = tbf->rlc_block[index]; #warning "Selection of the CS doesn't belong here" if (tbf->cs == 0) { tbf->cs = bts->initial_cs_dl; if (tbf->cs < 1 || tbf->cs > 4) tbf->cs = 1; } block_length = gprs_rlcmac_cs[tbf->cs].block_length; block_data = gprs_rlcmac_cs[tbf->cs].block_data; memset(data, 0x2b, block_data); /* spare bits will be left 0 */ rh = (struct rlc_dl_header *)data; rh->pt = 0; /* Data Block */ rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */ rh->usf = 7; /* will be set at scheduler */ rh->pr = 0; /* FIXME: power reduction */ rh->tfi = tbf->tfi; /* TFI */ rh->fbi = 0; /* Final Block Indicator, set late, if true */ rh->bsn = tbf->dir.dl.v_s; /* Block Sequence Number */ rh->e = 0; /* Extension bit, maybe set later */ e_pointer = data + 2; /* points to E of current chunk */ data += 3; delimiter = data; /* where next length header would be stored */ space = block_data - 3; while (1) { chunk = tbf->llc_length - tbf->llc_index; /* if chunk will exceed block limit */ if (chunk > space) { LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " "larger than space (%d) left in block: copy " "only remaining space, and we are done\n", chunk, space); /* block is filled, so there is no extension */ *e_pointer |= 0x01; /* fill only space */ memcpy(data, tbf->llc_frame + tbf->llc_index, space); /* incement index */ tbf->llc_index += space; /* return data block as message */ break; } /* if FINAL chunk would fit precisely in space left */ if (chunk == space && llist_empty(&tbf->llc_queue)) { LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " "would exactly fit into space (%d): because " "this is a final block, we don't add length " "header, and we are done\n", chunk, space); LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " "TBF=%d that fits precisely in last block: " "len=%d\n", tbf->tfi, tbf->llc_length); gprs_rlcmac_dl_bw(tbf, tbf->llc_length); /* block is filled, so there is no extension */ *e_pointer |= 0x01; /* fill space */ memcpy(data, tbf->llc_frame + tbf->llc_index, space); /* reset LLC frame */ tbf->llc_index = tbf->llc_length = 0; /* final block */ rh->fbi = 1; /* we indicate final block */ tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); /* return data block as message */ break; } /* if chunk would fit exactly in space left */ if (chunk == space) { LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " "would exactly fit into space (%d): add length " "header with LI=0, to make frame extend to " "next block, and we are done\n", chunk, space); /* make space for delimiter */ if (delimiter != data) memcpy(delimiter + 1, delimiter, data - delimiter); data++; space--; /* add LI with 0 length */ li = (struct rlc_li_field *)delimiter; li->e = 1; /* not more extension */ li->m = 0; /* shall be set to 0, in case of li = 0 */ li->li = 0; /* chunk fills the complete space */ // no need to set e_pointer nor increase delimiter /* fill only space, which is 1 octet less than chunk */ memcpy(data, tbf->llc_frame + tbf->llc_index, space); /* incement index */ tbf->llc_index += space; /* return data block as message */ break; } LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " "than remaining space (%d): add length header to " "to delimit LLC frame\n", chunk, space); /* the LLC frame chunk ends in this block */ /* make space for delimiter */ if (delimiter != data) memcpy(delimiter + 1, delimiter, data - delimiter); data++; space--; /* add LI to delimit frame */ li = (struct rlc_li_field *)delimiter; li->e = 0; /* Extension bit, maybe set later */ li->m = 0; /* will be set later, if there is more LLC data */ li->li = chunk; /* length of chunk */ e_pointer = delimiter; /* points to E of current delimiter */ delimiter++; /* copy (rest of) LLC frame to space */ memcpy(data, tbf->llc_frame + tbf->llc_index, chunk); data += chunk; space -= chunk; LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: " "len=%d\n", tbf->tfi, tbf->llc_length); gprs_rlcmac_dl_bw(tbf, tbf->llc_length); /* reset LLC frame */ tbf->llc_index = tbf->llc_length = 0; /* dequeue next LLC frame, if any */ msg = tbf->llc_dequeue(gprs_bssgp_pcu_current_bctx()); if (msg) { LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for " "TBF=%d (len=%d)\n", tbf->tfi, msg->len); tbf->update_llc_frame(msg); msgb_free(msg); } /* if we have more data and we have space left */ if (space > 0 && tbf->llc_length) { li->m = 1; /* we indicate more frames to follow */ continue; } /* if we don't have more LLC frames */ if (!tbf->llc_length) { LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " "done.\n"); li->e = 1; /* we cannot extend */ rh->fbi = 1; /* we indicate final block */ first_fin_ack = 1; /* + 1 indicates: first final ack */ tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); break; } /* we have no space left */ LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " "done.\n"); li->e = 1; /* we cannot extend */ break; } LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n", osmo_hexdump(tbf->rlc_block[index], block_length)); tbf->rlc_block_len[index] = block_length; /* raise send state and set ack state array */ tbf->dir.dl.v_b[index] = 'U'; /* unacked */ tbf->dir.dl.v_s = (tbf->dir.dl.v_s + 1) & mod_sns; /* inc send state */ tx_block: /* from this point on, new block is sent or old block is resent */ /* get data and header from current block */ data = tbf->rlc_block[index]; len = tbf->rlc_block_len[index]; rh = (struct rlc_dl_header *)data; /* Clear Polling, if still set in history buffer */ rh->s_p = 0; /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx. */ if (tbf->dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) { if (first_fin_ack) { LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " "polling, because first final block sent.\n"); } else { LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack " "polling, because %d blocks sent.\n", POLL_ACK_AFTER_FRAMES); } /* scheduling not possible, because: */ if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already " "sheduled for TBF=%d, so we must wait for " "requesting downlink ack\n", tbf->tfi); else if (tbf->control_ts != ts) LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be " "sheduled in this TS %d, waiting for " "TS %d\n", ts, tbf->control_ts); else if (tbf->bts->sba()->find(tbf->trx_no, ts, (fn + 13) % 2715648)) LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be " "sheduled, because single block alllocation " "already exists\n"); else { LOGP(DRLCMAC, LOGL_DEBUG, "Polling sheduled in this " "TS %d\n", ts); tbf->dir.dl.tx_counter = 0; /* start timer whenever we send the final block */ if (rh->fbi == 1) tbf_timer_start(tbf, 3191, bts->t3191, 0); /* schedule polling */ tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; tbf->poll_fn = (fn + 13) % 2715648; #ifdef DEBUG_DIAGRAM debug_diagram(bts->bts, tbf->diag, "poll DL-ACK"); if (first_fin_ack) debug_diagram(bts->bts, tbf->diag, "(is first FINAL)"); if (rh->fbi) debug_diagram(bts->bts, tbf->diag, "(FBI is set)"); #endif /* set polling in header */ rh->rrbp = 0; /* N+13 */ rh->s_p = 1; /* Polling */ /* Increment TX-counter */ tbf->dir.dl.tx_counter++; } } else { /* Increment TX-counter */ tbf->dir.dl.tx_counter++; } /* return data block as message */ dl_msg = msgb_alloc(len, "rlcmac_dl_data"); if (!dl_msg) return NULL; memcpy(msgb_put(dl_msg, len), data, len); return dl_msg; } int gprs_rlcmac_downlink_ack( struct gprs_rlcmac_tbf *tbf, uint8_t final, uint8_t ssn, uint8_t *rbb) { char show_rbb[65], show_v_b[RLC_MAX_SNS + 1]; uint16_t mod_sns = tbf->sns - 1; uint16_t mod_sns_half = (tbf->sns >> 1) - 1; int i; /* must be signed */ int16_t dist; /* must be signed */ uint8_t bit; uint16_t bsn; struct msgb *msg; uint16_t lost = 0, received = 0; LOGP(DRLCMACDL, LOGL_DEBUG, "TBF=%d downlink acknowledge\n", tbf->tfi); if (!final) { /* show received array in debug (bit 64..1) */ for (i = 63; i >= 0; i--) { bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; show_rbb[i] = (bit) ? '1' : 'o'; } show_rbb[64] = '\0'; LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" "(BSN=%d) 1=ACK o=NACK\n", (ssn - 64) & mod_sns, show_rbb, (ssn - 1) & mod_sns); /* apply received array to receive state (SSN-64..SSN-1) */ /* calculate distance of ssn from V(S) */ dist = (tbf->dir.dl.v_s - ssn) & mod_sns; /* check if distance is less than distance V(A)..V(S) */ if (dist >= ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns)) { /* this might happpen, if the downlink assignment * was not received by ms and the ack refers * to previous TBF * FIXME: we should implement polling for * control ack!*/ LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " "V(A)..V(S) range (DL TBF=%d) Free TFB!\n", tbf->tfi); return 1; /* indicate to free TBF */ } /* SSN - 1 is in range V(A)..V(S)-1 */ for (i = 63, bsn = (ssn - 1) & mod_sns; i >= 0 && bsn != ((tbf->dir.dl.v_a - 1) & mod_sns); i--, bsn = (bsn - 1) & mod_sns) { bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; if (bit) { LOGP(DRLCMACDL, LOGL_DEBUG, "- got " "ack for BSN=%d\n", bsn); if (tbf->dir.dl.v_b[bsn & mod_sns_half] != 'A') received++; tbf->dir.dl.v_b[bsn & mod_sns_half] = 'A'; } else { LOGP(DRLCMACDL, LOGL_DEBUG, "- got " "NACK for BSN=%d\n", bsn); tbf->dir.dl.v_b[bsn & mod_sns_half] = 'N'; lost++; } } /* report lost and received packets */ gprs_rlcmac_received_lost(tbf, received, lost); /* raise V(A), if possible */ for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; i++, bsn = (bsn + 1) & mod_sns) { if (tbf->dir.dl.v_b[bsn & mod_sns_half] == 'A') { tbf->dir.dl.v_b[bsn & mod_sns_half] = 'I'; /* mark invalid */ tbf->dir.dl.v_a = (tbf->dir.dl.v_a + 1) & mod_sns; } else break; } /* show receive state array in debug (V(A)..V(S)-1) */ for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; i++, bsn = (bsn + 1) & mod_sns) { show_v_b[i] = tbf->dir.dl.v_b[bsn & mod_sns_half]; if (show_v_b[i] == 0) show_v_b[i] = ' '; } show_v_b[i] = '\0'; LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " "X=Resend-Unacked\n", tbf->dir.dl.v_a, show_v_b, (tbf->dir.dl.v_s - 1) & mod_sns); if (tbf->state_is(GPRS_RLCMAC_FINISHED) && tbf->dir.dl.v_s == tbf->dir.dl.v_a) { LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " "all blocks, but without final ack " "inidcation (don't worry)\n"); } return 0; } LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); debug_diagram(tbf->bts, tbf->diag, "got Final ACK"); /* range V(A)..V(S)-1 */ for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; bsn = (bsn + 1) & mod_sns) { if (tbf->dir.dl.v_b[bsn & mod_sns_half] != 'A') received++; } /* report all outstanding packets as received */ gprs_rlcmac_received_lost(tbf, received, lost); /* check for LLC PDU in the LLC Queue */ msg = tbf->llc_dequeue(gprs_bssgp_pcu_current_bctx()); if (!msg) { /* no message, start T3193, change state to RELEASE */ LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we " "release.\n"); /* start T3193 */ debug_diagram(bts->bts, tbf->diag, "start T3193"); tbf_timer_start(tbf, 3193, tbf->bts->bts_data()->t3193_msec / 1000, (tbf->bts->bts_data()->t3193_msec % 1000) * 1000); tbf_new_state(tbf, GPRS_RLCMAC_WAIT_RELEASE); return 0; } #warning "Copy and paste on the sender path" tbf->update_llc_frame(msg); msgb_free(msg); /* we have a message, so we trigger downlink assignment, and there * set the state to ASSIGN. also we set old_downlink, because we * re-use this tbf. */ 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 &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep TO flags */ tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); tbf->update(); gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL); return 0; } struct msgb *gprs_rlcmac_send_packet_downlink_assignment( struct gprs_rlcmac_tbf *tbf, uint32_t fn) { struct msgb *msg; struct gprs_rlcmac_tbf *new_tbf; gprs_rlcmac_bts *bts = tbf->bts->bts_data(); int poll_ass_dl = POLLING_ASSIGNMENT_DL; if (poll_ass_dl && tbf->direction == GPRS_RLCMAC_DL_TBF && tbf->control_ts != tbf->first_common_ts) { LOGP(DRLCMAC, LOGL_NOTICE, "Cannot poll for downlink " "assigment, because MS cannot reply. (control TS=%d, " "first common TS=%d)\n", tbf->control_ts, tbf->first_common_ts); poll_ass_dl = 0; } if (poll_ass_dl) { if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) { LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already sheduled " "for TBF=%d, so we must wait for downlink " "assignment...\n", tbf->tfi); return NULL; } if (tbf->bts->sba()->find(tbf->trx_no, tbf->control_ts, (fn + 13) % 2715648)) { LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already " "scheduled for single block allocation...\n"); return NULL; } } /* on uplink TBF we get the downlink TBF to be assigned. */ if (tbf->direction == GPRS_RLCMAC_UL_TBF) { /* be sure to check first, if contention resolution is done, * otherwise we cannot send the assignment yet */ if (!tbf->dir.ul.contention_resolution_done) { LOGP(DRLCMAC, LOGL_DEBUG, "Cannot assign DL TBF now, " "because contention resolution is not " "finished.\n"); return NULL; } new_tbf = tbf->bts->tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF); } else new_tbf = tbf; if (!new_tbf) { LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink " "assignment at uplink TBF=%d, but there is no downlink " "TBF\n", tbf->tfi); tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; return NULL; } msg = msgb_alloc(23, "rlcmac_dl_ass"); if (!msg) return NULL; bitvec *ass_vec = bitvec_alloc(23); if (!ass_vec) { msgb_free(msg); return NULL; } bitvec_unhex(ass_vec, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Downlink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli); RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t); Encoding::write_packet_downlink_assignment(mac_control_block, tbf->tfi, (tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf, poll_ass_dl, bts->alpha, bts->gamma, -1, 0); LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n"); encode_gsm_rlcmac_downlink(ass_vec, mac_control_block); LOGPC(DCSN1, LOGL_NOTICE, "\n"); LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n"); bitvec_pack(ass_vec, msgb_put(msg, 23)); bitvec_free(ass_vec); talloc_free(mac_control_block); if (poll_ass_dl) { tbf->poll_state = GPRS_RLCMAC_POLL_SCHED; tbf->poll_fn = (fn + 13) % 2715648; tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK; } else { tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE; tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW); tbf_assign_control_ts(new_tbf); /* stop pending assignment timer */ new_tbf->stop_timer(); } debug_diagram(bts->bts, tbf->diag, "send DL-ASS"); return msg; } static void gprs_rlcmac_downlink_assignment( gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi) { gprs_rlcmac_bts *bts = tbf->bts->bts_data(); int plen; debug_diagram(bts->bts, tbf->diag, "IMM.ASS (PCH)"); LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u TLLI: 0x%08x Immediate Assignment Downlink (PCH)\n", tbf->tfi, tbf->tlli); bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */ bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"); /* use request reference that has maximum distance to current time, * so the assignment will not conflict with possible RACH requests. */ plen = Encoding::write_immediate_assignment(bts, immediate_assignment, 1, 125, (tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll, tbf->poll_fn, 0, bts->alpha, bts->gamma, -1); pcu_l1if_tx_pch(immediate_assignment, plen, imsi); bitvec_free(immediate_assignment); } /* depending on the current TBF, we assign on PACCH or AGCH */ void gprs_rlcmac_trigger_downlink_assignment( struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_tbf *old_tbf, const char *imsi) { #ifdef DEBUG_DL_ASS_IDLE strncpy(debug_imsi, imsi); LOGP(DRLCMAC, LOGL_ERROR, "**** DEBUGGING DOWNLINK ASSIGNMENT ****\n"); #endif /* stop pending timer */ tbf->stop_timer(); /* check for downlink tbf: */ if (old_tbf) { #ifdef DEBUG_DL_ASS_IDLE LOGP(DRLCMAC, LOGL_ERROR, "We must wait for current TBF to be " "released.\n"); /* wait one second until assignment */ tbf_timer_start(tbf, 1234, 1,0); #else LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on " "PACCH, because %s TBF=%d exists for TLLI=0x%08x\n", (old_tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", old_tbf->tfi, old_tbf->tlli); old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS; /* use TA from old TBF */ tbf->ta = old_tbf->ta; /* change state */ tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); /* start timer */ tbf_timer_start(tbf, 0, Tassign_pacch); #endif } else { LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for TBF=%d on PCH, no TBF exist (IMSI=%s)\n", tbf->tfi, imsi); if (!imsi || strlen(imsi) < 3) { LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n"); return; } /* 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); tbf->dir.dl.wait_confirm = 1; } } int gprs_rlcmac_imm_ass_cnf(BTS *bts, 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 = bts->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; }