blob: b9664b0870a14280cca8a5515dc34fe17a3cfae9 [file] [log] [blame]
Harald Welted32cbbb2015-11-11 21:23:23 +01001/* osmobts_sock.cpp
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +04002 *
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>
Max1187a772018-01-26 13:31:42 +010032#include <osmocom/core/linuxlist.h>
33#include <osmocom/core/logging.h>
34#include <osmocom/core/timer.h>
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040035}
36
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040037#include <pcu_l1_if.h>
38#include <gprs_debug.h>
39#include <gprs_bssgp_pcu.h>
Harald Welte68fc1272016-11-16 22:48:33 +010040#include <osmocom/pcu/pcuif_proto.h>
Holger Hans Peter Freyther34bd8bd2013-10-19 21:10:38 +020041#include <bts.h>
Holger Hans Peter Freyther9e21d842013-10-16 17:48:12 +020042#include <tbf.h>
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040043
Andreas Eversberge266bd42012-07-13 14:00:21 +020044extern void *tall_pcu_ctx;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040045
Andreas Eversberg0f4541b2013-01-16 09:17:24 +010046extern "C" {
47int l1if_close_pdch(void *obj);
48}
49
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040050/*
Harald Welted32cbbb2015-11-11 21:23:23 +010051 * osmo-bts PCU socket functions
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040052 */
53
54struct pcu_sock_state {
55 struct osmo_fd conn_bfd; /* fd for connection to lcr */
56 struct osmo_timer_list timer; /* socket connect retry timer */
57 struct llist_head upqueue; /* queue for sending messages */
58} *pcu_sock_state = NULL;
59
Stefan Sperling5b22fb72018-02-14 19:46:33 +010060static void pcu_sock_timeout(void *_priv)
61{
62 pcu_l1if_open();
63}
64
65static void pcu_tx_txt_retry(void *_priv)
66{
67 struct gprs_rlcmac_bts *bts = bts_main_data();
68 struct pcu_sock_state *state = pcu_sock_state;
69
70 if (bts->active)
71 return;
72
73 LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
74 pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
75 osmo_timer_schedule(&state->timer, 5, 0);
76}
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040077
78int pcu_sock_send(struct msgb *msg)
79{
80 struct pcu_sock_state *state = pcu_sock_state;
81 struct osmo_fd *conn_bfd;
82
83 if (!state) {
84 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
85 "message\n");
86 return -EINVAL;
87 }
88 conn_bfd = &state->conn_bfd;
89 if (conn_bfd->fd <= 0) {
90 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
91 "message\n");
92 return -EIO;
93 }
94 msgb_enqueue(&state->upqueue, msg);
95 conn_bfd->when |= BSC_FD_WRITE;
96
97 return 0;
98}
99
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200100static void pcu_sock_close(struct pcu_sock_state *state, int lost)
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400101{
102 struct osmo_fd *bfd = &state->conn_bfd;
Holger Hans Peter Freytherb6acfda2013-10-17 19:41:11 +0200103 struct gprs_rlcmac_bts *bts = bts_main_data();
Holger Hans Peter Freyther964ddb62013-10-16 17:53:23 +0200104 uint8_t trx, ts;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400105
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200106 LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
107 (lost) ? "LOST" : "closed");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400108
109 close(bfd->fd);
110 bfd->fd = -1;
111 osmo_fd_unregister(bfd);
112
113 /* flush the queue */
114 while (!llist_empty(&state->upqueue)) {
115 struct msgb *msg = msgb_dequeue(&state->upqueue);
116 msgb_free(msg);
117 }
118
119 /* disable all slots, kick all TBFs */
120 for (trx = 0; trx < 8; trx++) {
Maxcad867e2016-04-21 14:35:55 +0200121#ifdef ENABLE_DIRECT_PHY
Andreas Eversberg0f4541b2013-01-16 09:17:24 +0100122 if (bts->trx[trx].fl1h) {
123 l1if_close_pdch(bts->trx[trx].fl1h);
124 bts->trx[trx].fl1h = NULL;
125 }
Andreas Eversberg3afe56d2013-01-25 07:22:59 +0100126#endif
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200127 for (ts = 0; ts < 8; ts++)
Holger Hans Peter Freyther17b0d832013-10-19 17:37:48 +0200128 bts->trx[trx].pdch[ts].disable();
Max5a6bcfb2017-09-01 14:36:44 +0200129/* FIXME: NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c
130for the reset. */
Holger Hans Peter Freyther964ddb62013-10-16 17:53:23 +0200131 gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400132 }
133
Daniel Willmann6d8884d2014-06-04 18:30:59 +0200134 gprs_bssgp_destroy();
135 exit(0);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400136}
137
138static int pcu_sock_read(struct osmo_fd *bfd)
139{
140 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
141 struct gsm_pcu_if *pcu_prim;
142 struct msgb *msg;
143 int rc;
144
145 msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
146 if (!msg)
147 return -ENOMEM;
148
149 pcu_prim = (struct gsm_pcu_if *) msg->tail;
150
151 rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
152 if (rc == 0)
153 goto close;
154
155 if (rc < 0) {
156 if (errno == EAGAIN)
157 return 0;
158 goto close;
159 }
160
161 rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
162
163 /* as we always synchronously process the message in pcu_rx() and
164 * its callbacks, we can free the message here. */
165 msgb_free(msg);
166
167 return rc;
168
169close:
170 msgb_free(msg);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200171 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400172 return -1;
173}
174
175static int pcu_sock_write(struct osmo_fd *bfd)
176{
177 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
178 int rc;
179
180 while (!llist_empty(&state->upqueue)) {
181 struct msgb *msg, *msg2;
182 struct gsm_pcu_if *pcu_prim;
183
184 /* peek at the beginning of the queue */
185 msg = llist_entry(state->upqueue.next, struct msgb, list);
186 pcu_prim = (struct gsm_pcu_if *)msg->data;
187
188 bfd->when &= ~BSC_FD_WRITE;
189
190 /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
191 if (!msgb_length(msg)) {
192 LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
193 "bytes!\n", pcu_prim->msg_type);
194 goto dontsend;
195 }
196
197 /* try to send it over the socket */
198 rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
199 if (rc == 0)
200 goto close;
201 if (rc < 0) {
202 if (errno == EAGAIN) {
203 bfd->when |= BSC_FD_WRITE;
204 break;
205 }
206 goto close;
207 }
208
209dontsend:
210 /* _after_ we send it, we can deueue */
211 msg2 = msgb_dequeue(&state->upqueue);
212 assert(msg == msg2);
213 msgb_free(msg);
214 }
215 return 0;
216
217close:
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200218 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400219
220 return -1;
221}
222
223static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
224{
225 int rc = 0;
226
227 if (flags & BSC_FD_READ)
228 rc = pcu_sock_read(bfd);
229 if (rc < 0)
230 return rc;
231
232 if (flags & BSC_FD_WRITE)
233 rc = pcu_sock_write(bfd);
234
235 return rc;
236}
237
238int pcu_l1if_open(void)
239{
240 struct pcu_sock_state *state;
241 struct osmo_fd *bfd;
242 struct sockaddr_un local;
243 unsigned int namelen;
244 int rc;
Pau Espin Pedrolc4178e52017-08-08 15:03:50 +0200245 struct gprs_rlcmac_bts *bts = bts_main_data();
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400246
Harald Welte21848272015-11-12 01:07:41 +0100247 LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
248
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400249 state = pcu_sock_state;
250 if (!state) {
Andreas Eversberge266bd42012-07-13 14:00:21 +0200251 state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400252 if (!state)
253 return -ENOMEM;
254 INIT_LLIST_HEAD(&state->upqueue);
255 }
256
257 bfd = &state->conn_bfd;
258
259 bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
260 if (bfd->fd < 0) {
Harald Welted32cbbb2015-11-11 21:23:23 +0100261 LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400262 talloc_free(state);
263 return -1;
264 }
265
266 local.sun_family = AF_UNIX;
Pau Espin Pedrolc4178e52017-08-08 15:03:50 +0200267 strncpy(local.sun_path, bts->pcu_sock_path, sizeof(local.sun_path));
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400268 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
269
270 /* we use the same magic that X11 uses in Xtranssock.c for
271 * calculating the proper length of the sockaddr */
272#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
273 local.sun_len = strlen(local.sun_path);
274#endif
275#if defined(BSD44SOCKETS) || defined(SUN_LEN)
276 namelen = SUN_LEN(&local);
277#else
278 namelen = strlen(local.sun_path) +
279 offsetof(struct sockaddr_un, sun_path);
280#endif
281 rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
282 if (rc != 0) {
Harald Welted32cbbb2015-11-11 21:23:23 +0100283 LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
Pau Espin Pedrolda0a1942017-12-05 17:35:22 +0100284 " PCU socket (%s), delaying... '%s'\n",
285 strerror(errno), local.sun_path);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200286 pcu_sock_state = state;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400287 close(bfd->fd);
288 bfd->fd = -1;
Stefan Sperling5b22fb72018-02-14 19:46:33 +0100289 osmo_timer_setup(&state->timer, pcu_sock_timeout, NULL);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400290 osmo_timer_schedule(&state->timer, 5, 0);
291 return 0;
292 }
293
294 bfd->when = BSC_FD_READ;
295 bfd->cb = pcu_sock_cb;
296 bfd->data = state;
297
298 rc = osmo_fd_register(bfd);
299 if (rc < 0) {
300 LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
301 close(bfd->fd);
302 talloc_free(state);
303 return rc;
304 }
305
Maxb3df5862017-01-06 17:20:57 +0100306 LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket %s has been connected\n",
307 local.sun_path);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400308
309 pcu_sock_state = state;
310
Max0a8fae82017-03-08 18:53:30 +0100311 LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
312 pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
313
Stefan Sperling5b22fb72018-02-14 19:46:33 +0100314 /* Schedule a timer so we keep trying until the BTS becomes active. */
315 osmo_timer_setup(&state->timer, pcu_tx_txt_retry, NULL);
316 osmo_timer_schedule(&state->timer, 5, 0);
317
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400318 return 0;
319}
320
321void pcu_l1if_close(void)
322{
323 struct pcu_sock_state *state = pcu_sock_state;
324 struct osmo_fd *bfd;
325
326 if (!state)
327 return;
328
Holger Hans Peter Freyther51c57042013-07-13 11:52:50 +0200329 osmo_timer_del(&state->timer);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400330
331 bfd = &state->conn_bfd;
332 if (bfd->fd > 0)
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200333 pcu_sock_close(state, 0);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400334 talloc_free(state);
335 pcu_sock_state = NULL;
336}