Work on various L3 processes.
[osmocom-bb.git] / src / host / gsm48-andreas / gsm58.c
1 /*
2  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
3  *
4  * All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (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 along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  */
21
22 todo: cell selection and reselection criteria
23 todo: path loss
24 todo: downlink signal failure
25
26 /*
27  * initialization
28  */
29
30 void gsm58_init(struct osmocom_ms *ms)
31 {
32         struct gsm58_selproc *sp = &ms->selproc;
33
34         memset(sp, 0, sizeof(*sp));
35         sp->ms = ms;
36
37         /* init list */
38         INIT_LLIST_HEAD(&sp->event_queue);
39
40         return 0;
41 }
42
43 /*
44  * event messages
45  */
46
47 /* allocate a 05.08 event message */
48 struct msgb *gsm58_msgb_alloc(int msg_type)
49 {
50         struct msgb *msg;
51         struct gsm58_msg *gm;
52
53         msg = msgb_alloc_headroom(GSM58_ALLOC_SIZE, GSM58_ALLOC_HEADROOM,
54                 "GSM 05.08");
55         if (!msg)
56                 return NULL;
57
58         gm = (struct gsm58_msg *)msgb_put(msg, sizeof(*gm));
59         gm->msg_type = msg_type;
60         return msg;
61 }
62
63 /* queue received message */
64 int gsm58_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
65 {
66         struct gsm58_selproc *sp = &ms->selproc;
67
68         msgb_enqueue(&sp->event_queue, msg);
69 }
70
71 /*
72  * timer
73  */
74
75 /* search timer handling */
76 static void gsm58_timer_timeout(void *arg)
77 {
78         struct gsm322_58_selproc *sp = arg;
79         struct msgb *nmsg;
80
81         /* indicate BCCH selection T timeout */
82         nmsg = gsm58_msgb_alloc(GSM58_EVENT_TIMEOUT);
83         if (!nmsg)
84                 return -ENOMEM;
85         gsm58_sendmsg(ms, nmsg);
86 }
87
88 static void gsm58_timer_start(struct gsm58_selproc *sp, int secs, int micro)
89 {
90         DEBUGP(DLC, "starting FREQUENCY search timer\n");
91         sp->timer.cb = gsm58_timer_timeout;
92         sp->timer.data = sp;
93         bsc_schedule_timer(&sp->timer, secs, micro);
94 }
95
96 static void gsm58_timer_stop(struct gsm58_selproc *plmn)
97 {
98         if (timer_pending(&sp->timer)) {
99                 DEBUGP(DLC, "stopping pending timer\n");
100                 bsc_del_timer(&sp->timer);
101         }
102 }
103
104 /*
105  * process handlers
106  */
107
108 /* select first/next frequency */
109 static int gsm58_select_bcch(struct osmocom_ms *ms, int next)
110 {
111         struct gsm_support *s = &ms->support;
112         struct gsm58_selproc *sp = &ms->selproc;
113         int i, j = 0;
114
115         if (next)
116                 sp->cur_freq++;
117
118 next:
119         for (i = 0, i < 1024, i++) {
120                 if ((sp->ba[i >> 3] & (1 << (i & 7)))) {
121                         if (j == sp->cur_freq)
122                                 break;
123                         j++;
124                 }
125         }
126         if (i == 1024) {
127                 struct msgb *nmsg;
128
129                 DEBUGP(DLC, "Cycled through all %d frequencies in list.\n", j);
130                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_F);
131                 if (!nmsg)
132                         return -ENOMEM;
133                 gsm322_sendmsg(ms, nmsg);
134         }
135
136         /* if frequency not supported */
137         if (!(s->freq_map[i >> 3] & (1 << (i & 7)))) {
138                 DEBUGP(DLC, "Frequency %d in list, but not supported.\n", i);
139                 sp->cur_freq++;
140                 goto next;
141         }
142
143         /* set current BCCH frequency */
144         sp->arfcn = i;
145         DEBUGP(DLC, "Frequency %d selected, wait for sync.\n", sp->arfcn);
146         tx_ph_bcch_req(ms, sp->arfcn);
147
148         /* start timer for synchronizanation */
149         gsm58_timer_start(sp, 0, 500000);
150
151         sp->mode = GSM58_MODE_SYNC;
152
153         return 0;
154 }
155
156 /* start normal cell selection: search any channel for given PLMN */
157 static int gsm58_start_normal_sel(struct osmocom_ms *ms, struct msgb *msg)
158 {
159         struct gsm58_selproc *sp = &ms->selproc;
160         struct gsm_support *s = &ms->support;
161         struct gsm58_msg *gm = msgb->data;
162
163         /* reset process */
164         memset(sp, 0, sizeof(*sp));
165
166         /* use all frequencies from supported frequency map */
167         memcpy(sp->ba, s->freq_map, sizeof(sp->ba));
168
169         /* limit process to given PLMN */
170         sp->mcc = gm->mcc;
171         sp->mnc = gm->mnc;
172
173         /* select first channel */
174         gsm58_select_bcch(ms, 0);
175 }
176
177 /* start stored cell selection: search given channels for given PLMN */
178 static int gsm58_start_stored_sel(struct osmocom_ms *ms, struct msgb *msg)
179 {
180         struct gsm58_selproc *sp = &ms->selproc;
181         struct gsm_support *s = &ms->support;
182         struct gsm58_msg *gm = msgb->data;
183
184         /* reset process */
185         memset(sp, 0, sizeof(*sp));
186
187         /* use all frequencies from given frequency map */
188         memcpy(sp->ba, sp->ba, sizeof(sp->ba));
189
190         /* limit process to given PLMN */
191         sp->mcc = gm->mcc;
192         sp->mnc = gm->mnc;
193
194         /* select first channel */
195         gsm58_select_bcch(ms, 0);
196 }
197
198 /* start any cell selection: search any channel for any PLMN */
199 static int gsm58_start_any_sel(struct osmocom_ms *ms, struct msgb *msg)
200 {
201         struct gsm58_selproc *sp = &ms->selproc;
202         struct gsm_support *s = &ms->support;
203         struct gsm58_msg *gm = msgb->data;
204
205         /* reset process */
206         memset(sp, 0, sizeof(*sp));
207
208         /* allow any cell not barred */
209         sp->any = 1;
210
211         /* use all frequencies from supported frequency map */
212         memcpy(sp->ba, s->freq_map, sizeof(sp->ba));
213
214         /* select first channel */
215         gsm58_select_bcch(ms, 0);
216 }
217
218 /* timeout while selecting BCCH */
219 static int gsm58_sel_timeout(struct osmocom_ms *ms, struct msgb *msg)
220 {
221         struct gsm58_selproc *sp = &ms->selproc;
222
223         switch(sp->mode) {
224         case GSM58_MODE_SYNC:
225                 /* if no sync is received from radio withing sync time */
226                 DEBUGP(DLC, "Syncronization failed, selecting next frq.\n");
227                 break;
228         case GSM58_MODE_READ:
229                 /* timeout while reading BCCH */
230                 DEBUGP(DLC, "BCC reading failed, selecting next frq.\n");
231                 break;
232         default:
233                 DEBUGP(DLC, "Timeout in wrong mode, please fix!\n");
234         }
235
236         gsm58_select_bcch(ms, 1);
237
238         return 0;
239 }
240
241 /* we are synchronized to selecting BCCH */
242 static int gsm58_sel_sync(struct osmocom_ms *ms, struct msgb *msg)
243 {
244         struct gsm58_selproc *sp = &ms->selproc;
245         struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
246
247         /* if we got a sync while already selecting a new frequency */
248         if (gm->arfcn != sp->arfcn) {
249                 DEBUGP(DLC, "Requested frq %d, but synced to %d, ignoring.\n"
250                         sp->arfcn, gm->arfcn);
251                 return 0;
252         }
253
254         DEBUGP(DLC, "Synced to %d, waiting for relevant data.\n", sp->arfcn);
255
256         /* set timer for reading BCCH */
257         gsm58_timer_start(sp, 4, 0); // TODO: timer depends on BCCH configur.
258
259         /* reset sysinfo and wait for relevant data */
260         gsm_sysinfo_init(ms);
261         sp->mode = GSM58_MODE_READ;
262
263         return 0;
264 }
265
266 /* we are getting sysinfo from BCCH */
267 static int gsm58_sel_sysinfo(struct osmocom_ms *ms, struct msgb *msg)
268 {
269         struct gsm58_selproc *sp = &ms->selproc;
270         struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
271         struct gsm322_msg *ngm;
272         struct msgb *nmsg;
273         int barred = 0, i;
274
275         /* ignore system info, if not synced */
276         if (sp->mode != GSM58_MODE_DATA && sp->mode != GSM58_MODE_CAMP)
277                 return 0;
278
279         /* check if cell is barred for us */
280         if (!subscr->barred_access && s->cell_barred)
281                 barred = 1;
282         if (sp->any) {
283                 if (s->class_barr[10])
284                         barred = 1;
285         } else {
286                 for (i = 0, i <= 15, i++)
287                         if (subscr->class_access[i] && !s->class_barr[i])
288                                 break;
289                 if (i > 15)
290                         barred = 1;
291         }
292
293
294         if (sp->mode == GSM58_MODE_CAMP) {
295                 /* cell has become barred */
296                 if (barred) {
297                         DEBUGP(DLC, "Cell has become barred, starting "
298                                 "reselection.\n");
299
300                         sp->mode = GSM58_MODE_IDLE;
301
302                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
303                         if (!nmsg)
304                                 return -ENOMEM;
305                         gsm322_sendmsg(ms, nmsg);
306
307                         return 0;
308                 }
309
310                 return 0;
311         }
312
313         /* can't use barred cell */
314         if (barred) {
315                 DEBUGP(DLC, "Selected cell is barred, select next.\n");
316                 gsm58_timer_stop(sp);
317                 gsm58_select_bcch(ms, 1);
318
319                 return 0;
320         }
321
322         /* if PLMN not yet indicated, but required if not any cell selection */
323         if (!sp->any && !s->mcc)
324                 return 0;
325
326         // todo: what else do we need until we can camp?
327
328         /* wrong PLMN */
329         if (!sp->any && s->mcc == sp->mcc && s->mnc == sp->mnc) {
330                 DEBUGP(DLC, "Selected cell of differen PLMN, select next.\n");
331                 gsm58_timer_stop(sp);
332                 gsm58_select_bcch(ms, 1);
333
334                 return 0;
335         }
336
337         /* all relevant informations received */
338         gsm58_timer_stop(sp);
339         sp->mode = GSM58_MODE_CAMP;
340
341         DEBUGP(DCS, "Cell with freq %d, mcc = %d, mnc = %d selected.\n",
342                 sp->arfcn, s->mcc, s->mnc);
343
344         /* indicate cell */
345         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
346         if (!nmsg)
347                 return -ENOMEM;
348         ngm = (struct gsm322_msg *)nmsg->data;
349         ngm->mcc = s->mcc;
350         ngm->mnc = s->mnc;
351         ngm->lac = s->lac;
352         gsm322_sendmsg(ms, nmsg);
353
354         return 0;
355 }
356
357 /*
358  * events
359  */
360
361 /* receive events for GSM 05.08 processes */
362 static int gsm58_event(struct osmocom_ms *ms, struct msgb *msg)
363 {
364         struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
365         int msg_type = gm->msg_type;
366
367         DEBUGP(DCS, "(ms %s) Message '%s' for link control "
368                 "%s\n", ms->name, gsm58_event_names[msg_type],
369                 plmn_a_state_names[plmn->state]);
370
371         switch(msg_type) {
372         case GSM58_EVENT_START_NORMAL:
373                 gsm58_start_normal_sel(ms, msg);
374                 break;
375         case GSM58_EVENT_START_STORED:
376                 gsm58_start_stored_sel(ms, msg);
377                 break;
378         case GSM58_EVENT_START_ANY:
379                 gsm58_start_any_sel(ms, msg);
380                 break;
381         case GSM58_EVENT_TIMEOUT:
382                 gsm58_sel_timeout(ms, msg);
383                 break;
384         case GSM58_EVENT_SYNC:
385                 gsm58_sel_sync(ms, msg);
386                 break;
387         case GSM58_EVENT_SYSINFO:
388                 gsm58_sel_sysinfo(ms, msg);
389                 break;
390         default:
391                 DEBUGP(DLC, "Message unhandled.\n");
392         }
393
394         return 0;
395 }
396
397 /* dequeue GSM 05.08 events */
398 int gsm58_event_queue(struct osmocom_ms *ms)
399 {
400         struct gsm58_selproc *sp = &ms->selproc;
401         struct msgb *msg;
402         int work = 0;
403         
404         while ((msg = msgb_dequeue(&sp->event_queue))) {
405                 gsm58_event(ms, msg);
406                 free_msgb(msg);
407                 work = 1; /* work done */
408         }
409         
410         return work;
411 }
412
413
414