blob: 9d03eee65ba1449a9c91dbbdda6b4ce3a5ca8915 [file] [log] [blame]
Matthew Fredricksond105e202010-02-16 22:07:04 +01001/* OpenBSC Abis input driver for DAHDI */
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +01002
3/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
Matthew Fredricksond105e202010-02-16 22:07:04 +01005 * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +01006 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <stdio.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <string.h>
30#include <time.h>
31#include <sys/fcntl.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/ioctl.h>
35#include <arpa/inet.h>
36#include <mISDNif.h>
37
38//#define AF_COMPATIBILITY_FUNC
39//#include <compat_af_isdn.h>
40#ifndef AF_ISDN
41#define AF_ISDN 34
42#define PF_ISDN AF_ISDN
43#endif
44
45#include <openbsc/select.h>
46#include <openbsc/msgb.h>
47#include <openbsc/debug.h>
48#include <openbsc/gsm_data.h>
49#include <openbsc/abis_nm.h>
50#include <openbsc/abis_rsl.h>
51#include <openbsc/subchan_demux.h>
52#include <openbsc/e1_input.h>
53#include <openbsc/talloc.h>
54
55#define TS1_ALLOC_SIZE 300
56
57struct prim_name {
58 unsigned int prim;
59 const char *name;
60};
61
62const struct prim_name prim_names[] = {
63 { PH_CONTROL_IND, "PH_CONTROL_IND" },
64 { PH_DATA_IND, "PH_DATA_IND" },
65 { PH_DATA_CNF, "PH_DATA_CNF" },
66 { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" },
67 { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" },
68 { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" },
69 { DL_RELEASE_IND, "DL_RELEASE_IND" },
70 { DL_RELEASE_CNF, "DL_RELEASE_CNF" },
71 { DL_DATA_IND, "DL_DATA_IND" },
72 { DL_UNITDATA_IND, "DL_UNITDATA_IND" },
73 { DL_INFORMATION_IND, "DL_INFORMATION_IND" },
74 { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" },
75 { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" },
76};
77
78const char *get_prim_name(unsigned int prim)
79{
80 int i;
81
82 for (i = 0; i < ARRAY_SIZE(prim_names); i++) {
83 if (prim_names[i].prim == prim)
84 return prim_names[i].name;
85 }
86
87 return "UNKNOWN";
88}
89
90static int handle_ts1_read(struct bsc_fd *bfd)
91{
92 struct e1inp_line *line = bfd->data;
93 unsigned int ts_nr = bfd->priv_nr;
94 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
95 struct e1inp_sign_link *link;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010096 struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1");
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +010097 struct sockaddr_mISDN l2addr;
98 struct mISDNhead *hh;
99 socklen_t alen;
100 int ret;
101
102 if (!msg)
103 return -ENOMEM;
104
105 hh = (struct mISDNhead *) msg->data;
106
107 alen = sizeof(l2addr);
108 ret = recvfrom(bfd->fd, msg->data, 300, 0,
109 (struct sockaddr *) &l2addr, &alen);
110 if (ret < 0) {
111 fprintf(stderr, "recvfrom error %s\n", strerror(errno));
112 return ret;
113 }
114
115 if (alen != sizeof(l2addr)) {
116 fprintf(stderr, "%s error len\n", __func__);
117 return -EINVAL;
118 }
119
120 msgb_put(msg, ret);
121
122 DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
123 alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
124
125 DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
126 ret, hh->prim, hh->id, get_prim_name(hh->prim));
127
128 switch (hh->prim) {
129 case DL_INFORMATION_IND:
130 /* mISDN tells us which channel number is allocated for this
131 * tuple of (SAPI, TEI). */
132 DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
133 l2addr.channel, l2addr.sapi, l2addr.tei);
134 link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
135 if (!link) {
136 DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
137 msgb_free(msg);
138 return -EINVAL;
139 }
140 /* save the channel number in the driver private struct */
141 link->driver.misdn.channel = l2addr.channel;
142 break;
143 case DL_ESTABLISH_IND:
144 DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
145 l2addr.channel, l2addr.sapi, l2addr.tei);
146 ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi);
147 break;
148 case DL_RELEASE_IND:
149 DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
150 l2addr.channel, l2addr.sapi, l2addr.tei);
151 ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi);
152 break;
153 case DL_DATA_IND:
154 case DL_UNITDATA_IND:
155 msg->l2h = msg->data + MISDN_HEADER_LEN;
156 DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
157 ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
158 break;
159 case PH_ACTIVATE_IND:
160 DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
161 l2addr.channel, l2addr.sapi, l2addr.tei);
162 break;
163 case PH_DEACTIVATE_IND:
164 DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
165 l2addr.channel, l2addr.sapi, l2addr.tei);
166 break;
167 default:
168 break;
169 }
170 return ret;
171}
172
173static int ts_want_write(struct e1inp_ts *e1i_ts)
174{
175 /* We never include the mISDN B-Channel FD into the
176 * writeset, since it doesn't support poll() based
177 * write flow control */
178 if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
179 return 0;
180
181 e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE;
182
183 return 0;
184}
185
186static void timeout_ts1_write(void *data)
187{
188 struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
189
190 /* trigger write of ts1, due to tx delay timer */
191 ts_want_write(e1i_ts);
192}
193
194static int handle_ts1_write(struct bsc_fd *bfd)
195{
196 struct e1inp_line *line = bfd->data;
197 unsigned int ts_nr = bfd->priv_nr;
198 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
199 struct e1inp_sign_link *sign_link;
200 struct sockaddr_mISDN sa;
201 struct msgb *msg;
202 struct mISDNhead *hh;
203 u_int8_t *l2_data;
204 int ret;
205
206 bfd->when &= ~BSC_FD_WRITE;
207
208 /* get the next msg for this timeslot */
209 msg = e1inp_tx_ts(e1i_ts, &sign_link);
210 if (!msg) {
211 /* no message after tx delay timer */
212 return 0;
213 }
214
215 l2_data = msg->data;
216
217 /* prepend the mISDNhead */
218 hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
219 hh->prim = DL_DATA_REQ;
220
221 DEBUGP(DMI, "TX TEI(%d) SAPI(%d): %s\n", sign_link->tei,
222 sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN));
223
224 /* construct the sockaddr */
225 sa.family = AF_ISDN;
226 sa.sapi = sign_link->sapi;
227 sa.dev = sign_link->tei;
228 sa.channel = sign_link->driver.misdn.channel;
229
230 ret = sendto(bfd->fd, msg->data, msg->len, 0,
231 (struct sockaddr *)&sa, sizeof(sa));
232 if (ret < 0)
233 fprintf(stderr, "%s sendto failed %d\n", __func__, ret);
234 msgb_free(msg);
235
236 /* set tx delay timer for next event */
237 e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
238 e1i_ts->sign.tx_timer.data = e1i_ts;
239 bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
240
241 return ret;
242}
243
244#define BCHAN_TX_GRAN 160
245/* write to a B channel TS */
246static int handle_tsX_write(struct bsc_fd *bfd)
247{
248 struct e1inp_line *line = bfd->data;
249 unsigned int ts_nr = bfd->priv_nr;
250 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
251 struct mISDNhead *hh;
252 u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)];
253 struct subch_mux *mx = &e1i_ts->trau.mux;
254 int ret;
255
256 hh = (struct mISDNhead *) tx_buf;
257 hh->prim = PH_DATA_REQ;
258
259 subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN);
260
261 DEBUGP(DMIB, "BCHAN TX: %s\n",
262 hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN));
263
264 ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
265 if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
266 DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
267 sizeof(*hh) + BCHAN_TX_GRAN);
268
269 return ret;
270}
271
272#define TSX_ALLOC_SIZE 4096
273/* FIXME: read from a B channel TS */
274static int handle_tsX_read(struct bsc_fd *bfd)
275{
276 struct e1inp_line *line = bfd->data;
277 unsigned int ts_nr = bfd->priv_nr;
278 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
279 struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "DAHDI TSx");
280 struct mISDNhead *hh;
281 int ret;
282
283 if (!msg)
284 return -ENOMEM;
285
286 hh = (struct mISDNhead *) msg->data;
287
288 ret = read(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
289 if (ret < 0) {
290 fprintf(stderr, "read error %s\n", strerror(errno));
291 return ret;
292 }
293
294 msgb_put(msg, ret - 2);
295
296 if (hh->prim != PH_CONTROL_IND)
297 DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
298 ret, hh->prim, hh->id, get_prim_name(hh->prim));
299
300 switch (hh->prim) {
301 case PH_DATA_IND:
302 msg->l2h = msg->data + MISDN_HEADER_LEN;
303 DEBUGP(DMIB, "BCHAN RX: %s\n",
304 hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
305 ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
306 break;
307 case PH_ACTIVATE_IND:
308 case PH_DATA_CNF:
309 /* physical layer indicates that data has been sent,
310 * we thus can send some more data */
311 ret = handle_tsX_write(bfd);
312 default:
313 break;
314 }
315 /* FIXME: why do we free signalling msgs in the caller, and trau not? */
316 msgb_free(msg);
317
318 return ret;
319}
320
321/* callback from select.c in case one of the fd's can be read/written */
322static int dahdi_fd_cb(struct bsc_fd *bfd, unsigned int what)
323{
324 struct e1inp_line *line = bfd->data;
325 unsigned int ts_nr = bfd->priv_nr;
326 unsigned int idx = ts_nr-1;
327 struct e1inp_ts *e1i_ts = &line->ts[idx];
328 int rc = 0;
329
330 switch (e1i_ts->type) {
331 case E1INP_TS_TYPE_SIGN:
332 if (what & BSC_FD_READ)
333 rc = handle_ts1_read(bfd);
334 if (what & BSC_FD_WRITE)
335 rc = handle_ts1_write(bfd);
336 break;
337 case E1INP_TS_TYPE_TRAU:
338 if (what & BSC_FD_READ)
339 rc = handle_tsX_read(bfd);
340 /* We never include the mISDN B-Channel FD into the
341 * writeset, since it doesn't support poll() based
342 * write flow control */
343 break;
344 default:
345 fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
346 break;
347 }
348
349 return rc;
350}
351
352#if 0
353static int activate_bchan(struct e1inp_line *line, int ts, int act)
354{
355 struct mISDNhead hh;
356 int ret;
357 unsigned int idx = ts-1;
358 struct e1inp_ts *e1i_ts = &line->ts[idx];
359 struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
360
361 fprintf(stdout, "activate bchan\n");
362 if (act)
363 hh.prim = PH_ACTIVATE_REQ;
364 else
365 hh.prim = PH_DEACTIVATE_REQ;
366
367 hh.id = MISDN_ID_ANY;
368 ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
369 if (ret < 0) {
370 fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
371 strerror(errno));
372 }
373
374 return ret;
375}
376#endif
377
378struct e1inp_driver dahdi_driver = {
379 .name = "DAHDI",
380 .want_write = ts_want_write,
381};
382
383static int mi_e1_setup(struct e1inp_line *line, int release_l2)
384{
385 int ts, ret;
386
387 /* TS0 is CRC4, don't need any fd for it */
388 for (ts = 1; ts < NUM_E1_TS; ts++) {
389 unsigned int idx = ts-1;
390 char openstr[128];
391 struct e1inp_ts *e1i_ts = &line->ts[idx];
392 struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +0100393
394 bfd->data = line;
395 bfd->priv_nr = ts;
396 bfd->cb = dahdi_fd_cb;
397 snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", ts);
398
399 switch (e1i_ts->type) {
400 case E1INP_TS_TYPE_NONE:
401 continue;
402 break;
403 case E1INP_TS_TYPE_SIGN:
404 bfd->fd = open(openstr, O_RDWR);
405 bfd->when = BSC_FD_READ;
406 break;
407 case E1INP_TS_TYPE_TRAU:
408 bfd->fd = open(openstr, O_RDWR);
409 /* We never include the mISDN B-Channel FD into the
410 * writeset, since it doesn't support poll() based
411 * write flow control */
412 bfd->when = BSC_FD_READ;
413 break;
414 }
415
416 if (bfd->fd < 0) {
417 fprintf(stderr, "%s could not open %s %s\n",
418 __func__, openstr, strerror(errno));
419 return bfd->fd;
420 }
421
422 ret = bsc_register_fd(bfd);
423 if (ret < 0) {
424 fprintf(stderr, "could not register FD: %s\n",
425 strerror(ret));
426 return ret;
427 }
428 }
429
430 return 0;
431}
432
433int mi_e1_line_update(struct e1inp_line *line)
434{
435 struct mISDN_devinfo devinfo;
436 int sk, ret, cnt;
437
438 if (!line->driver) {
439 /* this must be the first update */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100440 line->driver = &dahdi_driver;
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +0100441 } else {
442 /* this is a subsequent update */
443 /* FIXME: first close all sockets */
444 fprintf(stderr, "incremental line updates not supported yet\n");
445 return 0;
446 }
447
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100448 if (line->driver != &dahdi_driver)
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +0100449 return -EINVAL;
450
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100451#if 0
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +0100452 /* open the ISDN card device */
453 sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
454 if (sk < 0) {
455 fprintf(stderr, "%s could not open socket %s\n",
456 __func__, strerror(errno));
457 return sk;
458 }
459
460 ret = ioctl(sk, IMGETCOUNT, &cnt);
461 if (ret) {
462 fprintf(stderr, "%s error getting interf count: %s\n",
463 __func__, strerror(errno));
464 close(sk);
465 return -ENODEV;
466 }
467 //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
468 printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
469#if 1
470 devinfo.id = line->num;
471 ret = ioctl(sk, IMGETDEVINFO, &devinfo);
472 if (ret < 0) {
473 fprintf(stdout, "error getting info for device %d: %s\n",
474 line->num, strerror(errno));
475 return -ENODEV;
476 }
477 fprintf(stdout, " id: %d\n", devinfo.id);
478 fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
479 fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
480 fprintf(stdout, " protocol: %d\n", devinfo.protocol);
481 fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
482 fprintf(stdout, " name: %s\n", devinfo.name);
483#endif
484
485 if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) {
486 fprintf(stderr, "error: card is not of type E1 (NT-mode)\n");
487 return -EINVAL;
488 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100489#endif
Matthew Fredricksonb5ddc182010-02-16 21:55:50 +0100490
491 ret = mi_e1_setup(line, 1);
492 if (ret)
493 return ret;
494
495 return 0;
496}
497
498static __attribute__((constructor)) void on_dso_load_sms(void)
499{
500 /* register the driver with the core */
501 e1inp_driver_register(&dahdi_driver);
502}