blob: e116e5a02252a9bb13323f173fd34d51804697e5 [file] [log] [blame]
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +04001/* sysmo_sock.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 <gprs_bssgp_pcu.h>
38#include <pcuif_proto.h>
39
Andreas Eversberge266bd42012-07-13 14:00:21 +020040extern void *tall_pcu_ctx;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040041
Andreas Eversberg0f4541b2013-01-16 09:17:24 +010042extern "C" {
43int l1if_close_pdch(void *obj);
44}
45
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040046/*
47 * SYSMO-PCU socket functions
48 */
49
50struct pcu_sock_state {
51 struct osmo_fd conn_bfd; /* fd for connection to lcr */
52 struct osmo_timer_list timer; /* socket connect retry timer */
53 struct llist_head upqueue; /* queue for sending messages */
54} *pcu_sock_state = NULL;
55
56static void pcu_sock_timeout(void *_priv);
57
58int pcu_sock_send(struct msgb *msg)
59{
60 struct pcu_sock_state *state = pcu_sock_state;
61 struct osmo_fd *conn_bfd;
62
63 if (!state) {
64 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
65 "message\n");
66 return -EINVAL;
67 }
68 conn_bfd = &state->conn_bfd;
69 if (conn_bfd->fd <= 0) {
70 LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
71 "message\n");
72 return -EIO;
73 }
74 msgb_enqueue(&state->upqueue, msg);
75 conn_bfd->when |= BSC_FD_WRITE;
76
77 return 0;
78}
79
Andreas Eversberga3c12fb2012-09-28 22:46:33 +020080static void pcu_sock_close(struct pcu_sock_state *state, int lost)
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040081{
82 struct osmo_fd *bfd = &state->conn_bfd;
83 struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
84 struct gprs_rlcmac_tbf *tbf;
85 uint8_t trx, ts, tfi;
86
Andreas Eversberga3c12fb2012-09-28 22:46:33 +020087 LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
88 (lost) ? "LOST" : "closed");
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +040089
90 close(bfd->fd);
91 bfd->fd = -1;
92 osmo_fd_unregister(bfd);
93
94 /* flush the queue */
95 while (!llist_empty(&state->upqueue)) {
96 struct msgb *msg = msgb_dequeue(&state->upqueue);
97 msgb_free(msg);
98 }
99
100 /* disable all slots, kick all TBFs */
101 for (trx = 0; trx < 8; trx++) {
Andreas Eversberg3afe56d2013-01-25 07:22:59 +0100102#ifdef ENABLE_SYSMODSP
Andreas Eversberg0f4541b2013-01-16 09:17:24 +0100103 if (bts->trx[trx].fl1h) {
104 l1if_close_pdch(bts->trx[trx].fl1h);
105 bts->trx[trx].fl1h = NULL;
106 }
Andreas Eversberg3afe56d2013-01-25 07:22:59 +0100107#endif
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200108 for (ts = 0; ts < 8; ts++)
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400109 bts->trx[trx].pdch[ts].enable = 0;
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200110 for (tfi = 0; tfi < 32; tfi++) {
111 tbf = bts->trx[trx].ul_tbf[tfi];
112 if (tbf)
113 tbf_free(tbf);
114 tbf = bts->trx[trx].dl_tbf[tfi];
115 if (tbf)
116 tbf_free(tbf);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400117 }
118 }
119
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200120 gprs_bssgp_destroy_or_exit();
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400121
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200122 if (lost) {
123 state->timer.cb = pcu_sock_timeout;
124 osmo_timer_schedule(&state->timer, 5, 0);
125 }
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400126}
127
128static int pcu_sock_read(struct osmo_fd *bfd)
129{
130 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
131 struct gsm_pcu_if *pcu_prim;
132 struct msgb *msg;
133 int rc;
134
135 msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
136 if (!msg)
137 return -ENOMEM;
138
139 pcu_prim = (struct gsm_pcu_if *) msg->tail;
140
141 rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
142 if (rc == 0)
143 goto close;
144
145 if (rc < 0) {
146 if (errno == EAGAIN)
147 return 0;
148 goto close;
149 }
150
151 rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
152
153 /* as we always synchronously process the message in pcu_rx() and
154 * its callbacks, we can free the message here. */
155 msgb_free(msg);
156
157 return rc;
158
159close:
160 msgb_free(msg);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200161 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400162 return -1;
163}
164
165static int pcu_sock_write(struct osmo_fd *bfd)
166{
167 struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
168 int rc;
169
170 while (!llist_empty(&state->upqueue)) {
171 struct msgb *msg, *msg2;
172 struct gsm_pcu_if *pcu_prim;
173
174 /* peek at the beginning of the queue */
175 msg = llist_entry(state->upqueue.next, struct msgb, list);
176 pcu_prim = (struct gsm_pcu_if *)msg->data;
177
178 bfd->when &= ~BSC_FD_WRITE;
179
180 /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
181 if (!msgb_length(msg)) {
182 LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
183 "bytes!\n", pcu_prim->msg_type);
184 goto dontsend;
185 }
186
187 /* try to send it over the socket */
188 rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
189 if (rc == 0)
190 goto close;
191 if (rc < 0) {
192 if (errno == EAGAIN) {
193 bfd->when |= BSC_FD_WRITE;
194 break;
195 }
196 goto close;
197 }
198
199dontsend:
200 /* _after_ we send it, we can deueue */
201 msg2 = msgb_dequeue(&state->upqueue);
202 assert(msg == msg2);
203 msgb_free(msg);
204 }
205 return 0;
206
207close:
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200208 pcu_sock_close(state, 1);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400209
210 return -1;
211}
212
213static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
214{
215 int rc = 0;
216
217 if (flags & BSC_FD_READ)
218 rc = pcu_sock_read(bfd);
219 if (rc < 0)
220 return rc;
221
222 if (flags & BSC_FD_WRITE)
223 rc = pcu_sock_write(bfd);
224
225 return rc;
226}
227
228int pcu_l1if_open(void)
229{
230 struct pcu_sock_state *state;
231 struct osmo_fd *bfd;
232 struct sockaddr_un local;
233 unsigned int namelen;
234 int rc;
235
236 state = pcu_sock_state;
237 if (!state) {
Andreas Eversberge266bd42012-07-13 14:00:21 +0200238 state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400239 if (!state)
240 return -ENOMEM;
241 INIT_LLIST_HEAD(&state->upqueue);
242 }
243
244 bfd = &state->conn_bfd;
245
246 bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
247 if (bfd->fd < 0) {
248 LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
249 talloc_free(state);
250 return -1;
251 }
252
253 local.sun_family = AF_UNIX;
254 strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
255 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
256
257 /* we use the same magic that X11 uses in Xtranssock.c for
258 * calculating the proper length of the sockaddr */
259#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
260 local.sun_len = strlen(local.sun_path);
261#endif
262#if defined(BSD44SOCKETS) || defined(SUN_LEN)
263 namelen = SUN_LEN(&local);
264#else
265 namelen = strlen(local.sun_path) +
266 offsetof(struct sockaddr_un, sun_path);
267#endif
268 rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
269 if (rc != 0) {
270 LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
271 "socket, delaying... '%s'\n", local.sun_path);
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200272 pcu_sock_state = state;
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400273 close(bfd->fd);
274 bfd->fd = -1;
275 state->timer.cb = pcu_sock_timeout;
276 osmo_timer_schedule(&state->timer, 5, 0);
277 return 0;
278 }
279
280 bfd->when = BSC_FD_READ;
281 bfd->cb = pcu_sock_cb;
282 bfd->data = state;
283
284 rc = osmo_fd_register(bfd);
285 if (rc < 0) {
286 LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
287 close(bfd->fd);
288 talloc_free(state);
289 return rc;
290 }
291
292 LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
293
294 pcu_sock_state = state;
295
296 return 0;
297}
298
299void pcu_l1if_close(void)
300{
301 struct pcu_sock_state *state = pcu_sock_state;
302 struct osmo_fd *bfd;
303
304 if (!state)
305 return;
306
Holger Hans Peter Freyther51c57042013-07-13 11:52:50 +0200307 osmo_timer_del(&state->timer);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400308
309 bfd = &state->conn_bfd;
310 if (bfd->fd > 0)
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200311 pcu_sock_close(state, 0);
Ivan Kluchnikovef7f28c2012-07-12 14:49:15 +0400312 talloc_free(state);
313 pcu_sock_state = NULL;
314}
315
316static void pcu_sock_timeout(void *_priv)
317{
318 pcu_l1if_open();
319}