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.
29 #include <byteorder.h>
31 #include <osmocore/msgb.h>
32 #include <comm/sercomm.h>
34 #include <layer1/sync.h>
35 #include <layer1/async.h>
36 #include <layer1/mframe_sched.h>
37 #include <layer1/tpu_window.h>
39 #include <rf/trf6151.h>
41 #include <l1a_l23_interface.h>
43 /* the size we will allocate struct msgb* for HDLC */
45 #define L3_MSG_SIZE (sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_data_ind) + L3_MSG_HEAD)
47 void l1_queue_for_l2(struct msgb *msg)
49 /* forward via serial for now */
50 sercomm_sendmsg(SC_DLCI_L1A_L23, msg);
53 static enum mframe_task chan_nr2mf_task(uint8_t chan_nr)
55 uint8_t cbits = chan_nr >> 3;
61 } else if ((cbits & 0x1e) == 0x02) {
62 lch_idx = cbits & 0x1;
64 } else if ((cbits & 0x1c) == 0x04) {
65 lch_idx = cbits & 0x3;
66 return MF_TASK_SDCCH4_0 + lch_idx;
67 } else if ((cbits & 0x18) == 0x08) {
68 lch_idx = cbits & 0x7;
69 return MF_TASK_SDCCH8_0 + lch_idx;
71 } else if (cbits == 0x10) {
72 /* FIXME: when to do extended BCCH? */
73 return MF_TASK_BCCH_NORM;
74 } else if (cbits == 0x11 || cbits == 0x12) {
75 /* FIXME: how to decide CCCH norm/extd? */
76 return MF_TASK_BCCH_CCCH;
82 struct msgb *l1ctl_msgb_alloc(uint8_t msg_type)
85 struct l1ctl_hdr *l1h;
87 msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1ctl");
90 puts("OOPS. Out of buffers...\n");
95 l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h));
96 l1h->msg_type = msg_type;
99 msg->l1h = (uint8_t *)l1h;
104 struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr,
107 struct l1ctl_info_dl *dl;
108 struct msgb *msg = l1ctl_msgb_alloc(msg_type);
110 dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
111 dl->frame_nr = htonl(fn);
113 dl->band_arfcn = htons(arfcn);
118 /* receive a L1CTL_PM_REQ from L23 */
119 void l1ctl_rx_pm_req(struct msgb *msg)
121 struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
122 struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data;
124 switch (pm_req->type) {
127 l1s.pm.range.arfcn_start =
128 ntohs(pm_req->range.band_arfcn_from);
129 l1s.pm.range.arfcn_next =
130 ntohs(pm_req->range.band_arfcn_from);
131 l1s.pm.range.arfcn_end =
132 ntohs(pm_req->range.band_arfcn_to);
133 printf("L1CTL_PM_REQ start=%u end=%u\n",
134 l1s.pm.range.arfcn_start, l1s.pm.range.arfcn_end);
138 l1s_pm_test(1, l1s.pm.range.arfcn_next);
141 /* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */
142 void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type)
144 struct msgb *msg = l1ctl_msgb_alloc(msg_type);
145 struct l1ctl_reset *reset_resp;
146 reset_resp = (struct l1ctl_reset *)
147 msgb_put(msg, sizeof(*reset_resp));
148 reset_resp->type = reset_type;
150 l1_queue_for_l2(msg);
153 /* receive a L1CTL_RESET_REQ from L23 */
154 static void l1ctl_rx_reset_req(struct msgb *msg)
156 struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
157 struct l1ctl_reset *reset_req =
158 (struct l1ctl_reset *) l1h->data;
160 switch (reset_req->type) {
161 case L1CTL_RES_T_FULL:
162 printf("L1CTL_RESET_REQ: FULL!\n");
165 l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
168 printf("unknown L1CTL_RESET_REQ type\n");
173 /* Transmit a L1CTL_CCCH_MODE_CONF */
174 static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode)
176 struct msgb *msg = l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF);
177 struct l1ctl_ccch_mode_conf *mode_conf;
178 mode_conf = (struct l1ctl_ccch_mode_conf *)
179 msgb_put(msg, sizeof(*mode_conf));
180 mode_conf->ccch_mode = ccch_mode;
182 l1_queue_for_l2(msg);
185 /* receive a L1CTL_CCCH_MODE_REQ from L23 */
186 static void l1ctl_rx_ccch_mode_req(struct msgb *msg)
188 struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
189 struct l1ctl_ccch_mode_req *ccch_mode_req =
190 (struct l1ctl_ccch_mode_req *) l1h->data;
191 uint8_t ccch_mode = ccch_mode_req->ccch_mode;
193 /* pre-set the CCCH mode */
194 l1s.serving_cell.ccch_mode = ccch_mode;
197 mframe_disable(MF_TASK_CCCH_COMB);
198 mframe_disable(MF_TASK_CCCH);
200 if (ccch_mode == CCCH_MODE_COMBINED)
201 mframe_enable(MF_TASK_CCCH_COMB);
202 else if (ccch_mode == CCCH_MODE_NON_COMBINED)
203 mframe_enable(MF_TASK_CCCH);
205 l1ctl_tx_ccch_mode_conf(ccch_mode);
208 /* callback from SERCOMM when L2 sends a message to L1 */
209 static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
211 struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
212 struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
213 struct l1ctl_fbsb_req *sync_req;
214 struct l1ctl_rach_req *rach_req;
215 struct l1ctl_dm_est_req *est_req;
216 struct l1ctl_data_ind *data_ind;
217 struct llist_head *tx_queue;
221 printf("l1a_l23_rx_cb (%u): ", msg->len);
222 for (i = 0; i < msg->len; i++)
223 printf("%02x ", msg->data[i]);
227 msg->l1h = msg->data;
229 if (sizeof(*l1h) > msg->len) {
230 printf("l1a_l23_cb: Short message. %u\n", msg->len);
234 switch (l1h->msg_type) {
236 if (sizeof(*sync_req) > msg->len) {
237 printf("Short sync msg. %u\n", msg->len);
241 sync_req = (struct l1ctl_fbsb_req *) l1h->data;
242 printd("L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n",
243 ntohs(sync_req->band_arfcn), sync_req->flags);
245 /* reset scheduler and hardware */
248 /* tune to specified frequency */
249 trf6151_rx_window(0, ntohs(sync_req->band_arfcn), 40, 0);
252 /* pre-set the CCCH mode */
253 l1s.serving_cell.ccch_mode = sync_req->ccch_mode;
255 printd("Starting FCCH Recognition\n");
256 l1s_fbsb_req(1, sync_req);
258 case L1CTL_DM_EST_REQ:
259 est_req = (struct l1ctl_dm_est_req *) ul->payload;
260 printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x)\n",
261 ntohs(est_req->band_arfcn), ul->chan_nr);
262 if (ntohs(est_req->band_arfcn) != l1s.serving_cell.arfcn) {
264 puts("We don't support ARFCN switches yet\n");
267 if (ul->chan_nr & 0x7) {
268 /* FIXME: Timeslot */
269 puts("We don't support non-0 TS yet\n");
273 puts("We don't support frequency hopping yet\n");
276 /* FIXME: set TSC of ded chan according to est_req.h0.tsc */
277 /* figure out which MF tasks to enable */
278 l1a_mftask_set(1 << chan_nr2mf_task(ul->chan_nr));
281 rach_req = (struct l1ctl_rach_req *) ul->payload;
282 printd("L1CTL_RACH_REQ (ra=0x%02x)\n", rach_req->ra);
283 l1a_rach_req(27, rach_req->ra);
286 data_ind = (struct l1ctl_data_ind *) ul->payload;
287 printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id);
288 if (ul->link_id & 0x40)
289 tx_queue = &l1s.tx_queue[L1S_CHAN_SACCH];
291 tx_queue = &l1s.tx_queue[L1S_CHAN_MAIN];
292 msg->l3h = data_ind->data;
293 printd("ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p\n",
294 ul, ul->payload, data_ind, data_ind->data, msg->l3h);
295 l1a_txq_msgb_enq(tx_queue, msg);
296 /* we have to keep the msgb, not free it! */
299 l1ctl_rx_pm_req(msg);
301 case L1CTL_RESET_REQ:
302 l1ctl_rx_reset_req(msg);
304 case L1CTL_CCCH_MODE_REQ:
305 l1ctl_rx_ccch_mode_req(msg);
315 void l1a_l23api_init(void)
317 sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);