2 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
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.
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.
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.
22 todo: cell selection and reselection criteria
24 todo: downlink signal failure
30 void gsm58_init(struct osmocom_ms *ms)
32 struct gsm58_selproc *sp = &ms->selproc;
34 memset(sp, 0, sizeof(*sp));
38 INIT_LLIST_HEAD(&sp->event_queue);
47 /* allocate a 05.08 event message */
48 struct msgb *gsm58_msgb_alloc(int msg_type)
53 msg = msgb_alloc_headroom(GSM58_ALLOC_SIZE, GSM58_ALLOC_HEADROOM,
58 gm = (struct gsm58_msg *)msgb_put(msg, sizeof(*gm));
59 gm->msg_type = msg_type;
63 /* queue received message */
64 int gsm58_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
66 struct gsm58_selproc *sp = &ms->selproc;
68 msgb_enqueue(&sp->event_queue, msg);
75 /* search timer handling */
76 static void gsm58_timer_timeout(void *arg)
78 struct gsm322_58_selproc *sp = arg;
81 /* indicate BCCH selection T timeout */
82 nmsg = gsm58_msgb_alloc(GSM58_EVENT_TIMEOUT);
85 gsm58_sendmsg(ms, nmsg);
88 static void gsm58_timer_start(struct gsm58_selproc *sp, int secs, int micro)
90 DEBUGP(DLC, "starting FREQUENCY search timer\n");
91 sp->timer.cb = gsm58_timer_timeout;
93 bsc_schedule_timer(&sp->timer, secs, micro);
96 static void gsm58_timer_stop(struct gsm58_selproc *plmn)
98 if (timer_pending(&sp->timer)) {
99 DEBUGP(DLC, "stopping pending timer\n");
100 bsc_del_timer(&sp->timer);
108 /* select first/next frequency */
109 static int gsm58_select_bcch(struct osmocom_ms *ms, int next)
111 struct gsm_support *s = &ms->support;
112 struct gsm58_selproc *sp = &ms->selproc;
119 for (i = 0, i < 1024, i++) {
120 if ((sp->ba[i >> 3] & (1 << (i & 7)))) {
121 if (j == sp->cur_freq)
129 DEBUGP(DLC, "Cycled through all %d frequencies in list.\n", j);
130 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_F);
133 gsm322_sendmsg(ms, nmsg);
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);
143 /* set current BCCH frequency */
145 DEBUGP(DLC, "Frequency %d selected, wait for sync.\n", sp->arfcn);
146 tx_ph_bcch_req(ms, sp->arfcn);
148 /* start timer for synchronizanation */
149 gsm58_timer_start(sp, 0, 500000);
151 sp->mode = GSM58_MODE_SYNC;
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)
159 struct gsm58_selproc *sp = &ms->selproc;
160 struct gsm_support *s = &ms->support;
161 struct gsm58_msg *gm = msgb->data;
164 memset(sp, 0, sizeof(*sp));
166 /* use all frequencies from supported frequency map */
167 memcpy(sp->ba, s->freq_map, sizeof(sp->ba));
169 /* limit process to given PLMN */
173 /* select first channel */
174 gsm58_select_bcch(ms, 0);
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)
180 struct gsm58_selproc *sp = &ms->selproc;
181 struct gsm_support *s = &ms->support;
182 struct gsm58_msg *gm = msgb->data;
185 memset(sp, 0, sizeof(*sp));
187 /* use all frequencies from given frequency map */
188 memcpy(sp->ba, sp->ba, sizeof(sp->ba));
190 /* limit process to given PLMN */
194 /* select first channel */
195 gsm58_select_bcch(ms, 0);
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)
201 struct gsm58_selproc *sp = &ms->selproc;
202 struct gsm_support *s = &ms->support;
203 struct gsm58_msg *gm = msgb->data;
206 memset(sp, 0, sizeof(*sp));
208 /* allow any cell not barred */
211 /* use all frequencies from supported frequency map */
212 memcpy(sp->ba, s->freq_map, sizeof(sp->ba));
214 /* select first channel */
215 gsm58_select_bcch(ms, 0);
218 /* timeout while selecting BCCH */
219 static int gsm58_sel_timeout(struct osmocom_ms *ms, struct msgb *msg)
221 struct gsm58_selproc *sp = &ms->selproc;
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");
228 case GSM58_MODE_READ:
229 /* timeout while reading BCCH */
230 DEBUGP(DLC, "BCC reading failed, selecting next frq.\n");
233 DEBUGP(DLC, "Timeout in wrong mode, please fix!\n");
236 gsm58_select_bcch(ms, 1);
241 /* we are synchronized to selecting BCCH */
242 static int gsm58_sel_sync(struct osmocom_ms *ms, struct msgb *msg)
244 struct gsm58_selproc *sp = &ms->selproc;
245 struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
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);
254 DEBUGP(DLC, "Synced to %d, waiting for relevant data.\n", sp->arfcn);
256 /* set timer for reading BCCH */
257 gsm58_timer_start(sp, 4, 0); // TODO: timer depends on BCCH configur.
259 /* reset sysinfo and wait for relevant data */
260 gsm_sysinfo_init(ms);
261 sp->mode = GSM58_MODE_READ;
266 /* we are getting sysinfo from BCCH */
267 static int gsm58_sel_sysinfo(struct osmocom_ms *ms, struct msgb *msg)
269 struct gsm58_selproc *sp = &ms->selproc;
270 struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
271 struct gsm322_msg *ngm;
275 /* ignore system info, if not synced */
276 if (sp->mode != GSM58_MODE_DATA && sp->mode != GSM58_MODE_CAMP)
279 /* check if cell is barred for us */
280 if (!subscr->barred_access && s->cell_barred)
283 if (s->class_barr[10])
286 for (i = 0, i <= 15, i++)
287 if (subscr->class_access[i] && !s->class_barr[i])
294 if (sp->mode == GSM58_MODE_CAMP) {
295 /* cell has become barred */
297 DEBUGP(DLC, "Cell has become barred, starting "
300 sp->mode = GSM58_MODE_IDLE;
302 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
305 gsm322_sendmsg(ms, nmsg);
313 /* can't use barred cell */
315 DEBUGP(DLC, "Selected cell is barred, select next.\n");
316 gsm58_timer_stop(sp);
317 gsm58_select_bcch(ms, 1);
322 /* if PLMN not yet indicated, but required if not any cell selection */
323 if (!sp->any && !s->mcc)
326 // todo: what else do we need until we can camp?
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);
337 /* all relevant informations received */
338 gsm58_timer_stop(sp);
339 sp->mode = GSM58_MODE_CAMP;
341 DEBUGP(DCS, "Cell with freq %d, mcc = %d, mnc = %d selected.\n",
342 sp->arfcn, s->mcc, s->mnc);
345 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
348 ngm = (struct gsm322_msg *)nmsg->data;
352 gsm322_sendmsg(ms, nmsg);
361 /* receive events for GSM 05.08 processes */
362 static int gsm58_event(struct osmocom_ms *ms, struct msgb *msg)
364 struct gsm58_msg *gm = (struct gsm58_msg *)msgb->data;
365 int msg_type = gm->msg_type;
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]);
372 case GSM58_EVENT_START_NORMAL:
373 gsm58_start_normal_sel(ms, msg);
375 case GSM58_EVENT_START_STORED:
376 gsm58_start_stored_sel(ms, msg);
378 case GSM58_EVENT_START_ANY:
379 gsm58_start_any_sel(ms, msg);
381 case GSM58_EVENT_TIMEOUT:
382 gsm58_sel_timeout(ms, msg);
384 case GSM58_EVENT_SYNC:
385 gsm58_sel_sync(ms, msg);
387 case GSM58_EVENT_SYSINFO:
388 gsm58_sel_sysinfo(ms, msg);
391 DEBUGP(DLC, "Message unhandled.\n");
397 /* dequeue GSM 05.08 events */
398 int gsm58_event_queue(struct osmocom_ms *ms)
400 struct gsm58_selproc *sp = &ms->selproc;
404 while ((msg = msgb_dequeue(&sp->event_queue))) {
405 gsm58_event(ms, msg);
407 work = 1; /* work done */