blob: 034bd97235776204d05f01b4b9c63338aabaf517 [file] [log] [blame]
Harald Welte1fa60c82009-02-09 18:13:26 +00001/* OpenBSC Abis interface to E1 */
2
3/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.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#include <stdio.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <string.h>
28#include <time.h>
29#include <sys/fcntl.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/ioctl.h>
33#include <arpa/inet.h>
34#include <mISDNif.h>
35
36//#define AF_COMPATIBILITY_FUNC
37//#include <compat_af_isdn.h>
Holger Freyther66e092b2009-03-31 12:13:50 +000038#ifndef AF_ISDN
Harald Welte1fa60c82009-02-09 18:13:26 +000039#define AF_ISDN 34
40#define PF_ISDN AF_ISDN
Holger Freyther66e092b2009-03-31 12:13:50 +000041#endif
Harald Welte1fa60c82009-02-09 18:13:26 +000042
43#include <openbsc/select.h>
44#include <openbsc/msgb.h>
45#include <openbsc/debug.h>
46#include <openbsc/gsm_data.h>
47#include <openbsc/e1_input.h>
48#include <openbsc/abis_nm.h>
49#include <openbsc/abis_rsl.h>
50#include <openbsc/linuxlist.h>
51#include <openbsc/subchan_demux.h>
Harald Welte45b407a2009-05-23 15:51:12 +000052#include <openbsc/trau_frame.h>
Harald Welte1fa60c82009-02-09 18:13:26 +000053#include <openbsc/trau_mux.h>
54
55#define NUM_E1_TS 32
56
57/* list of all E1 drivers */
Harald Welte44d542e2009-03-10 18:24:35 +000058LLIST_HEAD(e1inp_driver_list);
Harald Welte1fa60c82009-02-09 18:13:26 +000059
60/* list of all E1 lines */
Harald Welte44d542e2009-03-10 18:24:35 +000061LLIST_HEAD(e1inp_line_list);
Harald Welte1fa60c82009-02-09 18:13:26 +000062
Holger Freytherff9592f2009-03-09 16:17:14 +000063/* to be implemented, e.g. by bsc_hack.c */
64void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx);
65
Harald Welte1fa60c82009-02-09 18:13:26 +000066/*
67 * pcap writing of the misdn load
68 * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
69 */
70#define DLT_LINUX_LAPD 177
71#define PCAP_INPUT 0
72#define PCAP_OUTPUT 1
73
74struct pcap_hdr {
75 u_int32_t magic_number;
76 u_int16_t version_major;
77 u_int16_t version_minor;
78 int32_t thiszone;
79 u_int32_t sigfigs;
80 u_int32_t snaplen;
81 u_int32_t network;
82} __attribute__((packed));
83
84struct pcaprec_hdr {
85 u_int32_t ts_sec;
86 u_int32_t ts_usec;
87 u_int32_t incl_len;
88 u_int32_t orig_len;
89} __attribute__((packed));
90
91struct fake_linux_lapd_header {
92 u_int16_t pkttype;
93 u_int16_t hatype;
94 u_int16_t halen;
95 u_int64_t addr;
96 int16_t protocol;
97} __attribute__((packed));
98
99struct lapd_header {
100 u_int8_t ea1 : 1;
101 u_int8_t cr : 1;
102 u_int8_t sapi : 6;
103 u_int8_t ea2 : 1;
104 u_int8_t tei : 7;
105 u_int8_t control_foo; /* fake UM's ... */
106} __attribute__((packed));
107
108static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset);
109static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset);
110static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset);
111static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset);
112static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
113
114
115static int pcap_fd = -1;
116
Holger Freyther0469cf62009-03-31 12:14:16 +0000117void e1_set_pcap_fd(int fd)
Harald Welte1fa60c82009-02-09 18:13:26 +0000118{
119 int ret;
120 struct pcap_hdr header = {
121 .magic_number = 0xa1b2c3d4,
122 .version_major = 2,
123 .version_minor = 4,
124 .thiszone = 0,
125 .sigfigs = 0,
126 .snaplen = 65535,
127 .network = DLT_LINUX_LAPD,
128 };
129
130 pcap_fd = fd;
131 ret = write(pcap_fd, &header, sizeof(header));
132}
133
134/* This currently only works for the D-Channel */
Holger Freyther0469cf62009-03-31 12:14:16 +0000135static void write_pcap_packet(int direction, int sapi, int tei,
Harald Welte1fa60c82009-02-09 18:13:26 +0000136 struct msgb *msg) {
137 if (pcap_fd < 0)
138 return;
139
140 int ret;
141 time_t cur_time;
142 struct tm *tm;
Andreas Eversberg20152a32009-06-10 14:47:33 +0200143 int mi_head = (direction==PCAP_INPUT) ? MISDN_HEADER_LEN : 0;
Harald Welte1fa60c82009-02-09 18:13:26 +0000144
145 struct fake_linux_lapd_header header = {
146 .pkttype = 4,
147 .hatype = 0,
148 .halen = 0,
149 .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1,
150 .protocol = ntohs(48),
151 };
152
153 struct lapd_header lapd_header = {
154 .ea1 = 0,
155 .cr = direction == PCAP_OUTPUT ? 1 : 0,
Holger Freyther0469cf62009-03-31 12:14:16 +0000156 .sapi = sapi & 0x3F,
Harald Welte1fa60c82009-02-09 18:13:26 +0000157 .ea2 = 1,
Holger Freyther0469cf62009-03-31 12:14:16 +0000158 .tei = tei & 0x7F,
Harald Welte1fa60c82009-02-09 18:13:26 +0000159 .control_foo = 0x03 /* UI */,
160 };
161
162 struct pcaprec_hdr payload_header = {
163 .ts_sec = 0,
164 .ts_usec = 0,
165 .incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
166 + sizeof(struct lapd_header)
Andreas Eversberg20152a32009-06-10 14:47:33 +0200167 - mi_head,
Harald Welte1fa60c82009-02-09 18:13:26 +0000168 .orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
169 + sizeof(struct lapd_header)
Andreas Eversberg20152a32009-06-10 14:47:33 +0200170 - mi_head,
Harald Welte1fa60c82009-02-09 18:13:26 +0000171 };
172
173
Andreas Eversberg20152a32009-06-10 14:47:33 +0200174 printf("Packet of: %d\n", direction);
175
Harald Welte1fa60c82009-02-09 18:13:26 +0000176 cur_time = time(NULL);
177 tm = localtime(&cur_time);
178 payload_header.ts_sec = mktime(tm);
179
180 ret = write(pcap_fd, &payload_header, sizeof(payload_header));
181 ret = write(pcap_fd, &header, sizeof(header));
182 ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
Andreas Eversberg20152a32009-06-10 14:47:33 +0200183 ret = write(pcap_fd, msg->data + mi_head,
184 msg->len - mi_head);
Harald Welte1fa60c82009-02-09 18:13:26 +0000185}
Harald Welte1fa60c82009-02-09 18:13:26 +0000186
Harald Welted256d4f2009-03-10 19:44:48 +0000187static const char *sign_types[] = {
188 [E1INP_SIGN_NONE] = "None",
189 [E1INP_SIGN_OML] = "OML",
190 [E1INP_SIGN_RSL] = "RSL",
191};
192const char *e1inp_signtype_name(enum e1inp_sign_type tp)
193{
194 if (tp >= ARRAY_SIZE(sign_types))
195 return "undefined";
196 return sign_types[tp];
197}
198
199static const char *ts_types[] = {
200 [E1INP_TS_TYPE_NONE] = "None",
201 [E1INP_TS_TYPE_SIGN] = "Signalling",
202 [E1INP_TS_TYPE_TRAU] = "TRAU",
203};
204
205const char *e1inp_tstype_name(enum e1inp_ts_type tp)
206{
207 if (tp >= ARRAY_SIZE(ts_types))
208 return "undefined";
209 return ts_types[tp];
210}
211
Harald Welte1fa60c82009-02-09 18:13:26 +0000212/* callback when a TRAU frame was received */
213static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
214 void *_priv)
215{
216 struct e1inp_ts *e1i_ts = _priv;
217 struct gsm_e1_subslot src_ss;
218
219 src_ss.e1_nr = e1i_ts->line->num;
220 src_ss.e1_ts = e1i_ts->num;
221 src_ss.e1_ts_ss = ch;
222
223 return trau_mux_input(&src_ss, data, len);
224}
225
226int abis_rsl_sendmsg(struct msgb *msg)
227{
228 struct e1inp_sign_link *sign_link;
229 struct e1inp_driver *e1inp_driver;
Harald Weltef55b49f2009-05-23 06:20:41 +0000230 struct e1inp_ts *e1i_ts;
Harald Welte1fa60c82009-02-09 18:13:26 +0000231
232 msg->l2h = msg->data;
233
234 if (!msg->trx || !msg->trx->rsl_link) {
235 fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
236 return -EINVAL;
237 }
238
239 sign_link = msg->trx->rsl_link;
Harald Weltef55b49f2009-05-23 06:20:41 +0000240 e1i_ts = sign_link->ts;
241 if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
242 /* notify the driver we have something to write */
243 e1inp_driver = sign_link->ts->line->driver;
244 e1inp_driver->want_write(e1i_ts);
245 }
Harald Welte1fa60c82009-02-09 18:13:26 +0000246 msgb_enqueue(&sign_link->tx_list, msg);
247
Holger Freyther0469cf62009-03-31 12:14:16 +0000248 /* dump it */
249 write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
250
Harald Welte1fa60c82009-02-09 18:13:26 +0000251 return 0;
252}
253
254int _abis_nm_sendmsg(struct msgb *msg)
255{
256 struct e1inp_sign_link *sign_link;
257 struct e1inp_driver *e1inp_driver;
Harald Weltef55b49f2009-05-23 06:20:41 +0000258 struct e1inp_ts *e1i_ts;
Harald Welte1fa60c82009-02-09 18:13:26 +0000259
260 msg->l2h = msg->data;
261
262 if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
Holger Freytherfb3f5192009-02-09 23:09:15 +0000263 fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
Harald Welte1fa60c82009-02-09 18:13:26 +0000264 return -EINVAL;
265 }
Holger Freytherfb3f5192009-02-09 23:09:15 +0000266
Harald Welte1fa60c82009-02-09 18:13:26 +0000267 sign_link = msg->trx->bts->oml_link;
Harald Weltef55b49f2009-05-23 06:20:41 +0000268 e1i_ts = sign_link->ts;
269 if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
270 /* notify the driver we have something to write */
271 e1inp_driver = sign_link->ts->line->driver;
272 e1inp_driver->want_write(e1i_ts);
273 }
Harald Welte1fa60c82009-02-09 18:13:26 +0000274 msgb_enqueue(&sign_link->tx_list, msg);
275
Holger Freyther0469cf62009-03-31 12:14:16 +0000276 /* dump it */
277 write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
278
Harald Welte1fa60c82009-02-09 18:13:26 +0000279 return 0;
280}
281
282/* Timeslot */
283
284/* configure and initialize one e1inp_ts */
285int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
286 enum e1inp_ts_type type)
287{
288 ts->type = type;
289 ts->line = line;
290
291 switch (type) {
292 case E1INP_TS_TYPE_SIGN:
293 INIT_LLIST_HEAD(&ts->sign.sign_links);
294 break;
295 case E1INP_TS_TYPE_TRAU:
296 subchan_mux_init(&ts->trau.mux);
297 ts->trau.demux.out_cb = subch_cb;
298 ts->trau.demux.data = ts;
299 subch_demux_init(&ts->trau.demux);
300 break;
301 default:
302 fprintf(stderr, "unsupported E1 timeslot type %u\n",
303 ts->type);
304 return -EINVAL;
305 }
306 return 0;
307}
308
309static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr)
310{
311 struct e1inp_line *e1i_line;
312
313 /* iterate over global list of e1 lines */
Harald Welte44d542e2009-03-10 18:24:35 +0000314 llist_for_each_entry(e1i_line, &e1inp_line_list, list) {
Harald Welte1fa60c82009-02-09 18:13:26 +0000315 if (e1i_line->num == e1_nr)
316 return e1i_line;
317 }
318 return NULL;
319}
320
321static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr)
322{
323 struct e1inp_line *e1i_line;
324
325 e1i_line = e1inp_line_get(e1_nr);
326 if (!e1i_line)
327 return NULL;
328
329 return &e1i_line->ts[ts_nr-1];
330}
331
332struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr)
333{
334 struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr);
335
336 if (!e1i_ts)
337 return NULL;
338
339 return &e1i_ts->trau.mux;
340}
341
342/* Signalling Link */
343
344struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i,
345 u_int8_t tei, u_int8_t sapi)
346{
347 struct e1inp_sign_link *link;
348
349 llist_for_each_entry(link, &e1i->sign.sign_links, list) {
350 if (link->sapi == sapi && link->tei == tei)
351 return link;
352 }
353
354 return NULL;
355}
356
357/* create a new signalling link in a E1 timeslot */
358
359struct e1inp_sign_link *
360e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
361 struct gsm_bts_trx *trx, u_int8_t tei,
362 u_int8_t sapi)
363{
364 struct e1inp_sign_link *link;
365
366 if (ts->type != E1INP_TS_TYPE_SIGN)
367 return NULL;
368
369 link = malloc(sizeof(*link));
370 if (!link)
371 return NULL;
372
373 memset(link, 0, sizeof(*link));
374
375 link->ts = ts;
376 link->type = type;
377 INIT_LLIST_HEAD(&link->tx_list);
378 link->trx = trx;
Holger Freytherfb3f5192009-02-09 23:09:15 +0000379 link->tei = tei;
380 link->sapi = sapi;
Harald Welte1fa60c82009-02-09 18:13:26 +0000381
382 llist_add_tail(&link->list, &ts->sign.sign_links);
383
384 return link;
385}
386
387/* the E1 driver tells us he has received something on a TS */
388int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
389 u_int8_t tei, u_int8_t sapi)
390{
391 struct e1inp_sign_link *link;
392 int ret;
393
394 switch (ts->type) {
395 case E1INP_TS_TYPE_SIGN:
Harald Welte1fa60c82009-02-09 18:13:26 +0000396 /* consult the list of signalling links */
Holger Freyther0469cf62009-03-31 12:14:16 +0000397 write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
Harald Welte1fa60c82009-02-09 18:13:26 +0000398 link = e1inp_lookup_sign_link(ts, tei, sapi);
399 if (!link) {
400 fprintf(stderr, "didn't find singalling link for "
401 "tei %d, sapi %d\n", tei, sapi);
402 return -EINVAL;
403 }
404 switch (link->type) {
405 case E1INP_SIGN_OML:
406 msg->trx = link->trx;
407 ret = abis_nm_rcvmsg(msg);
408 break;
409 case E1INP_SIGN_RSL:
410 msg->trx = link->trx;
411 ret = abis_rsl_rcvmsg(msg);
412 break;
413 default:
414 ret = -EINVAL;
415 fprintf(stderr, "unknown link type %u\n", link->type);
416 break;
417 }
418 break;
419 case E1INP_TS_TYPE_TRAU:
Harald Welte9e783312009-02-17 19:02:29 +0000420 ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg));
Harald Welte1fa60c82009-02-09 18:13:26 +0000421 break;
422 default:
423 ret = -EINVAL;
424 fprintf(stderr, "unknown TS type %u\n", ts->type);
425 break;
426 }
427
428 return ret;
429}
430
431#define TSX_ALLOC_SIZE 4096
432
433/* called by driver if it wants to transmit on a given TS */
434struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
435 struct e1inp_sign_link **sign_link)
436{
437 struct e1inp_sign_link *link;
438 struct msgb *msg = NULL;
439 int len;
440
441 switch (e1i_ts->type) {
442 case E1INP_TS_TYPE_SIGN:
443 /* FIXME: implement this round robin */
444 llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) {
445 msg = msgb_dequeue(&link->tx_list);
446 if (msg) {
447 if (sign_link)
448 *sign_link = link;
449 break;
450 }
451 }
452 break;
453 case E1INP_TS_TYPE_TRAU:
454 msg = msgb_alloc(TSX_ALLOC_SIZE);
455 if (!msg)
456 return NULL;
457 len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
458 msgb_put(msg, 40);
459 break;
460 default:
461 fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
462 return NULL;
463 }
464 return msg;
465}
466
467/* called by driver in case some kind of link state event */
468int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi)
469{
470 struct e1inp_sign_link *link;
471
472 link = e1inp_lookup_sign_link(ts, tei, sapi);
473 if (!link)
474 return -EINVAL;
475
476 /* FIXME: report further upwards */
Holger Freytherff9592f2009-03-09 16:17:14 +0000477 input_event(evt, link->type, link->trx);
478 return 0;
Harald Welte1fa60c82009-02-09 18:13:26 +0000479}
480
481/* register a driver with the E1 core */
482int e1inp_driver_register(struct e1inp_driver *drv)
483{
Harald Welte44d542e2009-03-10 18:24:35 +0000484 llist_add_tail(&drv->list, &e1inp_driver_list);
Harald Welte1fa60c82009-02-09 18:13:26 +0000485 return 0;
486}
487
488/* register a line with the E1 core */
489int e1inp_line_register(struct e1inp_line *line)
490{
491 int i;
492
Harald Welted256d4f2009-03-10 19:44:48 +0000493 for (i = 0; i < NUM_E1_TS; i++) {
Harald Welte1fa60c82009-02-09 18:13:26 +0000494 line->ts[i].num = i+1;
Harald Welted256d4f2009-03-10 19:44:48 +0000495 line->ts[i].line = line;
496 }
Harald Welte1fa60c82009-02-09 18:13:26 +0000497
Harald Welte44d542e2009-03-10 18:24:35 +0000498 llist_add_tail(&line->list, &e1inp_line_list);
Harald Welte1fa60c82009-02-09 18:13:26 +0000499
500 return 0;
501}