fw/layer1: Fix compiler warning about mismatched ptr types
[osmocom-bb.git] / src / target / firmware / layer1 / l23_api.c
1 /* Synchronous part of GSM Layer 1: API to Layer2+ */
2
3 /* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4  *
5  * All Rights Reserved
6  *
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.
11  *
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.
16  *
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.
20  *
21  */
22
23 #define DEBUG
24
25 #include <stdint.h>
26 #include <stdio.h>
27
28 #include <debug.h>
29 #include <byteorder.h>
30
31 #include <osmocore/msgb.h>
32 #include <comm/sercomm.h>
33
34 #include <layer1/sync.h>
35 #include <layer1/async.h>
36 #include <layer1/mframe_sched.h>
37 #include <layer1/tpu_window.h>
38
39 #include <rf/trf6151.h>
40
41 #include <l1a_l23_interface.h>
42
43 /* the size we will allocate struct msgb* for HDLC */
44 #define L3_MSG_HEAD 4
45 #define L3_MSG_SIZE (sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_data_ind) + L3_MSG_HEAD)
46
47 void l1_queue_for_l2(struct msgb *msg)
48 {
49         /* forward via serial for now */
50         sercomm_sendmsg(SC_DLCI_L1A_L23, msg);
51 }
52
53 static enum mframe_task chan_nr2mf_task(uint8_t chan_nr)
54 {
55         uint8_t cbits = chan_nr >> 3;
56         uint8_t lch_idx;
57
58         if (cbits == 0x01) {
59                 lch_idx = 0;
60                 /* FIXME: TCH/F */
61         } else if ((cbits & 0x1e) == 0x02) {
62                 lch_idx = cbits & 0x1;
63                 /* FIXME: TCH/H */
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;
70 #if 0
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;
77 #endif
78         }
79         return 0;
80 }
81
82 struct msgb *l1ctl_msgb_alloc(uint8_t msg_type)
83 {
84         struct msgb *msg;
85         struct l1ctl_hdr *l1h;
86
87         msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1ctl");
88         if (!msg) {
89                 while (1) {
90                         puts("OOPS. Out of buffers...\n");
91                 }
92
93                 return NULL;
94         }
95         l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h));
96         l1h->msg_type = msg_type;
97         l1h->flags = 0;
98
99         msg->l1h = (uint8_t *)l1h;
100
101         return msg;
102 }
103
104 struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr,
105                               uint16_t arfcn)
106 {
107         struct l1ctl_info_dl *dl;
108         struct msgb *msg = l1ctl_msgb_alloc(msg_type);
109
110         dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
111         dl->frame_nr = htonl(fn);
112         dl->snr = snr;
113         dl->band_arfcn = htons(arfcn);
114
115         return msg;
116 }
117
118 /* receive a L1CTL_PM_REQ from L23 */
119 void l1ctl_rx_pm_req(struct msgb *msg)
120 {
121         struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
122         struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data;
123
124         switch (pm_req->type) {
125         case 1:
126                 l1s.pm.mode = 1;
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);
135                 break;
136         }
137
138         l1s_pm_test(1, l1s.pm.range.arfcn_next);
139 }
140
141 /* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */
142 void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type)
143 {
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;
149
150         l1_queue_for_l2(msg);
151 }
152
153 /* receive a L1CTL_RESET_REQ from L23 */
154 static void l1ctl_rx_reset_req(struct msgb *msg)
155 {
156         struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
157         struct l1ctl_reset *reset_req =
158                                 (struct l1ctl_reset *) l1h->data;
159
160         switch (reset_req->type) {
161         case L1CTL_RES_T_FULL:
162                 printf("L1CTL_RESET_REQ: FULL!\n");
163                 l1s_reset();
164                 l1s_reset_hw();
165                 l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
166                 break;
167         default:
168                 printf("unknown L1CTL_RESET_REQ type\n");
169                 break;
170         }
171 }
172
173 /* Transmit a L1CTL_CCCH_MODE_CONF */
174 static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode)
175 {
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;
181
182         l1_queue_for_l2(msg);
183 }
184
185 /* receive a L1CTL_CCCH_MODE_REQ from L23 */
186 static void l1ctl_rx_ccch_mode_req(struct msgb *msg)
187 {
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;
192
193         /* pre-set the CCCH mode */
194         l1s.serving_cell.ccch_mode = ccch_mode;
195
196         /* Update task */
197         mframe_disable(MF_TASK_CCCH_COMB);
198         mframe_disable(MF_TASK_CCCH);
199
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);
204
205         l1ctl_tx_ccch_mode_conf(ccch_mode);
206 }
207
208 /* callback from SERCOMM when L2 sends a message to L1 */
209 static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
210 {
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;
218 #if 0
219         {
220                 int i;
221                 printf("l1a_l23_rx_cb (%u): ", msg->len);
222                 for (i = 0; i < msg->len; i++)
223                         printf("%02x ", msg->data[i]);
224                 puts("\n");
225         }
226 #endif
227         msg->l1h = msg->data;
228
229         if (sizeof(*l1h) > msg->len) {
230                 printf("l1a_l23_cb: Short message. %u\n", msg->len);
231                 goto exit_msgbfree;
232         }
233
234         switch (l1h->msg_type) {
235         case L1CTL_FBSB_REQ:
236                 if (sizeof(*sync_req) > msg->len) {
237                         printf("Short sync msg. %u\n", msg->len);
238                         break;
239                 }
240
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);
244
245                 /* reset scheduler and hardware */
246                 l1s_reset();
247
248                 /* tune to specified frequency */
249                 trf6151_rx_window(0, ntohs(sync_req->band_arfcn), 40, 0);
250                 tpu_end_scenario();
251
252                 /* pre-set the CCCH mode */
253                 l1s.serving_cell.ccch_mode = sync_req->ccch_mode;
254
255                 printd("Starting FCCH Recognition\n");
256                 l1s_fbsb_req(1, sync_req);
257                 break;
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) {
263                         /* FIXME: ARFCN */
264                         puts("We don't support ARFCN switches yet\n");
265                         break;
266                 }
267                 if (ul->chan_nr & 0x7) {
268                         /* FIXME: Timeslot */
269                         puts("We don't support non-0 TS yet\n");
270                         break;
271                 }
272                 if (est_req->h0.h) {
273                         puts("We don't support frequency hopping yet\n");
274                         break;
275                 }
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));
279                 break;
280         case L1CTL_RACH_REQ:
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);
284                 break;
285         case L1CTL_DATA_REQ:
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];
290                 else
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! */
297                 goto exit_nofree;
298         case L1CTL_PM_REQ:
299                 l1ctl_rx_pm_req(msg);
300                 break;
301         case L1CTL_RESET_REQ:
302                 l1ctl_rx_reset_req(msg);
303                 break;
304         case L1CTL_CCCH_MODE_REQ:
305                 l1ctl_rx_ccch_mode_req(msg);
306                 break;
307         }
308
309 exit_msgbfree:
310         msgb_free(msg);
311 exit_nofree:
312         return;
313 }
314
315 void l1a_l23api_init(void)
316 {
317         sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
318 }