blob: b42f042ca99eaf9753fc905627d47be42881606a [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>
32}
33
34#include <gprs_rlcmac.h>
35#include <pcu_l1_if.h>
36#include <gprs_debug.h>
37#include <gprs_bssgp_pcu.h>
38#include <pcuif_proto.h>
Holger Hans Peter Freyther34bd8bd2013-10-19 21:10:38 +020039#include <bts.h>
Holger Hans Peter Freyther9e21d842013-10-16 17:48:12 +020040#include <tbf.h>
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040041
Andreas Eversberge266bd42012-07-13 14:00:21 +020042extern void *tall_pcu_ctx;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040043
Andreas Eversberg0f4541b2013-01-16 09:17:24 +010044extern "C" {
45int l1if_close_pdch(void *obj);
46}
47
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040048/*
Harald Welted32cbbb2015-11-11 21:23:23 +010049 * osmo-bts PCU socket functions
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040050 */
51
52struct pcu_sock_state {
53 struct osmo_fd conn_bfd; /* fd for connection to lcr */
54 struct osmo_timer_list timer; /* socket connect retry timer */
55 struct llist_head upqueue; /* queue for sending messages */
56} *pcu_sock_state = NULL;
57
58static void pcu_sock_timeout(void *_priv);
59
60int pcu_sock_send(struct msgb *msg)
61{
62 struct pcu_sock_state *state = pcu_sock_state;
63 struct osmo_fd *conn_bfd;
64
65 if (!state) {
66 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
67 "message\n");
68 return -EINVAL;
69 }
70 conn_bfd = &state->conn_bfd;
71 if (conn_bfd->fd <= 0) {
72 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
73 "message\n");
74 return -EIO;
75 }
76 msgb_enqueue(&state->upqueue, msg);
77 conn_bfd->when |= BSC_FD_WRITE;
78
79 return 0;
80}
81
Andreas Eversberga3c12fb2012-09-28 22:46:33 +020082static void pcu_sock_close(struct pcu_sock_state *state, int lost)
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040083{
84 struct osmo_fd *bfd = &state->conn_bfd;
Holger Hans Peter Freytherb6acfda2013-10-17 19:41:11 +020085 struct gprs_rlcmac_bts *bts = bts_main_data();
Holger Hans Peter Freyther964ddb62013-10-16 17:53:23 +020086 uint8_t trx, ts;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040087
Andreas Eversberga3c12fb2012-09-28 22:46:33 +020088 LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
89 (lost) ? "LOST" : "closed");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040090
91 close(bfd->fd);
92 bfd->fd = -1;
93 osmo_fd_unregister(bfd);
94
95 /* flush the queue */
96 while (!llist_empty(&state->upqueue)) {
97 struct msgb *msg = msgb_dequeue(&state->upqueue);
98 msgb_free(msg);
99 }
100
101 /* disable all slots, kick all TBFs */
102 for (trx = 0; trx < 8; trx++) {
Andreas Eversberg3afe56d2013-01-25 07:22:59 +0100103#ifdef ENABLE_SYSMODSP
Andreas Eversberg0f4541b2013-01-16 09:17:24 +0100104 if (bts->trx[trx].fl1h) {
105 l1if_close_pdch(bts->trx[trx].fl1h);
106 bts->trx[trx].fl1h = NULL;
107 }
Andreas Eversberg3afe56d2013-01-25 07:22:59 +0100108#endif
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200109 for (ts = 0; ts < 8; ts++)
Holger Hans Peter Freyther17b0d832013-10-19 17:37:48 +0200110 bts->trx[trx].pdch[ts].disable();
111#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
Holger Hans Peter Freyther964ddb62013-10-16 17:53:23 +0200112 gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400113 }
114
Daniel Willmann6d8884d2014-06-04 18:30:59 +0200115 gprs_bssgp_destroy();
116 exit(0);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400117}
118
119static int pcu_sock_read(struct osmo_fd *bfd)
120{
121 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
122 struct gsm_pcu_if *pcu_prim;
123 struct msgb *msg;
124 int rc;
125
126 msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
127 if (!msg)
128 return -ENOMEM;
129
130 pcu_prim = (struct gsm_pcu_if *) msg->tail;
131
132 rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
133 if (rc == 0)
134 goto close;
135
136 if (rc < 0) {
137 if (errno == EAGAIN)
138 return 0;
139 goto close;
140 }
141
142 rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
143
144 /* as we always synchronously process the message in pcu_rx() and
145 * its callbacks, we can free the message here. */
146 msgb_free(msg);
147
148 return rc;
149
150close:
151 msgb_free(msg);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200152 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400153 return -1;
154}
155
156static int pcu_sock_write(struct osmo_fd *bfd)
157{
158 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
159 int rc;
160
161 while (!llist_empty(&state->upqueue)) {
162 struct msgb *msg, *msg2;
163 struct gsm_pcu_if *pcu_prim;
164
165 /* peek at the beginning of the queue */
166 msg = llist_entry(state->upqueue.next, struct msgb, list);
167 pcu_prim = (struct gsm_pcu_if *)msg->data;
168
169 bfd->when &= ~BSC_FD_WRITE;
170
171 /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
172 if (!msgb_length(msg)) {
173 LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
174 "bytes!\n", pcu_prim->msg_type);
175 goto dontsend;
176 }
177
178 /* try to send it over the socket */
179 rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
180 if (rc == 0)
181 goto close;
182 if (rc < 0) {
183 if (errno == EAGAIN) {
184 bfd->when |= BSC_FD_WRITE;
185 break;
186 }
187 goto close;
188 }
189
190dontsend:
191 /* _after_ we send it, we can deueue */
192 msg2 = msgb_dequeue(&state->upqueue);
193 assert(msg == msg2);
194 msgb_free(msg);
195 }
196 return 0;
197
198close:
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200199 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400200
201 return -1;
202}
203
204static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
205{
206 int rc = 0;
207
208 if (flags & BSC_FD_READ)
209 rc = pcu_sock_read(bfd);
210 if (rc < 0)
211 return rc;
212
213 if (flags & BSC_FD_WRITE)
214 rc = pcu_sock_write(bfd);
215
216 return rc;
217}
218
219int pcu_l1if_open(void)
220{
221 struct pcu_sock_state *state;
222 struct osmo_fd *bfd;
223 struct sockaddr_un local;
224 unsigned int namelen;
225 int rc;
226
Harald Welte21848272015-11-12 01:07:41 +0100227 LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
228
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400229 state = pcu_sock_state;
230 if (!state) {
Andreas Eversberge266bd42012-07-13 14:00:21 +0200231 state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400232 if (!state)
233 return -ENOMEM;
234 INIT_LLIST_HEAD(&state->upqueue);
235 }
236
237 bfd = &state->conn_bfd;
238
239 bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
240 if (bfd->fd < 0) {
Harald Welted32cbbb2015-11-11 21:23:23 +0100241 LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400242 talloc_free(state);
243 return -1;
244 }
245
246 local.sun_family = AF_UNIX;
247 strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
248 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
249
250 /* we use the same magic that X11 uses in Xtranssock.c for
251 * calculating the proper length of the sockaddr */
252#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
253 local.sun_len = strlen(local.sun_path);
254#endif
255#if defined(BSD44SOCKETS) || defined(SUN_LEN)
256 namelen = SUN_LEN(&local);
257#else
258 namelen = strlen(local.sun_path) +
259 offsetof(struct sockaddr_un, sun_path);
260#endif
261 rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
262 if (rc != 0) {
Harald Welted32cbbb2015-11-11 21:23:23 +0100263 LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
Harald Welte19d1e922015-11-12 01:08:19 +0100264 " PCU socket, delaying... '%s'\n", local.sun_path);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200265 pcu_sock_state = state;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400266 close(bfd->fd);
267 bfd->fd = -1;
268 state->timer.cb = pcu_sock_timeout;
269 osmo_timer_schedule(&state->timer, 5, 0);
270 return 0;
271 }
272
273 bfd->when = BSC_FD_READ;
274 bfd->cb = pcu_sock_cb;
275 bfd->data = state;
276
277 rc = osmo_fd_register(bfd);
278 if (rc < 0) {
279 LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
280 close(bfd->fd);
281 talloc_free(state);
282 return rc;
283 }
284
Harald Welted32cbbb2015-11-11 21:23:23 +0100285 LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket has been connected\n");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400286
287 pcu_sock_state = state;
288
289 return 0;
290}
291
292void pcu_l1if_close(void)
293{
294 struct pcu_sock_state *state = pcu_sock_state;
295 struct osmo_fd *bfd;
296
297 if (!state)
298 return;
299
Holger Hans Peter Freyther51c57042013-07-13 11:52:50 +0200300 osmo_timer_del(&state->timer);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400301
302 bfd = &state->conn_bfd;
303 if (bfd->fd > 0)
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200304 pcu_sock_close(state, 0);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400305 talloc_free(state);
306 pcu_sock_state = NULL;
307}
308
309static void pcu_sock_timeout(void *_priv)
310{
311 pcu_l1if_open();
312}