blob: 034bd97235776204d05f01b4b9c63338aabaf517 [file] [log] [blame]
Harald Welte59b04682009-06-10 05:40:52 +08001/* 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>
38#ifndef AF_ISDN
39#define AF_ISDN 34
40#define PF_ISDN AF_ISDN
41#endif
42
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>
52#include <openbsc/trau_frame.h>
53#include <openbsc/trau_mux.h>
54
55#define NUM_E1_TS 32
56
57/* list of all E1 drivers */
58LLIST_HEAD(e1inp_driver_list);
59
60/* list of all E1 lines */
61LLIST_HEAD(e1inp_line_list);
62
63/* 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
66/*
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
117void e1_set_pcap_fd(int fd)
118{
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 */
135static void write_pcap_packet(int direction, int sapi, int tei,
136 struct msgb *msg) {
137 if (pcap_fd < 0)
138 return;
139
140 int ret;
141 time_t cur_time;
142 struct tm *tm;
Andreas Eversbergb5a3e412009-06-10 14:47:33 +0200143 int mi_head = (direction==PCAP_INPUT) ? MISDN_HEADER_LEN : 0;
Harald Welte59b04682009-06-10 05:40:52 +0800144
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,
156 .sapi = sapi & 0x3F,
157 .ea2 = 1,
158 .tei = tei & 0x7F,
159 .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 Eversbergb5a3e412009-06-10 14:47:33 +0200167 - mi_head,
Harald Welte59b04682009-06-10 05:40:52 +0800168 .orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
169 + sizeof(struct lapd_header)
Andreas Eversbergb5a3e412009-06-10 14:47:33 +0200170 - mi_head,
Harald Welte59b04682009-06-10 05:40:52 +0800171 };
172
173
Andreas Eversbergb5a3e412009-06-10 14:47:33 +0200174 printf("Packet of: %d\n", direction);
175
Harald Welte59b04682009-06-10 05:40:52 +0800176 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 Eversbergb5a3e412009-06-10 14:47:33 +0200183 ret = write(pcap_fd, msg->data + mi_head,
184 msg->len - mi_head);
Harald Welte59b04682009-06-10 05:40:52 +0800185}
186
187static 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
212/* 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;
230 struct e1inp_ts *e1i_ts;
231
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;
240 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 }
246 msgb_enqueue(&sign_link->tx_list, msg);
247
248 /* dump it */
249 write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
250
251 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;
258 struct e1inp_ts *e1i_ts;
259
260 msg->l2h = msg->data;
261
262 if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
263 fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
264 return -EINVAL;
265 }
266
267 sign_link = msg->trx->bts->oml_link;
268 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 }
274 msgb_enqueue(&sign_link->tx_list, msg);
275
276 /* dump it */
277 write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
278
279 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 */
314 llist_for_each_entry(e1i_line, &e1inp_line_list, list) {
315 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;
379 link->tei = tei;
380 link->sapi = sapi;
381
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:
396 /* consult the list of signalling links */
397 write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
398 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:
420 ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg));
421 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 */
477 input_event(evt, link->type, link->trx);
478 return 0;
479}
480
481/* register a driver with the E1 core */
482int e1inp_driver_register(struct e1inp_driver *drv)
483{
484 llist_add_tail(&drv->list, &e1inp_driver_list);
485 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
493 for (i = 0; i < NUM_E1_TS; i++) {
494 line->ts[i].num = i+1;
495 line->ts[i].line = line;
496 }
497
498 llist_add_tail(&line->list, &e1inp_line_list);
499
500 return 0;
501}