blob: 106d40bfc88b942b05e564bd3c803f9191758861 [file] [log] [blame]
Andreas Eversberg0aed6542012-06-23 10:33:16 +02001/* sysmo_l1_if.cpp
2 *
3 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <stdio.h>
21#include <unistd.h>
22#include <stdlib.h>
23#include <string.h>
24#include <errno.h>
25#include <assert.h>
26#include <sys/socket.h>
27#include <sys/un.h>
28extern "C" {
29#include <osmocom/core/talloc.h>
30#include <osmocom/core/select.h>
31#include <osmocom/core/msgb.h>
32}
33
34#include <gprs_rlcmac.h>
35#include <pcu_l1_if.h>
36#include <gprs_debug.h>
37#include "../../osmo-bts/include/osmo-bts/pcuif_proto.h"
38
39static int pcu_sock_send(struct msgb *msg);
40static void pcu_sock_timeout(void *_priv);
41
Andreas Eversberg0aed6542012-06-23 10:33:16 +020042// Variable for storage current FN.
43int frame_number;
44
45int get_current_fn()
46{
47 return frame_number;
48}
49
50void set_current_fn(int fn)
51{
52 frame_number = fn;
53}
54
55/*
56 * PCU messages
57 */
58
59struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
60{
61 struct msgb *msg;
62 struct gsm_pcu_if *pcu_prim;
63
64 msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
65 if (!msg)
66 return NULL;
67 msgb_put(msg, sizeof(struct gsm_pcu_if));
68 pcu_prim = (struct gsm_pcu_if *) msg->data;
69 pcu_prim->msg_type = msg_type;
70 pcu_prim->bts_nr = bts_nr;
71
72 return msg;
73}
74
75static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
76{
77 struct msgb *msg;
78 struct gsm_pcu_if *pcu_prim;
79 struct gsm_pcu_if_act_req *act_req;
80
81 LOGP(DL1IF, LOGL_INFO, "Sending %s request: trx=%d ts=%d\n",
82 (activate) ? "activate" : "deactivate", trx, ts);
83
84 msg = pcu_msgb_alloc(PCU_IF_MSG_ACT_REQ, 0);
85 if (!msg)
86 return -ENOMEM;
87 pcu_prim = (struct gsm_pcu_if *) msg->data;
88 act_req = &pcu_prim->u.act_req;
89 act_req->activate = activate;
90 act_req->trx_nr = trx;
91 act_req->ts_nr = ts;
92
93 return pcu_sock_send(msg);
94}
95
96static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
97 uint16_t arfcn, uint32_t fn, uint8_t block_nr, uint8_t *data,
98 uint8_t len)
99{
100 struct msgb *msg;
101 struct gsm_pcu_if *pcu_prim;
102 struct gsm_pcu_if_data *data_req;
103
104 LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
105 "arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
106 block_nr, osmo_hexdump(data, len));
107
108 msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
109 if (!msg)
110 return -ENOMEM;
111 pcu_prim = (struct gsm_pcu_if *) msg->data;
112 data_req = &pcu_prim->u.data_req;
113
114 data_req->sapi = sapi;
115 data_req->fn = fn;
116 data_req->arfcn = arfcn;
117 data_req->trx_nr = trx;
118 data_req->ts_nr = ts;
119 data_req->block_nr = block_nr;
120 memcpy(data_req->data, data, len);
121 data_req->len = len;
122
123 return pcu_sock_send(msg);
124}
125
126void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
127 uint32_t fn, uint8_t block_nr)
128{
129 pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
130 msg->data, msg->len);
131 msgb_free(msg);
132}
133
134void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
135 uint32_t fn, uint8_t block_nr)
136{
137 pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
138 msg->data, msg->len);
139 msgb_free(msg);
140}
141
142void pcu_l1if_tx_agch(bitvec * block, int len)
143{
144 uint8_t data[24]; /* prefix PLEN */
145
146 /* FIXME: why does OpenBTS has no PLEN and no fill in message? */
147 bitvec_pack(block, data + 1);
148 data[0] = (len << 2) | 0x01;
149 pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
150}
151
152static void pcu_l1if_tx_bcch(uint8_t *data, int len)
153{
154 pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
155}
156
157static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
158{
159 int rc = 0;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200160
161 LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
162 "block=%d data=%s\n", data_ind->sapi,
163 data_ind->arfcn, data_ind->block_nr,
164 osmo_hexdump(data_ind->data, data_ind->len));
165
166 switch (data_ind->sapi) {
167 case PCU_IF_SAPI_PDTCH:
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200168 rc = gprs_rlcmac_rcv_block(data_ind->data, data_ind->len,
169 data_ind->fn);
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200170 break;
171 default:
172 LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
173 "unsupported sapi %d\n", data_ind->sapi);
174 rc = -EINVAL;
175 }
176
177 return rc;
178}
179
180static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
181{
182 int rc = 0;
183
184 LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
185 "arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
186 rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
187
188 switch (rts_req->sapi) {
189 case PCU_IF_SAPI_PDTCH:
190 gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
191 rts_req->arfcn, rts_req->fn, rts_req->block_nr);
192 break;
193 case PCU_IF_SAPI_PTCCH:
194 /* FIXME */
195 {
196 struct msgb *msg = msgb_alloc(23, "l1_prim");
197 memset(msgb_put(msg, 23), 0x2b, 23);
198 pcu_l1if_tx_ptcch(msg, rts_req->trx_nr, rts_req->ts_nr,
199 rts_req->arfcn, rts_req->fn, rts_req->block_nr);
200 }
201 break;
202 default:
203 LOGP(DL1IF, LOGL_ERROR, "Received PCU RTS request with "
204 "unsupported sapi %d\n", rts_req->sapi);
205 rc = -EINVAL;
206 }
207
208 return rc;
209}
210
211static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
212{
213 int rc = 0;
214
215 LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
216 "qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
217 rach_ind->ra, rach_ind->fn);
218
219 switch (rach_ind->sapi) {
220 case PCU_IF_SAPI_RACH:
221 rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
222 rach_ind->qta);
223 break;
224 default:
225 LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
226 "unsupported sapi %d\n", rach_ind->sapi);
227 rc = -EINVAL;
228 }
229
230 return rc;
231}
232
233static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
234{
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200235 struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200236 int rc = 0;
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200237 int trx, ts, tfi;
238 struct gprs_rlcmac_tbf *tbf;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200239// uint8_t si13[23];
240
241 LOGP(DL1IF, LOGL_INFO, "Info indication received:\n");
242
243 if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
244 LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
Andreas Eversberge6228b32012-07-03 13:36:03 +0200245 /* free all TBF */
246 for (trx = 0; trx < 8; trx++) {
247 bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
248 for (ts = 0; ts < 8; ts++) {
249 for (tfi = 0; tfi < 32; tfi++) {
250 tbf = bts->trx[trx].pdch[ts].tbf[tfi];
251 if (tbf)
252 tbf_free(tbf);
253 }
254 }
255 }
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200256 return 0;
257 }
258 LOGP(DL1IF, LOGL_INFO, "BTS available\n");
259
260 for (trx = 0; trx < 8; trx++) {
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200261 bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200262 for (ts = 0; ts < 8; ts++) {
263 if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
264 /* FIXME: activate dynamically at RLCMAC */
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200265 if (!bts->trx[trx].pdch[ts].enable)
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200266 pcu_tx_act_req(trx, ts, 1);
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200267 bts->trx[trx].pdch[ts].enable = 1;
268 bts->trx[trx].pdch[ts].tsc =
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200269 info_ind->trx[trx].tsc[ts];
270 LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
271 trx, ts);
272 } else {
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200273 if (bts->trx[trx].pdch[ts].enable)
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200274 pcu_tx_act_req(trx, ts, 0);
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200275 bts->trx[trx].pdch[ts].enable = 0;
276 /* kick all tbf FIXME: multislot */
277 for (tfi = 0; tfi < 32; tfi++) {
278 tbf = bts->trx[trx].pdch[ts].tbf[tfi];
279 if (tbf)
280 tbf_free(tbf);
281 }
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200282 }
283 }
284 }
285
286#warning FIXME: RAC
287// rc = generate_si13(si13, 0 /* rac */);
288// printf("rc=%d\n", rc);
289// pcu_l1if_tx_bcch(si13, 23);
290
291 return rc;
292}
293
Andreas Eversberge6228b32012-07-03 13:36:03 +0200294static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
295{
296 struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
297 int trx, ts, tfi;
298 struct gprs_rlcmac_tbf *tbf;
299 uint32_t elapsed;
300 uint8_t fn13 = time_ind->fn % 13;
301
302 /* omit frame numbers not starting at a MAC block */
303 if (fn13 != 0 && fn13 != 4 && fn13 != 8)
304 return 0;
305
306 LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
307 time_ind->fn % 52);
308
309 set_current_fn(time_ind->fn);
310
311 /* check for poll timeout */
312 for (trx = 0; trx < 8; trx++) {
313 for (ts = 0; ts < 8; ts++) {
314 for (tfi = 0; tfi < 32; tfi++) {
315 tbf = bts->trx[trx].pdch[ts].tbf[tfi];
316 if (!tbf)
317 continue;
318 if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
319 continue;
320 elapsed = (frame_number - tbf->poll_fn)
321 % 2715648;
322 if (elapsed >= 20 && elapsed < 200)
323 gprs_rlcmac_poll_timeout(tbf);
324 }
325 }
326 }
327
328 return 0;
329}
330
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200331static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
332{
333 int rc = 0;
334
335 switch (msg_type) {
336 case PCU_IF_MSG_DATA_IND:
337 rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
338 break;
339 case PCU_IF_MSG_RTS_REQ:
340 rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
341 break;
342 case PCU_IF_MSG_RACH_IND:
343 rc = pcu_rx_rach_ind(&pcu_prim->u.rach_ind);
344 break;
345 case PCU_IF_MSG_INFO_IND:
346 rc = pcu_rx_info_ind(&pcu_prim->u.info_ind);
347 break;
Andreas Eversberge6228b32012-07-03 13:36:03 +0200348 case PCU_IF_MSG_TIME_IND:
349 rc = pcu_rx_time_ind(&pcu_prim->u.time_ind);
350 break;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200351 default:
352 LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
353 msg_type);
354 rc = -EINVAL;
355 }
356
357 return rc;
358}
359
360/*
361 * SYSMO-PCU socket functions
362 */
363
364struct pcu_sock_state {
365 struct osmo_fd conn_bfd; /* fd for connection to lcr */
366 struct osmo_timer_list timer; /* socket connect retry timer */
367 struct llist_head upqueue; /* queue for sending messages */
368} *pcu_sock_state = NULL;
369
370static int pcu_sock_send(struct msgb *msg)
371{
372 struct pcu_sock_state *state = pcu_sock_state;
373 struct osmo_fd *conn_bfd;
374
375 if (!state) {
376 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
377 "message\n");
378 return -EINVAL;
379 }
380 conn_bfd = &state->conn_bfd;
381 if (conn_bfd->fd <= 0) {
382 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
383 "message\n");
384 return -EIO;
385 }
386 msgb_enqueue(&state->upqueue, msg);
387 conn_bfd->when |= BSC_FD_WRITE;
388
389 return 0;
390}
391
392static void pcu_sock_close(struct pcu_sock_state *state)
393{
394 struct osmo_fd *bfd = &state->conn_bfd;
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200395 struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
396 struct gprs_rlcmac_tbf *tbf;
397 uint8_t trx, ts, tfi;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200398
399 LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
400
401 close(bfd->fd);
402 bfd->fd = -1;
403 osmo_fd_unregister(bfd);
404
405 /* flush the queue */
406 while (!llist_empty(&state->upqueue)) {
407 struct msgb *msg = msgb_dequeue(&state->upqueue);
408 msgb_free(msg);
409 }
410
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200411 /* disable all slots, kick all TBFs */
412 for (trx = 0; trx < 8; trx++) {
413 for (ts = 0; ts < 8; ts++) {
414 bts->trx[trx].pdch[ts].enable = 0;
415 for (tfi = 0; tfi < 32; tfi++) {
416 tbf = bts->trx[trx].pdch[ts].tbf[tfi];
417 if (tbf)
418 tbf_free(tbf);
419 }
420 }
421 }
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200422
423 state->timer.cb = pcu_sock_timeout;
424 osmo_timer_schedule(&state->timer, 5, 0);
425}
426
427static int pcu_sock_read(struct osmo_fd *bfd)
428{
429 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
430 struct gsm_pcu_if *pcu_prim;
431 struct msgb *msg;
432 int rc;
433
434 msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
435 if (!msg)
436 return -ENOMEM;
437
438 pcu_prim = (struct gsm_pcu_if *) msg->tail;
439
440 rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
441 if (rc == 0)
442 goto close;
443
444 if (rc < 0) {
445 if (errno == EAGAIN)
446 return 0;
447 goto close;
448 }
449
450 rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
451
452 /* as we always synchronously process the message in pcu_rx() and
453 * its callbacks, we can free the message here. */
454 msgb_free(msg);
455
456 return rc;
457
458close:
459 msgb_free(msg);
460 pcu_sock_close(state);
461 return -1;
462}
463
464static int pcu_sock_write(struct osmo_fd *bfd)
465{
466 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
467 int rc;
468
469 while (!llist_empty(&state->upqueue)) {
470 struct msgb *msg, *msg2;
471 struct gsm_pcu_if *pcu_prim;
472
473 /* peek at the beginning of the queue */
474 msg = llist_entry(state->upqueue.next, struct msgb, list);
475 pcu_prim = (struct gsm_pcu_if *)msg->data;
476
477 bfd->when &= ~BSC_FD_WRITE;
478
479 /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
480 if (!msgb_length(msg)) {
481 LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
482 "bytes!\n", pcu_prim->msg_type);
483 goto dontsend;
484 }
485
486 /* try to send it over the socket */
487 rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
488 if (rc == 0)
489 goto close;
490 if (rc < 0) {
491 if (errno == EAGAIN) {
492 bfd->when |= BSC_FD_WRITE;
493 break;
494 }
495 goto close;
496 }
497
498dontsend:
499 /* _after_ we send it, we can deueue */
500 msg2 = msgb_dequeue(&state->upqueue);
501 assert(msg == msg2);
502 msgb_free(msg);
503 }
504 return 0;
505
506close:
507 pcu_sock_close(state);
508
509 return -1;
510}
511
512static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
513{
514 int rc = 0;
515
516 if (flags & BSC_FD_READ)
517 rc = pcu_sock_read(bfd);
518 if (rc < 0)
519 return rc;
520
521 if (flags & BSC_FD_WRITE)
522 rc = pcu_sock_write(bfd);
523
524 return rc;
525}
526
527int pcu_l1if_open(void)
528{
529 struct pcu_sock_state *state;
530 struct osmo_fd *bfd;
531 struct sockaddr_un local;
532 unsigned int namelen;
533 int rc;
534
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200535 state = pcu_sock_state;
536 if (!state) {
537 state = talloc_zero(NULL, struct pcu_sock_state);
538 if (!state)
539 return -ENOMEM;
540 INIT_LLIST_HEAD(&state->upqueue);
541 }
542
543 bfd = &state->conn_bfd;
544
545 bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
546 if (bfd->fd < 0) {
547 LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
548 talloc_free(state);
549 return -1;
550 }
551
552 local.sun_family = AF_UNIX;
553 strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
554 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
555
556 /* we use the same magic that X11 uses in Xtranssock.c for
557 * calculating the proper length of the sockaddr */
558#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
559 local.sun_len = strlen(local.sun_path);
560#endif
561#if defined(BSD44SOCKETS) || defined(SUN_LEN)
562 namelen = SUN_LEN(&local);
563#else
564 namelen = strlen(local.sun_path) +
565 offsetof(struct sockaddr_un, sun_path);
566#endif
567 rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
568 if (rc != 0) {
569 LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
570 "socket, delaying... '%s'\n", local.sun_path);
571 close(bfd->fd);
572 bfd->fd = -1;
573 state->timer.cb = pcu_sock_timeout;
574 osmo_timer_schedule(&state->timer, 5, 0);
575 return -1;
576 }
577
578 bfd->when = BSC_FD_READ;
579 bfd->cb = pcu_sock_cb;
580 bfd->data = state;
581
582 rc = osmo_fd_register(bfd);
583 if (rc < 0) {
584 LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
585 close(bfd->fd);
586 talloc_free(state);
587 return rc;
588 }
589
590 LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
591
592 pcu_sock_state = state;
593
594 return 0;
595}
596
597void pcu_l1if_close(void)
598{
599 struct pcu_sock_state *state = pcu_sock_state;
600 struct osmo_fd *bfd;
601
602 if (!state)
603 return;
604
605 if (osmo_timer_pending(&state->timer))
606 osmo_timer_del(&state->timer);
607
608 bfd = &state->conn_bfd;
609 if (bfd->fd > 0)
610 pcu_sock_close(state);
611 talloc_free(state);
612 pcu_sock_state = NULL;
613}
614
615static void pcu_sock_timeout(void *_priv)
616{
617 pcu_l1if_open();
618}
619
620
621
622