Pau Espin Pedrol | deed90d | 2021-05-26 13:17:01 +0200 | [diff] [blame^] | 1 | /* ms_anr_fsm.c |
| 2 | * |
| 3 | * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
| 4 | * Author: Pau Espin Pedrol <pespin@sysmocom.de> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License |
| 8 | * as published by the Free Software Foundation; either version 2 |
| 9 | * of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 19 | */ |
| 20 | |
| 21 | #include <unistd.h> |
| 22 | #include <talloc.h> |
| 23 | |
| 24 | #include <osmocom/core/rate_ctr.h> |
| 25 | #include <osmocom/ctrl/control_cmd.h> |
| 26 | #include <osmocom/ctrl/control_if.h> |
| 27 | |
| 28 | #include <osmocom/gsm/gsm48.h> |
| 29 | #include <osmocom/gprs/gprs_bssgp.h> |
| 30 | #include <osmocom/gprs/gprs_bssgp_rim.h> |
| 31 | |
| 32 | #include <ms_anr_fsm.h> |
| 33 | #include <gprs_rlcmac.h> |
| 34 | #include <gprs_debug.h> |
| 35 | #include <gprs_ms.h> |
| 36 | #include <encoding.h> |
| 37 | #include <bts.h> |
| 38 | |
| 39 | #define X(s) (1 << (s)) |
| 40 | |
| 41 | /* We add safety timer to any FSM since ending up into some unexpected scenario |
| 42 | * can keep the FSM alive and hence the TBF kept open forever */ |
| 43 | static const struct osmo_tdef_state_timeout ms_anr_fsm_timeouts[32] = { |
| 44 | [MS_ANR_ST_INITIAL] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 45 | [MS_ANR_ST_TX_PKT_MEAS_RESET1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 46 | [MS_ANR_ST_WAIT_CTRL_ACK1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 47 | [MS_ANR_ST_TX_PKT_MEAS_ORDER] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 48 | [MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 49 | [MS_ANR_ST_TX_PKT_MEAS_RESET2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 50 | [MS_ANR_ST_WAIT_CTRL_ACK2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 51 | [MS_ANR_ST_DONE] = { .T = PCU_TDEF_ANR_MS_TIMEOUT }, |
| 52 | }; |
| 53 | |
| 54 | /* Transition to a state, using the T timer defined in assignment_fsm_timeouts. |
| 55 | * The actual timeout value is in turn obtained from conn->T_defs. |
| 56 | * Assumes local variable fi exists. */ |
| 57 | #define ms_anr_fsm_state_chg(fi, NEXT_STATE) \ |
| 58 | osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \ |
| 59 | ms_anr_fsm_timeouts, \ |
| 60 | ((struct ms_anr_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \ |
| 61 | -1) |
| 62 | |
| 63 | const struct value_string ms_anr_fsm_event_names[] = { |
| 64 | { MS_ANR_EV_START, "START" }, |
| 65 | { MS_ANR_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" }, |
| 66 | { MS_ANR_EV_RX_PKT_MEAS_REPORT, "RX_PKT_MEAS_REPORT" }, |
| 67 | { MS_ANR_EV_RX_PKT_CTRL_ACK_MSG, "RX_PKT_CTRL_ACK_MSG" }, |
| 68 | { MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT, "RX_PKT_CTRL_ACK_TIMEOUT" }, |
| 69 | { 0, NULL } |
| 70 | }; |
| 71 | |
| 72 | /* TS 44 060 11.2.9b Packet Measurement Order */ |
| 73 | static struct msgb *create_packet_meas_order(struct ms_anr_fsm_ctx *ctx, |
| 74 | const struct gprs_rlcmac_tbf *tbf, |
| 75 | uint8_t nco, uint8_t pmo_idx, uint8_t pmo_count, |
| 76 | const NC_Frequency_list_t *freq_li) |
| 77 | { |
| 78 | struct msgb *msg; |
| 79 | int rc; |
| 80 | RlcMacDownlink_t *mac_control_block; |
| 81 | struct GprsMs *ms = tbf_ms(tbf); |
| 82 | bool tfi_asigned, tfi_is_dl; |
| 83 | uint8_t tfi; |
| 84 | bool exist_nc; |
| 85 | uint8_t non_drx_period, nc_report_period_i, nc_report_period_t; |
| 86 | |
| 87 | if (tbf_is_tfi_assigned(tbf)) { |
| 88 | tfi_asigned = true; |
| 89 | tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF; |
| 90 | tfi = tbf_tfi(tbf); |
| 91 | } else { |
| 92 | tfi_asigned = false; |
| 93 | tfi_is_dl = false; |
| 94 | tfi = 0; |
| 95 | } |
| 96 | |
| 97 | |
| 98 | msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order"); |
| 99 | if (!msg) |
| 100 | return NULL; |
| 101 | |
| 102 | /* Initialize a bit vector that uses allocated msgb as the data buffer. */ |
| 103 | struct bitvec bv = { |
| 104 | .data = msgb_put(msg, GSM_MACBLOCK_LEN), |
| 105 | .data_len = GSM_MACBLOCK_LEN, |
| 106 | }; |
| 107 | bitvec_unhex(&bv, DUMMY_VEC); |
| 108 | |
| 109 | mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t); |
| 110 | |
| 111 | /* First message, set NC Meas Params. As per spec: |
| 112 | * "If parameters for the NC measurements are not included, a previous |
| 113 | * Packet Measurement Order message belonging to the same set of messages |
| 114 | * shall still be valid." */ |
| 115 | exist_nc = pmo_idx == 0; |
| 116 | non_drx_period = 2; /* default value, still need to check */ |
| 117 | nc_report_period_i = 5;//0; |
| 118 | nc_report_period_t = 5;//0; |
| 119 | |
| 120 | |
| 121 | write_packet_measurement_order(mac_control_block, 0, 0, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms), |
| 122 | pmo_idx, pmo_count, nco, exist_nc, non_drx_period, |
| 123 | nc_report_period_i, nc_report_period_t, freq_li); |
| 124 | LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order +++++++++++++++++++++++++\n"); |
| 125 | rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block); |
| 126 | if (rc < 0) { |
| 127 | LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc); |
| 128 | goto free_ret; |
| 129 | } |
| 130 | LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order -------------------------\n"); |
| 131 | rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]); |
| 132 | talloc_free(mac_control_block); |
| 133 | |
| 134 | return msg; |
| 135 | |
| 136 | free_ret: |
| 137 | talloc_free(mac_control_block); |
| 138 | msgb_free(msg); |
| 139 | return NULL; |
| 140 | } |
| 141 | |
| 142 | #define MAX_REMOVE_FREQ_PER_MSG 16 |
| 143 | #define MAX_ADD_FREQ_PER_MSG 5 |
| 144 | static void build_nc_freq_list(struct ms_anr_fsm_ctx *ctx, NC_Frequency_list_t *freq_li, |
| 145 | const uint16_t *freq_to_remove, unsigned *freq_to_remove_idx, unsigned freq_to_remove_cnt, |
| 146 | const struct arfcn_bsic *freq_to_add, unsigned *freq_to_add_idx, unsigned freq_to_add_cnt) |
| 147 | { |
| 148 | int i; |
| 149 | unsigned to_remove_this_message; |
| 150 | LOGP(DANR, LOGL_DEBUG, "Build NC Frequency List:\n"); |
| 151 | |
| 152 | /* First, remove all ARFCNs from BS(GPRS): */ |
| 153 | if (*freq_to_remove_idx < freq_to_remove_cnt) { |
| 154 | to_remove_this_message = OSMO_MIN(freq_to_remove_cnt - *freq_to_remove_idx, MAX_REMOVE_FREQ_PER_MSG); |
| 155 | freq_li->Exist_REMOVED_FREQ = 1; |
| 156 | freq_li->NR_OF_REMOVED_FREQ = to_remove_this_message; /* offset of 1 applied already by CSN1 encoder */ |
| 157 | for (i = 0; i < to_remove_this_message; i++) { |
| 158 | LOGP(DANR, LOGL_DEBUG, "Remove_Frequency[%d] INDEX=%u\n", i, freq_to_remove[*freq_to_remove_idx]); |
| 159 | freq_li->Removed_Freq_Index[i].REMOVED_FREQ_INDEX = freq_to_remove[(*freq_to_remove_idx)++]; |
| 160 | } |
| 161 | /* We want in general to first remove all frequencies, and only once we |
| 162 | * are done removing, starting adding new ones */ |
| 163 | if (*freq_to_remove_idx < freq_to_remove_cnt) { |
| 164 | freq_li->Count_Add_Frequency = 0; |
| 165 | return; |
| 166 | } |
| 167 | } else { |
| 168 | to_remove_this_message = 0; |
| 169 | freq_li->Exist_REMOVED_FREQ = 0; |
| 170 | } |
| 171 | |
| 172 | /* Then, add selected ones for ANR. ctx->cell_list has ARFCNs stored in ascending order */ |
| 173 | freq_li->Count_Add_Frequency = OSMO_MIN(freq_to_add_cnt - *freq_to_add_idx, |
| 174 | MAX_ADD_FREQ_PER_MSG - to_remove_this_message/4); |
| 175 | for (i = 0; i < freq_li->Count_Add_Frequency; i++) { |
| 176 | freq_li->Add_Frequency[i].START_FREQUENCY = freq_to_add[*freq_to_add_idx].arfcn; |
| 177 | freq_li->Add_Frequency[i].BSIC = freq_to_add[*freq_to_add_idx].bsic; |
| 178 | freq_li->Add_Frequency[i].Exist_Cell_Selection = 0; |
| 179 | freq_li->Add_Frequency[i].NR_OF_FREQUENCIES = 0; /* TODO: optimize here checking if we can fit more with DIFF */ |
| 180 | freq_li->Add_Frequency[i].FREQ_DIFF_LENGTH = 0; |
| 181 | LOGP(DANR, LOGL_DEBUG, "Add_Frequency[%d] START_FREQ=%u BSIC=%u\n", i, |
| 182 | freq_li->Add_Frequency[i].START_FREQUENCY, |
| 183 | freq_li->Add_Frequency[i].BSIC); |
| 184 | (*freq_to_add_idx)++; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | static int build_multipart_packet_meas_order(struct ms_anr_fsm_ctx *ctx) |
| 189 | { |
| 190 | struct gprs_rlcmac_bts *bts = ctx->ms->bts; |
| 191 | unsigned int i, j; |
| 192 | /* TODO: decide early whether to use si2 or si5, and pick is related BA-IND */ |
| 193 | struct gsm_sysinfo_freq *bcch_freq_list = bts->si2_bcch_cell_list; |
| 194 | unsigned int bcch_freq_list_len = ARRAY_SIZE(bts->si2_bcch_cell_list); |
| 195 | unsigned int bcch_freq_list_cnt = 0; // Number of freqs in Neigh List */ |
| 196 | |
| 197 | unsigned int freq_to_remove_cnt = 0, freq_to_add_cnt = 0; |
| 198 | uint16_t freq_to_remove[1024]; /* freq list index */ |
| 199 | struct arfcn_bsic freq_to_add[1024]; |
| 200 | |
| 201 | /* First calculate amount of REMOVE and ADD freq entries, to calculate |
| 202 | * required number of bits and hence number of RLCMAC messages */ |
| 203 | ctx->nc_measurement_list_len = 0; |
| 204 | for (i = 0; i < bcch_freq_list_len; i++) { |
| 205 | bool bcch_freq_marked = !!bcch_freq_list[i].mask; |
| 206 | |
| 207 | if (bcch_freq_marked) { |
| 208 | /* Freqs from BCCH list occupy one slot in the Neighbour |
| 209 | * List, even if removed later by NC_FreqList in Pkt |
| 210 | * Meas Order */ |
| 211 | if (ctx->nc_measurement_list_len < ARRAY_SIZE(ctx->nc_measurement_list)) { |
| 212 | ctx->nc_measurement_list[ctx->nc_measurement_list_len] = i; |
| 213 | ctx->nc_measurement_list_len++; |
| 214 | } |
| 215 | |
| 216 | /* Check if the ARFCN is in our target ANR subset, |
| 217 | * otherwise mark it from removal using Pkt Meas Order */ |
| 218 | bcch_freq_list_cnt++; |
| 219 | bool found = false; |
| 220 | for (j = 0; j < ctx->num_cells; j++) { |
| 221 | /* early termination, arfcns are in ascending order */ |
| 222 | if (ctx->cell_list[j].arfcn > i) |
| 223 | break; |
| 224 | if (ctx->cell_list[j].arfcn == i) { |
| 225 | found = true; |
| 226 | break; |
| 227 | } |
| 228 | } |
| 229 | if (!found) { |
| 230 | freq_to_remove[freq_to_remove_cnt] = bcch_freq_list_cnt - 1; |
| 231 | freq_to_remove_cnt++; |
| 232 | } |
| 233 | } else { |
| 234 | for (j = 0; j < ctx->num_cells; j++) { |
| 235 | /* early termination, arfcns are in ascending order */ |
| 236 | if (ctx->cell_list[j].arfcn > i) |
| 237 | break; |
| 238 | if (ctx->cell_list[j].arfcn == i) { |
| 239 | freq_to_add[freq_to_add_cnt] = ctx->cell_list[j]; |
| 240 | freq_to_add_cnt++; |
| 241 | /* Don't break here, there may be several ARFCN=N with different BSIC */ |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | LOGPFSML(ctx->fi, LOGL_DEBUG, "NC_freq_list to_remove=%u to_add=%u\n", freq_to_remove_cnt, freq_to_add_cnt); |
| 248 | |
| 249 | /* Added frequency through Pkt Meas Order NC Freq list are indexed after existing arfcns from BA(GPRS) */ |
| 250 | for (i = 0; i < freq_to_add_cnt; i++) { |
| 251 | if (ctx->nc_measurement_list_len >= ARRAY_SIZE(ctx->nc_measurement_list)) |
| 252 | break; |
| 253 | ctx->nc_measurement_list[ctx->nc_measurement_list_len] = freq_to_add[i].arfcn; |
| 254 | ctx->nc_measurement_list_len++; |
| 255 | } |
| 256 | |
| 257 | uint8_t pmo_index; |
| 258 | uint8_t pmo_count = 0; |
| 259 | unsigned int freq_to_remove_idx = 0, freq_to_add_idx = 0; |
| 260 | NC_Frequency_list_t freq_li[8]; |
| 261 | do { |
| 262 | OSMO_ASSERT(pmo_count < ARRAY_SIZE(freq_li)); /* TODO: print something here*/ |
| 263 | build_nc_freq_list(ctx, &freq_li[pmo_count], |
| 264 | freq_to_remove, &freq_to_remove_idx, freq_to_remove_cnt, |
| 265 | freq_to_add, &freq_to_add_idx, freq_to_add_cnt); |
| 266 | pmo_count++; |
| 267 | } while (freq_to_remove_idx < freq_to_remove_cnt || freq_to_add_idx < freq_to_add_cnt); |
| 268 | |
| 269 | /* Now build messages */ |
| 270 | for (pmo_index = 0; pmo_index < pmo_count; pmo_index++) { |
| 271 | struct msgb *msg = create_packet_meas_order(ctx, ctx->tbf, NC_1, pmo_index, pmo_count - 1, &freq_li[pmo_index]); |
| 272 | llist_add_tail(&msg->list, &ctx->meas_order_queue); |
| 273 | } |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | /* TS 44 060 11.2.9b Packet Measurement Order */ |
| 278 | static struct msgb *create_packet_meas_order_reset(struct ms_anr_fsm_ctx *ctx, |
| 279 | struct ms_anr_ev_create_rlcmac_msg_ctx *data, |
| 280 | uint32_t *new_poll_fn) |
| 281 | { |
| 282 | struct msgb *msg; |
| 283 | int rc; |
| 284 | RlcMacDownlink_t *mac_control_block; |
| 285 | struct gprs_rlcmac_tbf *tbf = ctx->tbf; |
| 286 | struct GprsMs *ms = tbf_ms(tbf); |
| 287 | bool tfi_asigned, tfi_is_dl; |
| 288 | uint8_t tfi; |
| 289 | uint8_t pmo_idx = 0, pmo_count = 0; |
| 290 | uint8_t nco = NC_RESET; |
| 291 | unsigned int rrbp; |
| 292 | |
| 293 | if (tbf_is_tfi_assigned(tbf)) { |
| 294 | tfi_asigned = true; |
| 295 | tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF; |
| 296 | tfi = tbf_tfi(tbf); |
| 297 | } else { |
| 298 | tfi_asigned = false; |
| 299 | tfi_is_dl = false; |
| 300 | tfi = 0; |
| 301 | } |
| 302 | |
| 303 | rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp); |
| 304 | if (rc < 0) { |
| 305 | LOGP(DANR, LOGL_ERROR, "Failed registering poll for Packet Measurement Order (reset) (%d)\n", rc); |
| 306 | return NULL; |
| 307 | } |
| 308 | |
| 309 | msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order"); |
| 310 | if (!msg) |
| 311 | return NULL; |
| 312 | |
| 313 | /* Initialize a bit vector that uses allocated msgb as the data buffer. */ |
| 314 | struct bitvec bv = { |
| 315 | .data = msgb_put(msg, GSM_MACBLOCK_LEN), |
| 316 | .data_len = GSM_MACBLOCK_LEN, |
| 317 | }; |
| 318 | bitvec_unhex(&bv, DUMMY_VEC); |
| 319 | |
| 320 | mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t); |
| 321 | |
| 322 | write_packet_measurement_order(mac_control_block, 1, rrbp, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms), |
| 323 | pmo_idx, pmo_count, nco, false, 0, 0, 0, NULL); |
| 324 | LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order (reset) FN=%" PRIu32 " +++++++++++++++++++++++++\n", data->fn); |
| 325 | rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block); |
| 326 | if (rc < 0) { |
| 327 | LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc); |
| 328 | goto free_ret; |
| 329 | } |
| 330 | LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order (reset) POLL_FN=%" PRIu32 "-------------------------\n", *new_poll_fn); |
| 331 | rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]); |
| 332 | talloc_free(mac_control_block); |
| 333 | |
| 334 | tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_MEAS_ORDER); |
| 335 | return msg; |
| 336 | |
| 337 | free_ret: |
| 338 | talloc_free(mac_control_block); |
| 339 | msgb_free(msg); |
| 340 | return NULL; |
| 341 | } |
| 342 | |
| 343 | static void handle_meas_report(struct ms_anr_fsm_ctx *ctx, uint8_t *meas, const Packet_Measurement_Report_t *pmr) |
| 344 | { |
| 345 | //TODO: transmit meas back to BSC |
| 346 | const NC_Measurement_Report_t *ncr; |
| 347 | int i, j; |
| 348 | memset(meas, 0xff, ctx->num_cells); |
| 349 | |
| 350 | switch (pmr->UnionType) { |
| 351 | case 0: break; |
| 352 | case 1: /* EXT Reporting, should not happen */ |
| 353 | default: |
| 354 | LOGPFSML(ctx->fi, LOGL_NOTICE, "EXT Reporting not supported!\n"); |
| 355 | osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_ERROR, NULL); |
| 356 | return; |
| 357 | } |
| 358 | |
| 359 | ncr = &pmr->u.NC_Measurement_Report; |
| 360 | LOGPFSML(ctx->fi, LOGL_NOTICE, "Rx MEAS REPORT %u neighbours\n", ncr->NUMBER_OF_NC_MEASUREMENTS); |
| 361 | |
| 362 | for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) { |
| 363 | const NC_Measurements_t *nc = &ncr->NC_Measurements[i]; |
| 364 | /* infer ARFCN from FREQUENCY_N using previously calculated data: */ |
| 365 | OSMO_ASSERT(nc->FREQUENCY_N < ARRAY_SIZE(ctx->nc_measurement_list)); |
| 366 | uint16_t arfcn = ctx->nc_measurement_list[nc->FREQUENCY_N]; |
| 367 | LOGPFSML(ctx->fi, LOGL_DEBUG, "Neigh arfcn_index=%u arfcn=%u bsic=%d %d dBm\n", |
| 368 | nc->FREQUENCY_N, arfcn, nc->Exist_BSIC_N ? nc->BSIC_N : -1, nc->RXLEV_N - 110); |
| 369 | if (!nc->Exist_BSIC_N) |
| 370 | continue; /* Skip measurement without BSIC, since there could be several cells with same ARFCN */ |
| 371 | for (j = 0; j < ctx->num_cells; j++) { |
| 372 | if (ctx->cell_list[j].arfcn != arfcn || ctx->cell_list[j].bsic != nc->BSIC_N) |
| 373 | continue; |
| 374 | meas[j] = nc->RXLEV_N; |
| 375 | break; |
| 376 | } |
| 377 | if (j == ctx->num_cells) { |
| 378 | LOGPFSML(ctx->fi, LOGL_NOTICE, |
| 379 | "Neigh arfcn_index=%u arfcn=%u bsic=%u %d dBm not found in target cell list!\n", |
| 380 | nc->FREQUENCY_N, arfcn, nc->BSIC_N, nc->RXLEV_N - 110); |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | //////////////// |
| 386 | // FSM states // |
| 387 | //////////////// |
| 388 | |
| 389 | static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 390 | { |
| 391 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 392 | //struct gprs_rlcmac_bts *bts = ctx->ms->bts; |
| 393 | const struct ms_anr_ev_start *start_data; |
| 394 | |
| 395 | switch (event) { |
| 396 | case MS_ANR_EV_START: |
| 397 | start_data = (const struct ms_anr_ev_start *)data; |
| 398 | /* Copy over cell list on which to ask for measurements */ |
| 399 | OSMO_ASSERT(start_data->tbf); |
| 400 | ctx->tbf = start_data->tbf; |
| 401 | OSMO_ASSERT(start_data->num_cells <= ARRAY_SIZE(ctx->cell_list)); |
| 402 | ctx->num_cells = start_data->num_cells; |
| 403 | if (start_data->num_cells) |
| 404 | memcpy(ctx->cell_list, start_data->cell_list, start_data->num_cells * sizeof(start_data->cell_list[0])); |
| 405 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1); |
| 406 | break; |
| 407 | default: |
| 408 | OSMO_ASSERT(0); |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | static void st_tx_pkt_meas_reset1(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 413 | { |
| 414 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 415 | struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx; |
| 416 | |
| 417 | switch (event) { |
| 418 | case MS_ANR_EV_CREATE_RLCMAC_MSG: |
| 419 | /* Set NC to RESET and drop NC_Freq_list for MS to go back to |
| 420 | network defaults. */ |
| 421 | data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data; |
| 422 | data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn); |
| 423 | if (!data_ctx->msg) { |
| 424 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); |
| 425 | return; |
| 426 | } |
| 427 | ctx->poll_ts = data_ctx->ts; |
| 428 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK1); |
| 429 | break; |
| 430 | case MS_ANR_EV_RX_PKT_MEAS_REPORT: |
| 431 | /* Ignore Meas Report from previously (potentially unfinished) prcoedures */ |
| 432 | break; |
| 433 | default: |
| 434 | OSMO_ASSERT(0); |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | static void st_wait_ctrl_ack1(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 439 | { |
| 440 | //struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 441 | |
| 442 | switch (event) { |
| 443 | case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG: |
| 444 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_ORDER); |
| 445 | break; |
| 446 | case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT: |
| 447 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1); |
| 448 | break; |
| 449 | case MS_ANR_EV_RX_PKT_MEAS_REPORT: |
| 450 | /* Ignore Meas Report from previously (potentially unfinished) prcoedures */ |
| 451 | break; |
| 452 | default: |
| 453 | OSMO_ASSERT(0); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | static void st_tx_pkt_meas_order_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 458 | { |
| 459 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 460 | build_multipart_packet_meas_order(ctx); |
| 461 | } |
| 462 | |
| 463 | static void st_tx_pkt_meas_order(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 464 | { |
| 465 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 466 | struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx; |
| 467 | |
| 468 | switch (event) { |
| 469 | case MS_ANR_EV_CREATE_RLCMAC_MSG: |
| 470 | data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data; |
| 471 | /* Set NC to 1 to force MS to send us Meas Rep */ |
| 472 | data_ctx->msg = llist_first_entry(&ctx->meas_order_queue, struct msgb, list); |
| 473 | if (!data_ctx->msg) { |
| 474 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); |
| 475 | return; |
| 476 | } |
| 477 | llist_del(&data_ctx->msg->list); |
| 478 | if (llist_empty(&ctx->meas_order_queue)) /* DONE */ |
| 479 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_PKT_MEAS_REPORT); |
| 480 | break; |
| 481 | default: |
| 482 | OSMO_ASSERT(0); |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | static void st_wait_rx_pkt_meas_report_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 487 | { |
| 488 | /* DO NOTHING */ |
| 489 | } |
| 490 | |
| 491 | |
| 492 | static void st_wait_rx_pkt_meas_report(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 493 | { |
| 494 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 495 | uint8_t *meas = alloca(ctx->num_cells); |
| 496 | struct ms_anr_ev_meas_compl ev_compl = { |
| 497 | .cell_list = ctx->cell_list, |
| 498 | .meas_list = meas, |
| 499 | .num_cells = ctx->num_cells, |
| 500 | }; |
| 501 | |
| 502 | switch (event) { |
| 503 | case MS_ANR_EV_RX_PKT_MEAS_REPORT: |
| 504 | handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data); |
| 505 | osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl); |
| 506 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET2); |
| 507 | break; |
| 508 | default: |
| 509 | OSMO_ASSERT(0); |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | static void st_tx_pkt_meas_reset2(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 514 | { |
| 515 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 516 | struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx; |
| 517 | uint8_t *meas = alloca(ctx->num_cells); |
| 518 | struct ms_anr_ev_meas_compl ev_compl = { |
| 519 | .cell_list = ctx->cell_list, |
| 520 | .meas_list = meas, |
| 521 | .num_cells = ctx->num_cells, |
| 522 | }; |
| 523 | |
| 524 | switch (event) { |
| 525 | case MS_ANR_EV_CREATE_RLCMAC_MSG: |
| 526 | /* Set NC to RESET and drop NC_Freq_list for MS to go back to |
| 527 | network defaults. */ |
| 528 | data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data; |
| 529 | data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn); |
| 530 | if (!data_ctx->msg) { |
| 531 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); |
| 532 | return; |
| 533 | } |
| 534 | ctx->poll_ts = data_ctx->ts; |
| 535 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK2); |
| 536 | break; |
| 537 | case MS_ANR_EV_RX_PKT_MEAS_REPORT: |
| 538 | handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data); |
| 539 | osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl); |
| 540 | break; |
| 541 | default: |
| 542 | OSMO_ASSERT(0); |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | static void st_wait_ctrl_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 547 | { |
| 548 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 549 | uint8_t *meas = alloca(ctx->num_cells); |
| 550 | struct ms_anr_ev_meas_compl ev_compl = { |
| 551 | .cell_list = ctx->cell_list, |
| 552 | .meas_list = meas, |
| 553 | .num_cells = ctx->num_cells, |
| 554 | }; |
| 555 | |
| 556 | switch (event) { |
| 557 | case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG: |
| 558 | ms_anr_fsm_state_chg(fi, MS_ANR_ST_DONE); |
| 559 | break; |
| 560 | case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT: |
| 561 | ms_anr_fsm_state_chg(fi, fi->state == MS_ANR_ST_WAIT_CTRL_ACK1 ? |
| 562 | MS_ANR_ST_TX_PKT_MEAS_RESET1 : |
| 563 | MS_ANR_ST_TX_PKT_MEAS_RESET2); |
| 564 | break; |
| 565 | case MS_ANR_EV_RX_PKT_MEAS_REPORT: |
| 566 | /* We may keep receiving meas report from MS while waiting to |
| 567 | * receive the CTRL ACK: */ |
| 568 | handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data); |
| 569 | osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl); |
| 570 | break; |
| 571 | default: |
| 572 | OSMO_ASSERT(0); |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 577 | { |
| 578 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); |
| 579 | } |
| 580 | |
| 581 | static void ms_anr_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) |
| 582 | { |
| 583 | struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv; |
| 584 | |
| 585 | /* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free, |
| 586 | so we need to avoid double-freeing it during ctx talloc free |
| 587 | destructor */ |
| 588 | talloc_reparent(ctx, ctx->ms, ctx->fi); |
| 589 | ctx->fi = NULL; |
| 590 | |
| 591 | /* remove references from owning MS and free entire ctx */ |
| 592 | ctx->ms->anr = NULL; |
| 593 | |
| 594 | if (cause != OSMO_FSM_TERM_REGULAR && cause != OSMO_FSM_TERM_REQUEST) { |
| 595 | /* Signal to bts_anr_fsm that orchestrates us that we failed, so |
| 596 | * that it can schedule the procedure again */ |
| 597 | struct ms_anr_ev_abort ev_data = { |
| 598 | .cell_list = &ctx->cell_list[0], |
| 599 | .num_cells = ctx->num_cells, |
| 600 | }; |
| 601 | osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data); |
| 602 | } |
| 603 | |
| 604 | talloc_free(ctx); |
| 605 | } |
| 606 | |
| 607 | static int ms_anr_fsm_timer_cb(struct osmo_fsm_inst *fi) |
| 608 | { |
| 609 | switch (fi->T) { |
| 610 | case PCU_TDEF_ANR_MS_TIMEOUT: |
| 611 | LOGPFSML(fi, LOGL_NOTICE, "ms_anr_fsm got stuck, freeing it. This probably indicates a bug somehwere (if not in state WAIT_PKT_MEAS_REPORT)\n"); |
| 612 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); |
| 613 | break; |
| 614 | default: |
| 615 | OSMO_ASSERT(0); |
| 616 | } |
| 617 | return 0; |
| 618 | } |
| 619 | |
| 620 | static struct osmo_fsm_state ms_anr_fsm_states[] = { |
| 621 | [MS_ANR_ST_INITIAL] = { |
| 622 | .in_event_mask = |
| 623 | X(MS_ANR_EV_START), |
| 624 | .out_state_mask = |
| 625 | X(MS_ANR_ST_TX_PKT_MEAS_RESET1), |
| 626 | .name = "INITIAL", |
| 627 | .action = st_initial, |
| 628 | }, |
| 629 | [MS_ANR_ST_TX_PKT_MEAS_RESET1] = { |
| 630 | .in_event_mask = |
| 631 | X(MS_ANR_EV_CREATE_RLCMAC_MSG) | |
| 632 | X(MS_ANR_EV_RX_PKT_MEAS_REPORT), |
| 633 | .out_state_mask = |
| 634 | X(MS_ANR_ST_WAIT_CTRL_ACK1), |
| 635 | .name = "TX_PKT_MEAS_RESET1", |
| 636 | .action = st_tx_pkt_meas_reset1, |
| 637 | }, |
| 638 | [MS_ANR_ST_WAIT_CTRL_ACK1] = { |
| 639 | .in_event_mask = |
| 640 | X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) | |
| 641 | X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) | |
| 642 | X(MS_ANR_EV_RX_PKT_MEAS_REPORT), |
| 643 | .out_state_mask = |
| 644 | X(MS_ANR_ST_TX_PKT_MEAS_RESET1) | |
| 645 | X(MS_ANR_ST_TX_PKT_MEAS_ORDER), |
| 646 | .name = "WAIT_CTRL_ACK1", |
| 647 | .action = st_wait_ctrl_ack1, |
| 648 | }, |
| 649 | [MS_ANR_ST_TX_PKT_MEAS_ORDER] = { |
| 650 | .in_event_mask = |
| 651 | X(MS_ANR_EV_CREATE_RLCMAC_MSG), |
| 652 | .out_state_mask = |
| 653 | X(MS_ANR_ST_WAIT_PKT_MEAS_REPORT), |
| 654 | .name = "TX_PKT_MEAS_ORDER", |
| 655 | .onenter = st_tx_pkt_meas_order_on_enter, |
| 656 | .action = st_tx_pkt_meas_order, |
| 657 | }, |
| 658 | [MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = { |
| 659 | .in_event_mask = |
| 660 | X(MS_ANR_EV_RX_PKT_MEAS_REPORT), |
| 661 | .out_state_mask = |
| 662 | X(MS_ANR_ST_TX_PKT_MEAS_RESET2), |
| 663 | .name = "WAIT_PKT_MEAS_REPORT", |
| 664 | .onenter = st_wait_rx_pkt_meas_report_on_enter, |
| 665 | .action = st_wait_rx_pkt_meas_report, |
| 666 | }, |
| 667 | [MS_ANR_ST_TX_PKT_MEAS_RESET2] = { |
| 668 | .in_event_mask = |
| 669 | X(MS_ANR_EV_CREATE_RLCMAC_MSG) | |
| 670 | X(MS_ANR_EV_RX_PKT_MEAS_REPORT), |
| 671 | .out_state_mask = |
| 672 | X(MS_ANR_ST_WAIT_CTRL_ACK2), |
| 673 | .name = "TX_PKT_MEAS_RESET2", |
| 674 | .action = st_tx_pkt_meas_reset2, |
| 675 | }, |
| 676 | [MS_ANR_ST_WAIT_CTRL_ACK2] = { |
| 677 | .in_event_mask = |
| 678 | X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) | |
| 679 | X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) | |
| 680 | X(MS_ANR_EV_RX_PKT_MEAS_REPORT), |
| 681 | .out_state_mask = |
| 682 | X(MS_ANR_ST_TX_PKT_MEAS_RESET2) | |
| 683 | X(MS_ANR_ST_DONE), |
| 684 | .name = "WAIT_CTRL_ACK2", |
| 685 | .action = st_wait_ctrl_ack2, |
| 686 | }, |
| 687 | [MS_ANR_ST_DONE] = { |
| 688 | .in_event_mask = 0, |
| 689 | .out_state_mask = 0, |
| 690 | .name = "DONE", |
| 691 | .onenter = st_done_on_enter, |
| 692 | }, |
| 693 | }; |
| 694 | |
| 695 | static struct osmo_fsm ms_anr_fsm = { |
| 696 | .name = "MS_ANR", |
| 697 | .states = ms_anr_fsm_states, |
| 698 | .num_states = ARRAY_SIZE(ms_anr_fsm_states), |
| 699 | .timer_cb = ms_anr_fsm_timer_cb, |
| 700 | .cleanup = ms_anr_fsm_cleanup, |
| 701 | .log_subsys = DANR, |
| 702 | .event_names = ms_anr_fsm_event_names, |
| 703 | }; |
| 704 | |
| 705 | static __attribute__((constructor)) void ms_anr_fsm_init(void) |
| 706 | { |
| 707 | OSMO_ASSERT(osmo_fsm_register(&ms_anr_fsm) == 0); |
| 708 | } |
| 709 | |
| 710 | static int ms_anr_fsm_ctx_talloc_destructor(struct ms_anr_fsm_ctx *ctx) |
| 711 | { |
| 712 | /* if ctx->fi != NULL it means we come directly from talloc_free(ctx) |
| 713 | * without having passed through ms_anr_fsm_cleanup() as part of |
| 714 | * osmo_fsm_inst_term(). In this case, clean up manually similarly to |
| 715 | * ms_anr_fsm_cleanup() and free the ctx->fi. */ |
| 716 | if (ctx->fi) { |
| 717 | /* Signal to bts_anr_fsm that orchestrates us that we failed, so |
| 718 | * that it can schedule the procedure again */ |
| 719 | struct ms_anr_ev_abort ev_data = { |
| 720 | .cell_list = &ctx->cell_list[0], |
| 721 | .num_cells = ctx->num_cells, |
| 722 | }; |
| 723 | osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data); |
| 724 | osmo_fsm_inst_free(ctx->fi); |
| 725 | ctx->fi = NULL; |
| 726 | } |
| 727 | |
| 728 | return 0; |
| 729 | } |
| 730 | |
| 731 | struct ms_anr_fsm_ctx *ms_anr_fsm_alloc(struct GprsMs* ms) |
| 732 | { |
| 733 | struct ms_anr_fsm_ctx *ctx = talloc_zero(ms, struct ms_anr_fsm_ctx); |
| 734 | char buf[64]; |
| 735 | |
| 736 | talloc_set_destructor(ctx, ms_anr_fsm_ctx_talloc_destructor); |
| 737 | |
| 738 | ctx->ms = ms; |
| 739 | INIT_LLIST_HEAD(&ctx->meas_order_queue); |
| 740 | |
| 741 | snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms)); |
| 742 | ctx->fi = osmo_fsm_inst_alloc(&ms_anr_fsm, ctx, ctx, LOGL_INFO, buf); |
| 743 | if (!ctx->fi) |
| 744 | goto free_ret; |
| 745 | |
| 746 | return ctx; |
| 747 | free_ret: |
| 748 | talloc_free(ctx); |
| 749 | return NULL; |
| 750 | } |
| 751 | |
| 752 | /* Used by bts_anr_fsm to abort ongoing procedure without need of being informed |
| 753 | * back by BTS_ANR_EV_MS_MEAS_ABORTED */ |
| 754 | void ms_anr_fsm_abort(struct ms_anr_fsm_ctx *ctx) |
| 755 | { |
| 756 | osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_REQUEST, NULL); |
| 757 | } |