1 /* Synchronous part of GSM Layer 1: API to Layer2+ */
3 /* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <byteorder.h>
32 #include <asm/system.h>
34 #include <osmocom/core/msgb.h>
35 #include <osmocom/gsm/protocol/gsm_04_08.h>
36 #include <comm/sercomm.h>
38 #include <layer1/sync.h>
39 #include <layer1/async.h>
40 #include <layer1/mframe_sched.h>
41 #include <layer1/prim.h>
42 #include <layer1/tpu_window.h>
44 #include <abb/twl3025.h>
45 #include <rf/trf6151.h>
46 #include <calypso/sim.h>
48 #include <l1ctl_proto.h>
50 /* the size we will allocate struct msgb* for HDLC */
52 #define L3_MSG_SIZE (sizeof(struct l1ctl_hdr)+sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_traffic_ind) + L3_MSG_HEAD)
54 void (*l1a_l23_tx_cb
)(struct msgb
*msg
) = NULL
;
56 void l1_queue_for_l2(struct msgb
*msg
)
62 /* forward via serial for now */
63 sercomm_sendmsg(SC_DLCI_L1A_L23
, msg
);
72 static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr
, uint8_t neigh_mode
)
74 uint8_t cbits
= chan_nr
>> 3;
75 uint8_t tn
= chan_nr
& 0x7;
77 enum mframe_task master_task
= 0;
78 uint32_t neigh_task
= 0;
79 enum mf_type multiframe
;
83 master_task
= (tn
& 1) ? MF_TASK_TCH_F_ODD
: MF_TASK_TCH_F_EVEN
;
84 multiframe
= (tn
& 1) ? MF26ODD
: MF26EVEN
;
85 } else if ((cbits
& 0x1e) == 0x02) {
86 lch_idx
= cbits
& 0x1;
87 master_task
= MF_TASK_TCH_H_0
+ lch_idx
;
88 } else if ((cbits
& 0x1c) == 0x04) {
89 lch_idx
= cbits
& 0x3;
90 master_task
= MF_TASK_SDCCH4_0
+ lch_idx
;
92 } else if ((cbits
& 0x18) == 0x08) {
93 lch_idx
= cbits
& 0x7;
94 master_task
= MF_TASK_SDCCH8_0
+ lch_idx
;
97 } else if (cbits
== 0x10) {
98 /* FIXME: when to do extended BCCH? */
99 master_task
= MF_TASK_BCCH_NORM
;
100 } else if (cbits
== 0x11 || cbits
== 0x12) {
101 /* FIXME: how to decide CCCH norm/extd? */
102 master_task
= MF_TASK_BCCH_CCCH
;
105 switch (neigh_mode
) {
107 switch (multiframe
) {
109 neigh_task
= (1 << MF_TASK_NEIGH_PM51
);
112 neigh_task
= (1 << MF_TASK_NEIGH_PM26E
);
115 neigh_task
= (1 << MF_TASK_NEIGH_PM26O
);
120 return (1 << master_task
) | neigh_task
;
123 static int chan_nr2dchan_type(uint8_t chan_nr
)
125 uint8_t cbits
= chan_nr
>> 3;
128 return GSM_DCHAN_TCH_F
;
129 } else if ((cbits
& 0x1e) == 0x02) {
130 return GSM_DCHAN_TCH_H
;
131 } else if ((cbits
& 0x1c) == 0x04) {
132 return GSM_DCHAN_SDCCH_4
;
133 } else if ((cbits
& 0x18) == 0x08) {
134 return GSM_DCHAN_SDCCH_8
;
136 return GSM_DCHAN_UNKNOWN
;
139 static int chan_nr_is_tch(uint8_t chan_nr
)
141 return ((chan_nr
>> 3) == 0x01 || /* TCH/F */
142 ((chan_nr
>> 3) & 0x1e) == 0x02); /* TCH/H */
145 static void audio_set_enabled(uint8_t tch_mode
, uint8_t audio_mode
)
147 if (tch_mode
== GSM48_CMODE_SIGN
) {
148 twl3025_unit_enable(TWL3025_UNIT_VUL
, 0);
149 twl3025_unit_enable(TWL3025_UNIT_VDL
, 0);
151 twl3025_unit_enable(TWL3025_UNIT_VUL
,
152 !!(audio_mode
& AUDIO_TX_MICROPHONE
));
153 twl3025_unit_enable(TWL3025_UNIT_VDL
,
154 !!(audio_mode
& AUDIO_RX_SPEAKER
));
158 struct msgb
*l1ctl_msgb_alloc(uint8_t msg_type
)
161 struct l1ctl_hdr
*l1h
;
163 msg
= msgb_alloc_headroom(L3_MSG_SIZE
, L3_MSG_HEAD
, "l1ctl");
166 puts("OOPS. Out of buffers...\n");
171 l1h
= (struct l1ctl_hdr
*) msgb_put(msg
, sizeof(*l1h
));
172 l1h
->msg_type
= msg_type
;
175 msg
->l1h
= (uint8_t *)l1h
;
180 struct msgb
*l1_create_l2_msg(int msg_type
, uint32_t fn
, uint16_t snr
,
183 struct l1ctl_info_dl
*dl
;
184 struct msgb
*msg
= l1ctl_msgb_alloc(msg_type
);
186 dl
= (struct l1ctl_info_dl
*) msgb_put(msg
, sizeof(*dl
));
187 dl
->frame_nr
= htonl(fn
);
189 dl
->band_arfcn
= htons(arfcn
);
194 /* receive a L1CTL_FBSB_REQ from L23 */
195 static void l1ctl_rx_fbsb_req(struct msgb
*msg
)
197 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
198 struct l1ctl_fbsb_req
*sync_req
= (struct l1ctl_fbsb_req
*) l1h
->data
;
200 if (sizeof(*sync_req
) > msg
->len
) {
201 printf("Short sync msg. %u\n", msg
->len
);
205 printd("L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n",
206 ntohs(sync_req
->band_arfcn
), sync_req
->flags
);
208 /* reset scheduler and hardware */
211 /* pre-set the CCCH mode */
212 l1s
.serving_cell
.ccch_mode
= sync_req
->ccch_mode
;
214 printd("Starting FCCH Recognition\n");
215 l1s_fbsb_req(1, sync_req
);
218 /* receive a L1CTL_DM_EST_REQ from L23 */
219 static void l1ctl_rx_dm_est_req(struct msgb
*msg
)
221 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
222 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
223 struct l1ctl_dm_est_req
*est_req
= (struct l1ctl_dm_est_req
*) ul
->payload
;
225 printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n",
226 ntohs(est_req
->h0
.band_arfcn
), ul
->chan_nr
, est_req
->tsc
);
228 /* disable neighbour cell measurement */
229 mframe_disable(MF_TASK_NEIGH_PM51
);
231 /* configure dedicated channel state */
232 l1s
.dedicated
.type
= chan_nr2dchan_type(ul
->chan_nr
);
233 l1s
.dedicated
.tsc
= est_req
->tsc
;
234 l1s
.dedicated
.tn
= ul
->chan_nr
& 0x7;
235 l1s
.dedicated
.h
= est_req
->h
;
239 l1s
.dedicated
.h1
.hsn
= est_req
->h1
.hsn
;
240 l1s
.dedicated
.h1
.maio
= est_req
->h1
.maio
;
241 l1s
.dedicated
.h1
.n
= est_req
->h1
.n
;
242 for (i
=0; i
<est_req
->h1
.n
; i
++)
243 l1s
.dedicated
.h1
.ma
[i
] = ntohs(est_req
->h1
.ma
[i
]);
245 l1s
.dedicated
.h0
.arfcn
= ntohs(est_req
->h0
.band_arfcn
);
249 if (chan_nr_is_tch(ul
->chan_nr
)) {
251 l1a_tch_mode_set(est_req
->tch_mode
);
252 l1a_audio_mode_set(est_req
->audio_mode
);
255 l1s
.tch_sync
= 1; /* can be set without locking */
258 audio_set_enabled(est_req
->tch_mode
, est_req
->audio_mode
);
261 /* figure out which MF tasks to enable */
262 l1a_mftask_set(chan_nr2mf_task_mask(ul
->chan_nr
, NEIGH_MODE_PM
));
265 /* receive a L1CTL_DM_FREQ_REQ from L23 */
266 static void l1ctl_rx_dm_freq_req(struct msgb
*msg
)
268 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
269 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
270 struct l1ctl_dm_freq_req
*freq_req
=
271 (struct l1ctl_dm_freq_req
*) ul
->payload
;
273 printd("L1CTL_DM_FREQ_REQ (arfcn=%u, tsc=%u)\n",
274 ntohs(freq_req
->h0
.band_arfcn
), freq_req
->tsc
);
276 /* configure dedicated channel state */
277 l1s
.dedicated
.st_tsc
= freq_req
->tsc
;
278 l1s
.dedicated
.st_h
= freq_req
->h
;
282 l1s
.dedicated
.st_h1
.hsn
= freq_req
->h1
.hsn
;
283 l1s
.dedicated
.st_h1
.maio
= freq_req
->h1
.maio
;
284 l1s
.dedicated
.st_h1
.n
= freq_req
->h1
.n
;
285 for (i
=0; i
<freq_req
->h1
.n
; i
++)
286 l1s
.dedicated
.st_h1
.ma
[i
] = ntohs(freq_req
->h1
.ma
[i
]);
288 l1s
.dedicated
.st_h0
.arfcn
= ntohs(freq_req
->h0
.band_arfcn
);
291 l1a_freq_req(ntohs(freq_req
->fn
));
294 /* receive a L1CTL_CRYPTO_REQ from L23 */
295 static void l1ctl_rx_crypto_req(struct msgb
*msg
)
297 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
298 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
299 struct l1ctl_crypto_req
*cr
= (struct l1ctl_crypto_req
*) ul
->payload
;
300 uint8_t key_len
= msg
->len
- sizeof(*l1h
) - sizeof(*ul
) - sizeof(*cr
);
302 printd("L1CTL_CRYPTO_REQ (algo=A5/%u, len=%u)\n", cr
->algo
, key_len
);
304 if (cr
->algo
&& key_len
!= 8) {
305 printd("L1CTL_CRYPTO_REQ -> Invalid key\n");
309 dsp_load_ciph_param(cr
->algo
, cr
->key
);
312 /* receive a L1CTL_DM_REL_REQ from L23 */
313 static void l1ctl_rx_dm_rel_req(struct msgb
*msg
)
315 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
316 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
318 printd("L1CTL_DM_REL_REQ\n");
320 l1s
.dedicated
.type
= GSM_DCHAN_NONE
;
321 l1a_txq_msgb_flush(&l1s
.tx_queue
[L1S_CHAN_MAIN
]);
322 l1a_txq_msgb_flush(&l1s
.tx_queue
[L1S_CHAN_SACCH
]);
323 l1a_txq_msgb_flush(&l1s
.tx_queue
[L1S_CHAN_TRAFFIC
]);
324 l1a_meas_msgb_set(NULL
);
325 dsp_load_ciph_param(0, NULL
);
326 l1a_tch_mode_set(GSM48_CMODE_SIGN
);
327 audio_set_enabled(GSM48_CMODE_SIGN
, 0);
331 /* receive a L1CTL_PARAM_REQ from L23 */
332 static void l1ctl_rx_param_req(struct msgb
*msg
)
334 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
335 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
336 struct l1ctl_par_req
*par_req
= (struct l1ctl_par_req
*) ul
->payload
;
338 printd("L1CTL_PARAM_REQ (ta=%d, tx_power=%d)\n", par_req
->ta
,
341 l1s
.ta
= par_req
->ta
;
342 l1s
.tx_power
= par_req
->tx_power
;
345 /* receive a L1CTL_RACH_REQ from L23 */
346 static void l1ctl_rx_rach_req(struct msgb
*msg
)
348 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
349 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
350 struct l1ctl_rach_req
*rach_req
= (struct l1ctl_rach_req
*) ul
->payload
;
352 printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n",
353 rach_req
->ra
, ntohs(rach_req
->offset
), rach_req
->combined
);
355 l1a_rach_req(ntohs(rach_req
->offset
), rach_req
->combined
,
359 /* receive a L1CTL_DATA_REQ from L23 */
360 static void l1ctl_rx_data_req(struct msgb
*msg
)
362 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
363 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
364 struct l1ctl_data_ind
*data_ind
= (struct l1ctl_data_ind
*) ul
->payload
;
365 struct llist_head
*tx_queue
;
367 printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul
->link_id
);
369 msg
->l3h
= data_ind
->data
;
370 if (ul
->link_id
& 0x40) {
371 struct gsm48_hdr
*gh
= (struct gsm48_hdr
*)(data_ind
->data
+ 5);
372 if (gh
->proto_discr
== GSM48_PDISC_RR
373 && gh
->msg_type
== GSM48_MT_RR_MEAS_REP
) {
374 printd("updating measurement report\n");
375 l1a_meas_msgb_set(msg
);
378 tx_queue
= &l1s
.tx_queue
[L1S_CHAN_SACCH
];
380 tx_queue
= &l1s
.tx_queue
[L1S_CHAN_MAIN
];
382 printd("ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p\n",
383 ul
, ul
->payload
, data_ind
, data_ind
->data
, msg
->l3h
);
385 l1a_txq_msgb_enq(tx_queue
, msg
);
388 /* receive a L1CTL_PM_REQ from L23 */
389 static void l1ctl_rx_pm_req(struct msgb
*msg
)
391 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
392 struct l1ctl_pm_req
*pm_req
= (struct l1ctl_pm_req
*) l1h
->data
;
394 switch (pm_req
->type
) {
397 l1s
.pm
.range
.arfcn_start
=
398 ntohs(pm_req
->range
.band_arfcn_from
);
399 l1s
.pm
.range
.arfcn_next
=
400 ntohs(pm_req
->range
.band_arfcn_from
);
401 l1s
.pm
.range
.arfcn_end
=
402 ntohs(pm_req
->range
.band_arfcn_to
);
403 printf("L1CTL_PM_REQ start=%u end=%u\n",
404 l1s
.pm
.range
.arfcn_start
, l1s
.pm
.range
.arfcn_end
);
407 l1s_reset_hw(); /* must reset, otherwise measurement results are delayed */
408 l1s_pm_test(1, l1s
.pm
.range
.arfcn_next
);
411 /* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */
412 void l1ctl_tx_reset(uint8_t msg_type
, uint8_t reset_type
)
414 struct msgb
*msg
= l1ctl_msgb_alloc(msg_type
);
415 struct l1ctl_reset
*reset_resp
;
416 reset_resp
= (struct l1ctl_reset
*)
417 msgb_put(msg
, sizeof(*reset_resp
));
418 reset_resp
->type
= reset_type
;
420 l1_queue_for_l2(msg
);
423 /* receive a L1CTL_RESET_REQ from L23 */
424 static void l1ctl_rx_reset_req(struct msgb
*msg
)
426 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
427 struct l1ctl_reset
*reset_req
=
428 (struct l1ctl_reset
*) l1h
->data
;
430 switch (reset_req
->type
) {
431 case L1CTL_RES_T_FULL
:
432 printf("L1CTL_RESET_REQ: FULL!\n");
435 audio_set_enabled(GSM48_CMODE_SIGN
, 0);
436 l1ctl_tx_reset(L1CTL_RESET_CONF
, reset_req
->type
);
438 case L1CTL_RES_T_SCHED
:
439 printf("L1CTL_RESET_REQ: SCHED!\n");
440 l1ctl_tx_reset(L1CTL_RESET_CONF
, reset_req
->type
);
441 sched_gsmtime_reset();
444 printf("unknown L1CTL_RESET_REQ type\n");
449 /* Transmit a L1CTL_CCCH_MODE_CONF */
450 static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode
)
452 struct msgb
*msg
= l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF
);
453 struct l1ctl_ccch_mode_conf
*mode_conf
;
454 mode_conf
= (struct l1ctl_ccch_mode_conf
*)
455 msgb_put(msg
, sizeof(*mode_conf
));
456 mode_conf
->ccch_mode
= ccch_mode
;
458 l1_queue_for_l2(msg
);
461 /* receive a L1CTL_CCCH_MODE_REQ from L23 */
462 static void l1ctl_rx_ccch_mode_req(struct msgb
*msg
)
464 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
465 struct l1ctl_ccch_mode_req
*ccch_mode_req
=
466 (struct l1ctl_ccch_mode_req
*) l1h
->data
;
467 uint8_t ccch_mode
= ccch_mode_req
->ccch_mode
;
469 /* pre-set the CCCH mode */
470 l1s
.serving_cell
.ccch_mode
= ccch_mode
;
473 mframe_disable(MF_TASK_CCCH_COMB
);
474 mframe_disable(MF_TASK_CCCH
);
476 if (ccch_mode
== CCCH_MODE_COMBINED
)
477 mframe_enable(MF_TASK_CCCH_COMB
);
478 else if (ccch_mode
== CCCH_MODE_NON_COMBINED
)
479 mframe_enable(MF_TASK_CCCH
);
481 l1ctl_tx_ccch_mode_conf(ccch_mode
);
484 /* Transmit a L1CTL_TCH_MODE_CONF */
485 static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode
, uint8_t audio_mode
)
487 struct msgb
*msg
= l1ctl_msgb_alloc(L1CTL_TCH_MODE_CONF
);
488 struct l1ctl_tch_mode_conf
*mode_conf
;
489 mode_conf
= (struct l1ctl_tch_mode_conf
*)
490 msgb_put(msg
, sizeof(*mode_conf
));
491 mode_conf
->tch_mode
= tch_mode
;
492 mode_conf
->audio_mode
= audio_mode
;
494 l1_queue_for_l2(msg
);
497 /* receive a L1CTL_TCH_MODE_REQ from L23 */
498 static void l1ctl_rx_tch_mode_req(struct msgb
*msg
)
500 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
501 struct l1ctl_tch_mode_req
*tch_mode_req
=
502 (struct l1ctl_tch_mode_req
*) l1h
->data
;
503 uint8_t tch_mode
= tch_mode_req
->tch_mode
;
504 uint8_t audio_mode
= tch_mode_req
->audio_mode
;
506 printd("L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n",
507 tch_mode
, audio_mode
);
508 tch_mode
= l1a_tch_mode_set(tch_mode
);
509 audio_mode
= l1a_audio_mode_set(audio_mode
);
511 audio_set_enabled(tch_mode
, audio_mode
);
513 l1s
.tch_sync
= 1; /* Needed for audio to work */
515 l1ctl_tx_tch_mode_conf(tch_mode
, audio_mode
);
518 /* receive a L1CTL_NEIGH_PM_REQ from L23 */
519 static void l1ctl_rx_neigh_pm_req(struct msgb
*msg
)
521 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
522 struct l1ctl_neigh_pm_req
*pm_req
=
523 (struct l1ctl_neigh_pm_req
*) l1h
->data
;
526 /* reset list in order to prevent race condition */
527 l1s
.neigh_pm
.n
= 0; /* atomic */
528 l1s
.neigh_pm
.second
= 0;
529 /* now reset pointer and fill list */
530 l1s
.neigh_pm
.pos
= 0;
531 l1s
.neigh_pm
.running
= 0;
532 for (i
= 0; i
< pm_req
->n
; i
++)
533 l1s
.neigh_pm
.band_arfcn
[i
] = ntohs(pm_req
->band_arfcn
[i
]);
534 printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req
->n
);
535 l1s
.neigh_pm
.n
= pm_req
->n
; /* atomic */
537 /* on BCCH enable PM on frame 51 */
538 if (l1s
.dedicated
.type
== GSM_DCHAN_NONE
)
539 mframe_enable(MF_TASK_NEIGH_PM51
);
542 /* receive a L1CTL_TRAFFIC_REQ from L23 */
543 static void l1ctl_rx_traffic_req(struct msgb
*msg
)
545 struct l1ctl_hdr
*l1h
= (struct l1ctl_hdr
*) msg
->data
;
546 struct l1ctl_info_ul
*ul
= (struct l1ctl_info_ul
*) l1h
->data
;
547 struct l1ctl_traffic_req
*tr
= (struct l1ctl_traffic_req
*) ul
->payload
;
550 /* printd("L1CTL_TRAFFIC_REQ\n"); */ /* Very verbose, can overwelm serial */
554 num
= l1a_txq_msgb_count(&l1s
.tx_queue
[L1S_CHAN_TRAFFIC
]);
556 printd("dropping traffic frame\n");
561 l1a_txq_msgb_enq(&l1s
.tx_queue
[L1S_CHAN_TRAFFIC
], msg
);
564 void sim_apdu(uint16_t len
, uint8_t *data
);
566 static void l1ctl_sim_req(struct msgb
*msg
)
568 uint16_t len
= msg
->len
- sizeof(struct l1ctl_hdr
);
569 uint8_t *data
= msg
->data
+ sizeof(struct l1ctl_hdr
);
571 #if 1 /* for debugging only */
574 printf("SIM Request (%u): ", len
);
575 for (i
= 0; i
< len
; i
++)
576 printf("%02x ", data
[i
]);
584 static struct llist_head l23_rx_queue
= LLIST_HEAD_INIT(l23_rx_queue
);
586 /* callback from SERCOMM when L2 sends a message to L1 */
587 void l1a_l23_rx(uint8_t dlci
, struct msgb
*msg
)
591 local_firq_save(flags
);
592 msgb_enqueue(&l23_rx_queue
, msg
);
593 local_irq_restore(flags
);
596 void l1a_l23_handler(void)
599 struct l1ctl_hdr
*l1h
;
602 local_firq_save(flags
);
603 msg
= msgb_dequeue(&l23_rx_queue
);
604 local_irq_restore(flags
);
608 l1h
= (struct l1ctl_hdr
*) msg
->data
;
613 printf("l1a_l23_rx_cb (%u): ", msg
->len
);
614 for (i
= 0; i
< msg
->len
; i
++)
615 printf("%02x ", msg
->data
[i
]);
620 msg
->l1h
= msg
->data
;
622 if (sizeof(*l1h
) > msg
->len
) {
623 printf("l1a_l23_cb: Short message. %u\n", msg
->len
);
627 switch (l1h
->msg_type
) {
629 l1ctl_rx_fbsb_req(msg
);
631 case L1CTL_DM_EST_REQ
:
632 l1ctl_rx_dm_est_req(msg
);
634 case L1CTL_DM_REL_REQ
:
635 l1ctl_rx_dm_rel_req(msg
);
637 case L1CTL_PARAM_REQ
:
638 l1ctl_rx_param_req(msg
);
640 case L1CTL_DM_FREQ_REQ
:
641 l1ctl_rx_dm_freq_req(msg
);
643 case L1CTL_CRYPTO_REQ
:
644 l1ctl_rx_crypto_req(msg
);
647 l1ctl_rx_rach_req(msg
);
650 l1ctl_rx_data_req(msg
);
651 /* we have to keep the msgb, not free it! */
654 l1ctl_rx_pm_req(msg
);
656 case L1CTL_RESET_REQ
:
657 l1ctl_rx_reset_req(msg
);
659 case L1CTL_CCCH_MODE_REQ
:
660 l1ctl_rx_ccch_mode_req(msg
);
662 case L1CTL_TCH_MODE_REQ
:
663 l1ctl_rx_tch_mode_req(msg
);
665 case L1CTL_NEIGH_PM_REQ
:
666 l1ctl_rx_neigh_pm_req(msg
);
668 case L1CTL_TRAFFIC_REQ
:
669 l1ctl_rx_traffic_req(msg
);
670 /* we have to keep the msgb, not free it! */
683 void l1a_l23api_init(void)
685 sercomm_register_rx_cb(SC_DLCI_L1A_L23
, l1a_l23_rx
);