2 /* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
3 * (C) 2010,2017 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87@googlemail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <osmocom/gsm/gsm_utils.h>
30 #include <osmocom/gsm/protocol/gsm_08_58.h>
31 #include <osmocom/core/msgb.h>
32 #include <virtphy/l1ctl_sap.h>
33 #include <virtphy/virt_l1_sched.h>
34 #include <osmocom/core/gsmtap.h>
35 #include <virtphy/logging.h>
36 #include <l1ctl_proto.h>
39 * @brief Change the signal strength for a given arfcn.
41 * Should be called if a msg is received on the virtual layer. The configured signal level reduction is applied.
43 * @param [in] arfcn to change sig str for.
44 * @param [in] sig_lev the measured signal level value.
46 uint16_t prim_pm_set_sig_strength(struct l1_model_ms
*ms
, uint16_t arfcn
, int16_t sig_lev
)
48 struct l1_state_ms
*l1s
= &ms
->state
;
50 if (l1s
->pm
.timeout_s
> 0 || l1s
->pm
.timeout_us
> 0) {
51 osmo_timer_schedule(&l1s
->pm
.meas
.arfcn_sig_lev_timers
[arfcn
],
52 l1s
->pm
.timeout_s
, l1s
->pm
.timeout_us
);
54 l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn
] = sig_lev
- l1s
->pm
.meas
.arfcn_sig_lev_red_dbm
[arfcn
];
55 DEBUGPMS(DL1C
, ms
, "Power measurement set for arfcn %u. Set signal level to %d (== rxlev: %u).\n",
56 arfcn
, l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn
],
57 dbm2rxlev(l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn
]));
58 return l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn
];
61 void prim_pm_timer_cb(void *data
)
63 /* reset the signal level to bad value if no messages have been
64 * received from that rfcn for a given time */
65 DEBUGP(DL1C
, "Timeout occurred for arfcn, signal level reset to worst value.\n");
66 *((int16_t*)data
) = MIN_SIG_LEV_DBM
;
70 * @brief Handler for received L1CTL_PM_REQ from L23.
72 * -- power measurement request --
74 * @param [in] msg the received message.
76 * Process power measurement for a given range of arfcns to calculate
77 * signal power and connection quality.
79 * Note: This should only be called after a certain time so some
80 * messages have already been received.
82 void l1ctl_rx_pm_req(struct l1_model_ms
*ms
, struct msgb
*msg
)
84 struct l1_state_ms
*l1s
= &ms
->state
;
85 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
86 struct l1ctl_pm_req
*pm_req
= (struct l1ctl_pm_req
*) l1h
->data
;
87 struct msgb
*resp_msg
= l1ctl_msgb_alloc(L1CTL_PM_CONF
);
90 /* convert to host order */
91 pm_req
->range
.band_arfcn_from
= ntohs(pm_req
->range
.band_arfcn_from
);
92 pm_req
->range
.band_arfcn_to
= ntohs(pm_req
->range
.band_arfcn_to
);
94 LOGPMS(DL1C
, LOGL_INFO
, ms
, "Rx L1CTL_PM_REQ TYPE=%u, FROM=%d, TO=%d\n",
95 pm_req
->type
, pm_req
->range
.band_arfcn_from
, pm_req
->range
.band_arfcn_to
);
97 for (arfcn_next
= pm_req
->range
.band_arfcn_from
;
98 arfcn_next
<= pm_req
->range
.band_arfcn_to
; ++arfcn_next
) {
99 struct l1ctl_pm_conf
*pm_conf
= (struct l1ctl_pm_conf
*) msgb_put(resp_msg
, sizeof(*pm_conf
));
100 pm_conf
->band_arfcn
= htons(arfcn_next
);
101 /* set min and max to the value calculated for that
102 * arfcn (IGNORE UPLINKK AND PCS AND OTHER FLAGS) */
103 pm_conf
->pm
[0] = dbm2rxlev(l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn_next
& ARFCN_NO_FLAGS_MASK
]);
104 pm_conf
->pm
[1] = dbm2rxlev(l1s
->pm
.meas
.arfcn_sig_lev_dbm
[arfcn_next
& ARFCN_NO_FLAGS_MASK
]);
105 if (arfcn_next
== pm_req
->range
.band_arfcn_to
) {
106 struct l1ctl_hdr
*resp_l1h
= msgb_l1(resp_msg
);
107 resp_l1h
->flags
|= L1CTL_F_DONE
;
109 /* no more space to hold mor pm info in msgb, flush to l23 */
110 if (msgb_tailroom(resp_msg
) < sizeof(*pm_conf
)) {
111 LOGPMS(DL1C
, LOGL_INFO
, ms
, "Tx L1CTL_PM_CONF\n");
112 l1ctl_sap_tx_to_l23_inst(ms
, resp_msg
);
113 resp_msg
= l1ctl_msgb_alloc(L1CTL_PM_CONF
);
116 /* transmit the remaining part of pm response to l23 */
118 LOGPMS(DL1C
, LOGL_INFO
, ms
, "Tx L1CTL_PM_CONF\n");
119 l1ctl_sap_tx_to_l23_inst(ms
, resp_msg
);
124 * @brief Initialize virtual prim pm.
126 * @param [in] model the l1 model instance
128 void prim_pm_init(struct l1_model_ms
*model
)
130 struct l1_state_ms
*l1s
= &model
->state
;
133 /* init the signal level of all arfcns with the lowest value possible */
134 memset(l1s
->pm
.meas
.arfcn_sig_lev_dbm
, MIN_SIG_LEV_DBM
, sizeof (int16_t) * 1024);
136 for (i
= 0; i
< 1024; ++i
) {
137 l1s
->pm
.meas
.arfcn_sig_lev_timers
[i
].cb
= prim_pm_timer_cb
;
138 l1s
->pm
.meas
.arfcn_sig_lev_timers
[i
].data
= &l1s
->pm
.meas
.arfcn_sig_lev_dbm
[i
];
142 void prim_pm_exit(struct l1_model_ms
*model
)
144 struct l1_state_ms
*l1s
= &model
->state
;
147 for (i
= 0; i
< 1024; ++i
)
148 osmo_timer_del(&l1s
->pm
.meas
.arfcn_sig_lev_timers
[i
]);