blob: 94d3d0d1e5697e1bbedb9616cf3501b25871e791 [file] [log] [blame]
Harald Welte798418a2009-11-29 22:56:14 +01001/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
2 * actually implement the handover algorithm/decision, but executes a
3 * handover decision */
4
5/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
29#include <time.h>
30#include <netinet/in.h>
31
32#include <openbsc/msgb.h>
33#include <openbsc/debug.h>
34#include <openbsc/gsm_data.h>
35#include <openbsc/gsm_utils.h>
36#include <openbsc/gsm_subscriber.h>
37#include <openbsc/gsm_04_08.h>
38#include <openbsc/abis_rsl.h>
39#include <openbsc/chan_alloc.h>
40#include <openbsc/signal.h>
41#include <openbsc/talloc.h>
42#include <openbsc/transaction.h>
Harald Weltea0379022009-12-20 17:04:40 +010043#include <openbsc/rtp_proxy.h>
Harald Welte798418a2009-11-29 22:56:14 +010044
45struct bsc_handover {
46 struct llist_head list;
47
48 struct gsm_lchan *old_lchan;
49 struct gsm_lchan *new_lchan;
50
51 struct timer_list T3103;
52
53 u_int8_t ho_ref;
54};
55
56static LLIST_HEAD(bsc_handovers);
57
58static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
59{
60 struct bsc_handover *ho;
61
62 llist_for_each_entry(ho, &bsc_handovers, list) {
63 if (ho->new_lchan == new_lchan)
64 return ho;
65 }
66
67 return NULL;
68}
69
70static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
71{
72 struct bsc_handover *ho;
73
74 llist_for_each_entry(ho, &bsc_handovers, list) {
75 if (ho->old_lchan == old_lchan)
76 return ho;
77 }
78
79 return NULL;
80}
81
82/* Hand over the specified logical channel to the specified new BTS.
83 * This is the main entry point for the actual handover algorithm,
84 * after it has decided it wants to initiate HO to a specific BTS */
85int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
86{
87 struct gsm_lchan *new_lchan;
88 struct bsc_handover *ho;
Harald Welte8d77b952009-12-17 00:31:10 +010089 static u_int8_t ho_ref;
Harald Welte798418a2009-11-29 22:56:14 +010090 int rc;
91
Harald Welte66706812009-12-17 22:23:21 +010092 /* don't attempt multiple handovers for the same lchan at
93 * the same time */
94 if (bsc_ho_by_old_lchan(old_lchan))
95 return -EBUSY;
96
Harald Welteb1d4c8e2009-12-17 23:10:46 +010097 DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
Harald Welte8d77b952009-12-17 00:31:10 +010098 old_lchan->ts->trx->bts->nr, bts->nr);
99
Harald Welte798418a2009-11-29 22:56:14 +0100100 new_lchan = lchan_alloc(bts, old_lchan->type);
Harald Welte8d77b952009-12-17 00:31:10 +0100101 if (!new_lchan) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100102 LOGP(DHO, LOGL_NOTICE, "No free channel\n");
Harald Welte798418a2009-11-29 22:56:14 +0100103 return -ENOSPC;
Harald Welte8d77b952009-12-17 00:31:10 +0100104 }
Harald Welte798418a2009-11-29 22:56:14 +0100105
106 ho = talloc_zero(NULL, struct bsc_handover);
107 if (!ho) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100108 LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
Harald Welte798418a2009-11-29 22:56:14 +0100109 lchan_free(new_lchan);
110 return -ENOMEM;
111 }
112 ho->old_lchan = old_lchan;
113 ho->new_lchan = new_lchan;
Harald Welte8d77b952009-12-17 00:31:10 +0100114 ho->ho_ref = ho_ref++;
115
116 /* copy some parameters from old lchan */
117 memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
118 new_lchan->ms_power = old_lchan->ms_power;
119 new_lchan->bs_power = old_lchan->bs_power;
120 new_lchan->rsl_cmode = old_lchan->rsl_cmode;
121 new_lchan->tch_mode = old_lchan->tch_mode;
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100122 new_lchan->subscr = subscr_get(old_lchan->subscr);
Harald Welte798418a2009-11-29 22:56:14 +0100123
124 /* FIXME: do we have a better idea of the timing advance? */
Harald Welte8d77b952009-12-17 00:31:10 +0100125 rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
126 ho->ho_ref);
Harald Welte798418a2009-11-29 22:56:14 +0100127 if (rc < 0) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100128 LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
Harald Welte798418a2009-11-29 22:56:14 +0100129 talloc_free(ho);
130 lchan_free(new_lchan);
131 return rc;
132 }
133
134 llist_add(&ho->list, &bsc_handovers);
135 /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
136
137 return 0;
138}
139
140/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
141static void ho_T3103_cb(void *_ho)
142{
143 struct bsc_handover *ho = _ho;
144
Harald Welte8d77b952009-12-17 00:31:10 +0100145 DEBUGP(DHO, "HO T3103 expired\n");
146
Harald Welte798418a2009-11-29 22:56:14 +0100147 lchan_free(ho->new_lchan);
148 llist_del(&ho->list);
149 talloc_free(ho);
150}
151
152/* RSL has acknowledged activation of the new lchan */
153static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
154{
155 struct bsc_handover *ho;
156 int rc;
157
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100158 /* we need to check if this channel activation is related to
159 * a handover at all (and if, which particular handover) */
Harald Welte798418a2009-11-29 22:56:14 +0100160 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100161 if (!ho)
Harald Welte798418a2009-11-29 22:56:14 +0100162 return -ENODEV;
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100163
164 DEBUGP(DHO, "handover activate ack, send HO Command\n");
Harald Welte798418a2009-11-29 22:56:14 +0100165
166 /* we can now send the 04.08 HANDOVER COMMAND to the MS
167 * using the old lchan */
168
Harald Welte8d77b952009-12-17 00:31:10 +0100169 rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
Harald Welte798418a2009-11-29 22:56:14 +0100170
171 /* start T3103. We can continue either with T3103 expiration,
172 * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
173 ho->T3103.cb = ho_T3103_cb;
Harald Weltee47f96b2009-12-18 11:49:03 +0100174 ho->T3103.data = ho;
Harald Welte798418a2009-11-29 22:56:14 +0100175 bsc_schedule_timer(&ho->T3103, 10, 0);
176
Harald Weltea0379022009-12-20 17:04:40 +0100177 /* create a RTP connection */
178 if (is_ipaccess_bts(new_lchan->ts->trx->bts))
179 rsl_ipacc_crcx(new_lchan);
180
Harald Welte798418a2009-11-29 22:56:14 +0100181 return 0;
182}
183
184/* RSL has not acknowledged activation of the new lchan */
185static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
186{
187 struct bsc_handover *ho;
188
189 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100190 if (!ho) {
191 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100192 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100193 }
Harald Welte798418a2009-11-29 22:56:14 +0100194
195 llist_del(&ho->list);
196 talloc_free(ho);
197
198 /* FIXME: maybe we should try to allocate a new LCHAN here? */
199
200 return 0;
201}
202
203/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
204static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
205{
206 struct bsc_handover *ho;
207
208 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100209 if (!ho) {
210 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100211 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100212 }
Harald Welte798418a2009-11-29 22:56:14 +0100213
214 bsc_del_timer(&ho->T3103);
Harald Welte798418a2009-11-29 22:56:14 +0100215
Harald Weltefe18d5c2009-12-17 17:14:43 +0100216 /* update lchan pointer of transaction */
217 trans_lchan_change(ho->old_lchan, new_lchan);
218
Harald Welteade773f2009-12-21 13:29:19 +0100219 ho->old_lchan->state = LCHAN_S_INACTIVE;
220 lchan_auto_release(ho->old_lchan);
221
Harald Welte798418a2009-11-29 22:56:14 +0100222 /* do something to re-route the actual speech frames ! */
Harald Welte798418a2009-11-29 22:56:14 +0100223
Harald Welteade773f2009-12-21 13:29:19 +0100224 llist_del(&ho->list);
Harald Welte798418a2009-11-29 22:56:14 +0100225 talloc_free(ho);
226
227 return 0;
228}
229
230/* GSM 04.08 HANDOVER FAIL has been received */
231static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
232{
233 struct bsc_handover *ho;
234
235 ho = bsc_ho_by_old_lchan(old_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100236 if (!ho) {
237 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100238 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100239 }
Harald Welte798418a2009-11-29 22:56:14 +0100240
241 bsc_del_timer(&ho->T3103);
242 llist_del(&ho->list);
243 put_lchan(ho->new_lchan);
244 talloc_free(ho);
245
246 return 0;
247}
248
249/* GSM 08.58 HANDOVER DETECT has been received */
250static int ho_rsl_detect(struct gsm_lchan *new_lchan)
251{
252 struct bsc_handover *ho;
253
Harald Welte6f7a5a72009-12-18 11:52:03 +0100254 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100255 if (!ho) {
256 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100257 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100258 }
Harald Welte798418a2009-11-29 22:56:14 +0100259
260 /* FIXME: do we actually want to do something here ? */
261
262 return 0;
263}
264
Harald Weltea0379022009-12-20 17:04:40 +0100265static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan)
266{
267 struct bsc_handover *ho;
268 struct rtp_socket *old_rs, *new_rs, *other_rs;
269
270 ho = bsc_ho_by_new_lchan(new_lchan);
271 if (!ho) {
272 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
273 return -ENODEV;
274 }
275
276 if (ipacc_rtp_direct) {
277 LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
278 return 0;
279 }
280
281 /* RTP Proxy mode */
282 new_rs = new_lchan->abis_ip.rtp_socket;
283 old_rs = ho->old_lchan->abis_ip.rtp_socket;
284
285 if (!new_rs) {
286 LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n");
287 return -EIO;
288 }
289
290 rsl_ipacc_mdcx_to_rtpsock(new_lchan);
291
292 if (!old_rs) {
293 LOGP(DHO, LOGL_ERROR, "no RTP socekt for old_lchan\n");
294 return -EIO;
295 }
296
297 /* copy rx_action and reference to other sock */
298 new_rs->rx_action = old_rs->rx_action;
299 new_rs->tx_action = old_rs->tx_action;
300 new_rs->transmit = old_rs->transmit;
301
302 switch (ho->old_lchan->abis_ip.rtp_socket->rx_action) {
303 case RTP_PROXY:
304 other_rs = old_rs->proxy.other_sock;
305 rtp_socket_proxy(new_rs, other_rs);
306 /* delete reference to other end socket to prevent
307 * rtp_socket_free() from removing the inverse reference */
308 old_rs->proxy.other_sock = NULL;
309 break;
310 case RTP_RECV_UPSTREAM:
311 new_rs->receive = old_rs->receive;
312 break;
313 case RTP_NONE:
314 break;
315 }
316
317 return 0;
318}
319
Harald Welte798418a2009-11-29 22:56:14 +0100320static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
321 void *handler_data, void *signal_data)
322{
323 struct gsm_lchan *lchan;
324
325 switch (subsys) {
326 case SS_LCHAN:
327 lchan = signal_data;
328 switch (signal) {
329 case S_LCHAN_ACTIVATE_ACK:
330 return ho_chan_activ_ack(lchan);
331 case S_LCHAN_ACTIVATE_NACK:
332 return ho_chan_activ_nack(lchan);
333 case S_LCHAN_HANDOVER_DETECT:
334 return ho_rsl_detect(lchan);
335 case S_LCHAN_HANDOVER_COMPL:
336 return ho_gsm48_ho_compl(lchan);
337 case S_LCHAN_HANDOVER_FAIL:
338 return ho_gsm48_ho_fail(lchan);
339 }
340 break;
Harald Weltea0379022009-12-20 17:04:40 +0100341 case SS_ABISIP:
342 lchan = signal_data;
343 switch (signal) {
344 case S_ABISIP_CRCX_ACK:
345 return ho_ipac_crcx_ack(lchan);
346 break;
347 }
348 break;
Harald Welte798418a2009-11-29 22:56:14 +0100349 default:
350 break;
351 }
352
353 return 0;
354}
355
356static __attribute__((constructor)) void on_dso_load_ho_logic(void)
357{
358 register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
Harald Weltea0379022009-12-20 17:04:40 +0100359 register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL);
Harald Welte798418a2009-11-29 22:56:14 +0100360}