blob: 813ec97fa405a4a8b20108754b14e7caea31bedb [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>
38#define AF_ISDN 34
39#define PF_ISDN AF_ISDN
40
41#include <openbsc/select.h>
42#include <openbsc/msgb.h>
43#include <openbsc/debug.h>
44#include <openbsc/gsm_data.h>
45#include <openbsc/e1_input.h>
46#include <openbsc/abis_nm.h>
47#include <openbsc/abis_rsl.h>
48#include <openbsc/linuxlist.h>
49#include <openbsc/subchan_demux.h>
50#include <openbsc/trau_mux.h>
51
52#define NUM_E1_TS 32
53
54/* list of all E1 drivers */
55static LLIST_HEAD(driver_list);
56
57/* list of all E1 lines */
58static LLIST_HEAD(line_list);
59
60#if 0
61/*
62 * pcap writing of the misdn load
63 * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
64 */
65#define DLT_LINUX_LAPD 177
66#define PCAP_INPUT 0
67#define PCAP_OUTPUT 1
68
69struct pcap_hdr {
70 u_int32_t magic_number;
71 u_int16_t version_major;
72 u_int16_t version_minor;
73 int32_t thiszone;
74 u_int32_t sigfigs;
75 u_int32_t snaplen;
76 u_int32_t network;
77} __attribute__((packed));
78
79struct pcaprec_hdr {
80 u_int32_t ts_sec;
81 u_int32_t ts_usec;
82 u_int32_t incl_len;
83 u_int32_t orig_len;
84} __attribute__((packed));
85
86struct fake_linux_lapd_header {
87 u_int16_t pkttype;
88 u_int16_t hatype;
89 u_int16_t halen;
90 u_int64_t addr;
91 int16_t protocol;
92} __attribute__((packed));
93
94struct lapd_header {
95 u_int8_t ea1 : 1;
96 u_int8_t cr : 1;
97 u_int8_t sapi : 6;
98 u_int8_t ea2 : 1;
99 u_int8_t tei : 7;
100 u_int8_t control_foo; /* fake UM's ... */
101} __attribute__((packed));
102
103static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset);
104static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset);
105static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset);
106static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset);
107static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
108
109
110static int pcap_fd = -1;
111
112void mi_set_pcap_fd(int fd)
113{
114 int ret;
115 struct pcap_hdr header = {
116 .magic_number = 0xa1b2c3d4,
117 .version_major = 2,
118 .version_minor = 4,
119 .thiszone = 0,
120 .sigfigs = 0,
121 .snaplen = 65535,
122 .network = DLT_LINUX_LAPD,
123 };
124
125 pcap_fd = fd;
126 ret = write(pcap_fd, &header, sizeof(header));
127}
128
129/* This currently only works for the D-Channel */
130static void write_pcap_packet(int direction, struct sockaddr_mISDN* addr,
131 struct msgb *msg) {
132 if (pcap_fd < 0)
133 return;
134
135 int ret;
136 time_t cur_time;
137 struct tm *tm;
138
139 struct fake_linux_lapd_header header = {
140 .pkttype = 4,
141 .hatype = 0,
142 .halen = 0,
143 .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1,
144 .protocol = ntohs(48),
145 };
146
147 struct lapd_header lapd_header = {
148 .ea1 = 0,
149 .cr = direction == PCAP_OUTPUT ? 1 : 0,
150 .sapi = addr->sapi & 0x3F,
151 .ea2 = 1,
152 .tei = addr->tei & 0x7F,
153 .control_foo = 0x03 /* UI */,
154 };
155
156 struct pcaprec_hdr payload_header = {
157 .ts_sec = 0,
158 .ts_usec = 0,
159 .incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
160 + sizeof(struct lapd_header)
161 - MISDN_HEADER_LEN,
162 .orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
163 + sizeof(struct lapd_header)
164 - MISDN_HEADER_LEN,
165 };
166
167
168 cur_time = time(NULL);
169 tm = localtime(&cur_time);
170 payload_header.ts_sec = mktime(tm);
171
172 ret = write(pcap_fd, &payload_header, sizeof(payload_header));
173 ret = write(pcap_fd, &header, sizeof(header));
174 ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
175 ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
176 msg->len - MISDN_HEADER_LEN);
177}
178#endif
179
180/* callback when a TRAU frame was received */
181static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
182 void *_priv)
183{
184 struct e1inp_ts *e1i_ts = _priv;
185 struct gsm_e1_subslot src_ss;
186
187 src_ss.e1_nr = e1i_ts->line->num;
188 src_ss.e1_ts = e1i_ts->num;
189 src_ss.e1_ts_ss = ch;
190
191 return trau_mux_input(&src_ss, data, len);
192}
193
194int abis_rsl_sendmsg(struct msgb *msg)
195{
196 struct e1inp_sign_link *sign_link;
197 struct e1inp_driver *e1inp_driver;
198
199 msg->l2h = msg->data;
200
201 if (!msg->trx || !msg->trx->rsl_link) {
202 fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
203 return -EINVAL;
204 }
205
206 sign_link = msg->trx->rsl_link;
207 msgb_enqueue(&sign_link->tx_list, msg);
208
209 /* notify the driver we have something to write */
210 e1inp_driver = sign_link->ts->line->driver;
211 e1inp_driver->want_write(sign_link->ts);
212
213 return 0;
214}
215
216int _abis_nm_sendmsg(struct msgb *msg)
217{
218 struct e1inp_sign_link *sign_link;
219 struct e1inp_driver *e1inp_driver;
220
221 msg->l2h = msg->data;
222
223 if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
Holger Freytherfb3f5192009-02-09 23:09:15 +0000224 fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
Harald Welte1fa60c82009-02-09 18:13:26 +0000225 return -EINVAL;
226 }
Holger Freytherfb3f5192009-02-09 23:09:15 +0000227
Harald Welte1fa60c82009-02-09 18:13:26 +0000228 sign_link = msg->trx->bts->oml_link;
229 msgb_enqueue(&sign_link->tx_list, msg);
230
231 /* notify the driver we have something to write */
232 e1inp_driver = sign_link->ts->line->driver;
233 e1inp_driver->want_write(sign_link->ts);
234
235 return 0;
236}
237
238/* Timeslot */
239
240/* configure and initialize one e1inp_ts */
241int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
242 enum e1inp_ts_type type)
243{
244 ts->type = type;
245 ts->line = line;
246
247 switch (type) {
248 case E1INP_TS_TYPE_SIGN:
249 INIT_LLIST_HEAD(&ts->sign.sign_links);
250 break;
251 case E1INP_TS_TYPE_TRAU:
252 subchan_mux_init(&ts->trau.mux);
253 ts->trau.demux.out_cb = subch_cb;
254 ts->trau.demux.data = ts;
255 subch_demux_init(&ts->trau.demux);
256 break;
257 default:
258 fprintf(stderr, "unsupported E1 timeslot type %u\n",
259 ts->type);
260 return -EINVAL;
261 }
262 return 0;
263}
264
265static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr)
266{
267 struct e1inp_line *e1i_line;
268
269 /* iterate over global list of e1 lines */
270 llist_for_each_entry(e1i_line, &line_list, list) {
271 if (e1i_line->num == e1_nr)
272 return e1i_line;
273 }
274 return NULL;
275}
276
277static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr)
278{
279 struct e1inp_line *e1i_line;
280
281 e1i_line = e1inp_line_get(e1_nr);
282 if (!e1i_line)
283 return NULL;
284
285 return &e1i_line->ts[ts_nr-1];
286}
287
288struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr)
289{
290 struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr);
291
292 if (!e1i_ts)
293 return NULL;
294
295 return &e1i_ts->trau.mux;
296}
297
298/* Signalling Link */
299
300struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i,
301 u_int8_t tei, u_int8_t sapi)
302{
303 struct e1inp_sign_link *link;
304
305 llist_for_each_entry(link, &e1i->sign.sign_links, list) {
306 if (link->sapi == sapi && link->tei == tei)
307 return link;
308 }
309
310 return NULL;
311}
312
313/* create a new signalling link in a E1 timeslot */
314
315struct e1inp_sign_link *
316e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
317 struct gsm_bts_trx *trx, u_int8_t tei,
318 u_int8_t sapi)
319{
320 struct e1inp_sign_link *link;
321
322 if (ts->type != E1INP_TS_TYPE_SIGN)
323 return NULL;
324
325 link = malloc(sizeof(*link));
326 if (!link)
327 return NULL;
328
329 memset(link, 0, sizeof(*link));
330
331 link->ts = ts;
332 link->type = type;
333 INIT_LLIST_HEAD(&link->tx_list);
334 link->trx = trx;
Holger Freytherfb3f5192009-02-09 23:09:15 +0000335 link->tei = tei;
336 link->sapi = sapi;
Harald Welte1fa60c82009-02-09 18:13:26 +0000337
338 llist_add_tail(&link->list, &ts->sign.sign_links);
339
340 return link;
341}
342
343/* the E1 driver tells us he has received something on a TS */
344int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
345 u_int8_t tei, u_int8_t sapi)
346{
347 struct e1inp_sign_link *link;
348 int ret;
349
350 switch (ts->type) {
351 case E1INP_TS_TYPE_SIGN:
352 /* FIXME: write pcap packet */
353 /* consult the list of signalling links */
354 link = e1inp_lookup_sign_link(ts, tei, sapi);
355 if (!link) {
356 fprintf(stderr, "didn't find singalling link for "
357 "tei %d, sapi %d\n", tei, sapi);
358 return -EINVAL;
359 }
360 switch (link->type) {
361 case E1INP_SIGN_OML:
362 msg->trx = link->trx;
363 ret = abis_nm_rcvmsg(msg);
364 break;
365 case E1INP_SIGN_RSL:
366 msg->trx = link->trx;
367 ret = abis_rsl_rcvmsg(msg);
368 break;
369 default:
370 ret = -EINVAL;
371 fprintf(stderr, "unknown link type %u\n", link->type);
372 break;
373 }
374 break;
375 case E1INP_TS_TYPE_TRAU:
376 ret = subch_demux_in(&ts->trau.demux, msg->data, msg->len);
377 break;
378 default:
379 ret = -EINVAL;
380 fprintf(stderr, "unknown TS type %u\n", ts->type);
381 break;
382 }
383
384 return ret;
385}
386
387#define TSX_ALLOC_SIZE 4096
388
389/* called by driver if it wants to transmit on a given TS */
390struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
391 struct e1inp_sign_link **sign_link)
392{
393 struct e1inp_sign_link *link;
394 struct msgb *msg = NULL;
395 int len;
396
397 switch (e1i_ts->type) {
398 case E1INP_TS_TYPE_SIGN:
399 /* FIXME: implement this round robin */
400 llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) {
401 msg = msgb_dequeue(&link->tx_list);
402 if (msg) {
403 if (sign_link)
404 *sign_link = link;
405 break;
406 }
407 }
408 break;
409 case E1INP_TS_TYPE_TRAU:
410 msg = msgb_alloc(TSX_ALLOC_SIZE);
411 if (!msg)
412 return NULL;
413 len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
414 msgb_put(msg, 40);
415 break;
416 default:
417 fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
418 return NULL;
419 }
420 return msg;
421}
422
423/* called by driver in case some kind of link state event */
424int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi)
425{
426 struct e1inp_sign_link *link;
427
428 link = e1inp_lookup_sign_link(ts, tei, sapi);
429 if (!link)
430 return -EINVAL;
431
432 /* FIXME: report further upwards */
433 return input_event(evt, link->type, link->trx);
434}
435
436/* register a driver with the E1 core */
437int e1inp_driver_register(struct e1inp_driver *drv)
438{
439 llist_add_tail(&drv->list, &driver_list);
440 return 0;
441}
442
443/* register a line with the E1 core */
444int e1inp_line_register(struct e1inp_line *line)
445{
446 int i;
447
448 for (i = 0; i < NUM_E1_TS; i++)
449 line->ts[i].num = i+1;
450
451 llist_add_tail(&line->list, &line_list);
452
453 return 0;
454}