| /* |
| * 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 <string.h> |
| |
| extern void *tall_pcu_ctx; |
| |
| 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); |
| } |
| |
| 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; |
| } |
| |