blob: ab378da3440cd93d09ed9578db44981fc070f9bf [file] [log] [blame]
Harald Welte8470bf22008-12-25 23:28:35 +00001/* OpenBSC Abis interface to mISDNuser */
2
3/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
Harald Welte52b1f982008-12-23 20:25:15 +00004 *
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>
Harald Weltee9a82612008-12-27 16:45:29 +000028#include <sys/fcntl.h>
Harald Welte52b1f982008-12-23 20:25:15 +000029#include <sys/types.h>
30#include <sys/socket.h>
31#include <sys/ioctl.h>
32#include <mISDNif.h>
33
Harald Welte8470bf22008-12-25 23:28:35 +000034//#define AF_COMPATIBILITY_FUNC
35//#include <compat_af_isdn.h>
36#define AF_ISDN 34
37#define PF_ISDN AF_ISDN
38
39#include <openbsc/select.h>
40#include <openbsc/msgb.h>
41#include <openbsc/debug.h>
42#include <openbsc/gsm_data.h>
43#include <openbsc/abis_nm.h>
44#include <openbsc/abis_rsl.h>
Harald Welte52b1f982008-12-23 20:25:15 +000045
46#define NUM_E1_TS 32
47
48/* data structure for one E1 interface with A-bis */
49struct mi_e1_handle {
50 struct gsm_bts *bts;
Harald Welte52b1f982008-12-23 20:25:15 +000051 /* The mISDN card number of the card we use */
52 int cardnr;
Harald Welte52b1f982008-12-23 20:25:15 +000053 /* The RSL adress */
54 struct sockaddr_mISDN l2addr;
Harald Welte52b1f982008-12-23 20:25:15 +000055 /* The OML adress */
56 struct sockaddr_mISDN omladdr;
Harald Welte8470bf22008-12-25 23:28:35 +000057 /* list (queue) of to-be-sent msgb's */
58 struct llist_head rsl_tx_list;
59 struct llist_head oml_tx_list;
Harald Welte52b1f982008-12-23 20:25:15 +000060
Harald Weltead384642008-12-26 10:20:07 +000061 void (*cb)(int event, struct gsm_bts *bts);
Harald Welte8470bf22008-12-25 23:28:35 +000062 struct bsc_fd fd[NUM_E1_TS];
Harald Weltee9a82612008-12-27 16:45:29 +000063
64 int ts2_fd;
Harald Welte52b1f982008-12-23 20:25:15 +000065};
66
Harald Welte8470bf22008-12-25 23:28:35 +000067/* FIXME: this needs to go */
68static struct mi_e1_handle *global_e1h;
69
Harald Welte52b1f982008-12-23 20:25:15 +000070#define SAPI_L2ML 0
71#define SAPI_OML 62
Harald Weltead384642008-12-26 10:20:07 +000072#define SAPI_RSL 0 /* 63 ? */
Harald Welte52b1f982008-12-23 20:25:15 +000073
74#define TEI_L2ML 127
75#define TEI_OML 25
76#define TEI_RSL 1
77
Harald Welte702d8702008-12-26 20:25:35 +000078void hexdump(unsigned char *buf, int len)
Harald Welte52b1f982008-12-23 20:25:15 +000079{
80 int i;
81 for (i = 0; i < len; i++) {
82 fprintf(stdout, "%02x ", buf[i]);
83 }
84 fprintf(stdout, "\n");
85}
86
87#define TS1_ALLOC_SIZE 300
88
89static int handle_ts1_read(struct bsc_fd *bfd)
90{
91 struct mi_e1_handle *e1h = bfd->data;
92 struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
Harald Welte8470bf22008-12-25 23:28:35 +000093 struct sockaddr_mISDN l2addr;
94 struct mISDNhead *hh;
Harald Welte52b1f982008-12-23 20:25:15 +000095 socklen_t alen;
Harald Welte8470bf22008-12-25 23:28:35 +000096 int ret;
Harald Welte52b1f982008-12-23 20:25:15 +000097
98 if (!msg)
99 return -ENOMEM;
100
Harald Welte8470bf22008-12-25 23:28:35 +0000101 hh = (struct mISDNhead *) msg->data;
102
103 /* FIXME: Map TEI/SAPI to TRX */
104 msg->trx = e1h->bts->c0;
Harald Welte52b1f982008-12-23 20:25:15 +0000105
106 alen = sizeof(l2addr);
107 ret = recvfrom(bfd->fd, msg->data, 300, 0,
108 (struct sockaddr *) &l2addr, &alen);
109 if (ret < 0) {
110 fprintf(stderr, "recvfrom error %s\n", strerror(errno));
111 return ret;
112 }
113
114 if (alen != sizeof(l2addr))
115 return -EINVAL;
116
117 msgb_put(msg, ret);
118
119 DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
120 alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
121
122 DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x)\n",
123 ret, hh->prim, hh->id);
124
125 switch (hh->prim) {
126 case DL_INFORMATION_IND:
127 DEBUGP(DMI, "got DL_INFORMATION_IND\n");
Harald Welte8470bf22008-12-25 23:28:35 +0000128 struct sockaddr_mISDN *sa = NULL;
Harald Welte52b1f982008-12-23 20:25:15 +0000129 char *lstr = "UNKN";
130
131 switch (l2addr.tei) {
132 case TEI_OML:
133 sa = &e1h->omladdr;
134 lstr = "OML";
135 break;
136 case TEI_RSL:
137 sa = &e1h->l2addr;
138 lstr = "RSL";
139 break;
140 default:
Harald Welte8470bf22008-12-25 23:28:35 +0000141 break;
Harald Welte52b1f982008-12-23 20:25:15 +0000142 }
Harald Welte8470bf22008-12-25 23:28:35 +0000143 if (sa) {
144 DEBUGP(DMI, "%s use channel(%d) sapi(%d) tei(%d) for now\n",
145 lstr, l2addr.channel, l2addr.sapi, l2addr.tei);
146 memcpy(sa, &l2addr, sizeof(l2addr));
147 }
Harald Welte52b1f982008-12-23 20:25:15 +0000148 break;
149 case DL_ESTABLISH_IND:
150 DEBUGP(DMI, "got DL_ESTABLISH_IND\n");
151 break;
152 case DL_ESTABLISH_CNF:
153 DEBUGP(DMI, "got DL_ESTABLISH_CNF\n");
154 break;
155 case DL_RELEASE_IND:
Harald Weltead384642008-12-26 10:20:07 +0000156 DEBUGP(DMI, "got DL_RELEASE_IND: E1 Layer 1 disappeared?\n");
Harald Welte52b1f982008-12-23 20:25:15 +0000157 break;
158 case MPH_ACTIVATE_IND:
159 DEBUGP(DMI, "got MPH_ACTIVATE_IND\n");
Harald Weltead384642008-12-26 10:20:07 +0000160 if (l2addr.tei == TEI_OML && l2addr.sapi == SAPI_OML)
161 e1h->cb(EVT_E1_OML_UP, e1h->bts);
162 else if (l2addr.tei == TEI_RSL && l2addr.sapi == SAPI_RSL)
163 e1h->cb(EVT_E1_RSL_UP, e1h->bts);
Harald Welte52b1f982008-12-23 20:25:15 +0000164 break;
165 case MPH_DEACTIVATE_IND:
Harald Weltead384642008-12-26 10:20:07 +0000166 DEBUGP(DMI, "got MPH_DEACTIVATE_IND: TEI link closed?\n");
167 if (l2addr.tei == TEI_OML && l2addr.sapi == SAPI_OML)
168 e1h->cb(EVT_E1_OML_DN, e1h->bts);
169 else if (l2addr.tei == TEI_RSL && l2addr.sapi == SAPI_RSL)
170 e1h->cb(EVT_E1_RSL_DN, e1h->bts);
171 break;
Harald Welte52b1f982008-12-23 20:25:15 +0000172 case DL_DATA_IND:
173 DEBUGP(DMI, "got DL_DATA_IND\n");
Harald Weltead384642008-12-26 10:20:07 +0000174
Harald Weltead384642008-12-26 10:20:07 +0000175 msg->l2h = msg->data + MISDN_HEADER_LEN;
176
Harald Welte6cc38d72008-12-30 14:58:19 +0000177 if (debug_mask & DMI) {
178 fprintf(stdout, "RX: ");
179 hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN);
180 }
Harald Welte52b1f982008-12-23 20:25:15 +0000181 switch (l2addr.tei) {
182 case TEI_OML:
183 ret = abis_nm_rcvmsg(msg);
184 break;
185 case TEI_RSL:
186 ret = abis_rsl_rcvmsg(msg);
187 break;
188 default:
189 fprintf(stderr, "DATA_IND for unknown TEI\n");
190 break;
191 }
192 break;
193 default:
194 DEBUGP(DMI, "got unexpected 0x%x prim\n", hh->prim);
195 break;
196 }
197 return ret;
198}
199
200static int handle_ts1_write(struct bsc_fd *bfd)
201{
202 struct mi_e1_handle *e1h = bfd->data;
Harald Welte8470bf22008-12-25 23:28:35 +0000203 struct msgb *msg;
204 struct mISDNhead *hh;
Harald Weltead384642008-12-26 10:20:07 +0000205 int ret, no_oml = 0;
Harald Welte52b1f982008-12-23 20:25:15 +0000206
Harald Weltead384642008-12-26 10:20:07 +0000207 msg = msgb_dequeue(&e1h->oml_tx_list);
Harald Welte8470bf22008-12-25 23:28:35 +0000208 if (!msg)
Harald Weltead384642008-12-26 10:20:07 +0000209 no_oml = 1;
Harald Welte8470bf22008-12-25 23:28:35 +0000210 else {
Harald Weltead384642008-12-26 10:20:07 +0000211 u_int8_t *l2_data = msg->data;
212
Harald Welte8470bf22008-12-25 23:28:35 +0000213 /* prepend the mISDNhead */
214 hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
215 hh->prim = DL_DATA_REQ;
Harald Welte52b1f982008-12-23 20:25:15 +0000216
Harald Welte6cc38d72008-12-30 14:58:19 +0000217 if (debug_mask & DMI) {
218 fprintf(stdout, "OML TX: ");
219 hexdump(l2_data, msg->len - MISDN_HEADER_LEN);
220 }
Harald Welte8470bf22008-12-25 23:28:35 +0000221 ret = sendto(bfd->fd, msg->data, msg->len, 0,
Harald Welte5d2f8ec2008-12-26 10:46:24 +0000222 (struct sockaddr *)&e1h->omladdr,
223 sizeof(e1h->omladdr));
Harald Weltee571fb82008-12-25 23:50:30 +0000224 msgb_free(msg);
Harald Welte8470bf22008-12-25 23:28:35 +0000225 usleep(100000);
Harald Weltead384642008-12-26 10:20:07 +0000226 /* we always dequeue all OML messages */
227 return ret;
Harald Welte8470bf22008-12-25 23:28:35 +0000228 }
Harald Weltead384642008-12-26 10:20:07 +0000229
Harald Welte8470bf22008-12-25 23:28:35 +0000230 msg = msgb_dequeue(&e1h->rsl_tx_list);
231 if (!msg) {
Harald Weltead384642008-12-26 10:20:07 +0000232 if (no_oml)
Harald Welte8470bf22008-12-25 23:28:35 +0000233 bfd->when &= ~BSC_FD_WRITE;
234 } else {
Harald Weltead384642008-12-26 10:20:07 +0000235 u_int8_t *l2_data = msg->data;
236
Harald Welte8470bf22008-12-25 23:28:35 +0000237 /* prepend the mISDNhead */
238 hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
239 hh->prim = DL_DATA_REQ;
240
Harald Welte6cc38d72008-12-30 14:58:19 +0000241 if (debug_mask & DMI) {
242 fprintf(stdout, "RSL TX: ");
243 hexdump(l2_data, msg->len - MISDN_HEADER_LEN);
244 }
Harald Weltead384642008-12-26 10:20:07 +0000245
Harald Welte8470bf22008-12-25 23:28:35 +0000246 ret = sendto(bfd->fd, msg->data, msg->len, 0,
Harald Welte5d2f8ec2008-12-26 10:46:24 +0000247 (struct sockaddr *)&e1h->l2addr,
248 sizeof(e1h->l2addr));
Harald Weltee571fb82008-12-25 23:50:30 +0000249 msgb_free(msg);
Harald Welteb2750422008-12-27 11:24:23 +0000250 usleep(10000);
Harald Welte702d8702008-12-26 20:25:35 +0000251 //sleep(1);
Harald Welte8470bf22008-12-25 23:28:35 +0000252 }
253
254 return ret;
Harald Welte52b1f982008-12-23 20:25:15 +0000255}
256
Harald Weltee9a82612008-12-27 16:45:29 +0000257#define TSX_ALLOC_SIZE 4096
258
259/* FIXME: read from a B channel TS */
Harald Welte52b1f982008-12-23 20:25:15 +0000260static int handle_tsX_read(struct bsc_fd *bfd)
261{
Harald Weltee9a82612008-12-27 16:45:29 +0000262 struct mi_e1_handle *e1h = bfd->data;
263 struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE);
264 struct mISDNhead *hh;
265 int ret;
266
267 if (!msg)
268 return -ENOMEM;
269
270 hh = (struct mISDNhead *) msg->data;
271
272 /* FIXME: Map TEI/SAPI to TRX */
273 msg->trx = e1h->bts->c0;
274
275 ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
276 if (ret < 0) {
277 fprintf(stderr, "recvfrom error %s\n", strerror(errno));
278 return ret;
279 }
280
281 msgb_put(msg, ret);
282
283 DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x)\n", ret, hh->prim, hh->id);
284
285 switch (hh->prim) {
286 case PH_CONTROL_IND:
287 DEBUGP(DMIB, "got PH_CONTROL_IND\n");
288 break;
289 case PH_DATA_IND:
290 DEBUGP(DMIB, "got PH_DATA_IND\n");
291
292 msg->l2h = msg->data + MISDN_HEADER_LEN;
293
Harald Welte6cc38d72008-12-30 14:58:19 +0000294 if (debug_mask & DMIB) {
295 fprintf(stdout, "BCHAN RX: ");
296 hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN);
297 }
Harald Weltee9a82612008-12-27 16:45:29 +0000298 if (!e1h->ts2_fd)
299 e1h->ts2_fd = open("/tmp/ts2.dump", O_WRONLY|O_APPEND|O_CREAT, 0660);
300
301 write(e1h->ts2_fd, msgb_l2(msg), ret - MISDN_HEADER_LEN);
302
303 break;
304 default:
305 DEBUGP(DMIB, "got unexpected 0x%x prim\n", hh->prim);
306 break;
307 }
Harald Weltee27bb342008-12-29 06:06:35 +0000308 /* FIXME: don't free it if we still hold reference! */
309 msgb_free(msg);
310
Harald Weltee9a82612008-12-27 16:45:29 +0000311 return ret;
Harald Welte52b1f982008-12-23 20:25:15 +0000312}
313
Harald Welte8470bf22008-12-25 23:28:35 +0000314static int handle_tsX_write(struct bsc_fd *bfd)
Harald Welte52b1f982008-12-23 20:25:15 +0000315{
316 /* FIXME: write to a B channel TS */
Holger Freyther059c2542008-12-27 09:39:48 +0000317 return -1;
Harald Welte52b1f982008-12-23 20:25:15 +0000318}
319
320/* callback from select.c in case one of the fd's can be read/written */
Harald Welte8470bf22008-12-25 23:28:35 +0000321static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what)
Harald Welte52b1f982008-12-23 20:25:15 +0000322{
323 unsigned int e1_ts = bfd->priv_nr;
324 int rc = 0;
325
326 switch (e1_ts) {
327 case 1:
328 if (what & BSC_FD_READ)
329 rc = handle_ts1_read(bfd);
330 if (what & BSC_FD_WRITE)
331 rc = handle_ts1_write(bfd);
332 break;
333 default:
334 if (what & BSC_FD_READ)
335 rc = handle_tsX_read(bfd);
336 if (what & BSC_FD_WRITE)
337 rc = handle_tsX_write(bfd);
338 break;
339 }
340
341 return rc;
342}
343
Harald Welte8470bf22008-12-25 23:28:35 +0000344int abis_rsl_sendmsg(struct msgb *msg)
Harald Welte52b1f982008-12-23 20:25:15 +0000345{
Harald Welte8470bf22008-12-25 23:28:35 +0000346 struct mi_e1_handle *e1h = global_e1h;
347
Harald Weltead384642008-12-26 10:20:07 +0000348 msg->l2h = msg->data;
Harald Welte8470bf22008-12-25 23:28:35 +0000349 msgb_enqueue(&e1h->rsl_tx_list, msg);
350 e1h->fd[0].when |= BSC_FD_WRITE;
351
352 return 0;
353}
354
Harald Weltead384642008-12-26 10:20:07 +0000355int _abis_nm_sendmsg(struct msgb *msg)
Harald Welte8470bf22008-12-25 23:28:35 +0000356{
357 struct mi_e1_handle *e1h = global_e1h;
358
Harald Weltead384642008-12-26 10:20:07 +0000359 msg->l2h = msg->data;
Harald Welte8470bf22008-12-25 23:28:35 +0000360 msgb_enqueue(&e1h->oml_tx_list, msg);
361 e1h->fd[0].when |= BSC_FD_WRITE;
362
363 return 0;
364}
365
Harald Weltee9a82612008-12-27 16:45:29 +0000366static int activate_bchan(struct mi_e1_handle *e1h, int ts)
367{
368 struct mISDNhead hh;
369 int ret;
370 struct bsc_fd *bfd = &e1h->fd[ts-1];
371
372 fprintf(stdout, "activate bchan\n");
373 hh.prim = PH_ACTIVATE_REQ;
374 hh.id = MISDN_ID_ANY;
375 ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
376 if (ret < 0) {
377 fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
378 strerror(errno));
379 return 0;
380 }
381
382 return ret;
383}
384
Harald Welte8470bf22008-12-25 23:28:35 +0000385static int mi_e1_setup(struct mi_e1_handle *e1h)
386{
387 int ts, sk, ret, cnt;
388 struct mISDN_devinfo devinfo;
Harald Welte52b1f982008-12-23 20:25:15 +0000389
390 sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
Harald Welte8470bf22008-12-25 23:28:35 +0000391 if (sk < 0) {
Harald Welte52b1f982008-12-23 20:25:15 +0000392 fprintf(stderr, "could not open socket %s\n", strerror(errno));
393 return sk;
394 }
395
396 ret = ioctl(sk, IMGETCOUNT, &cnt);
397 if (ret) {
398 fprintf(stderr, "error getting interf count: %s\n",
399 strerror(errno));
400 close(sk);
401 return -ENODEV;
402 }
Harald Weltead384642008-12-26 10:20:07 +0000403 //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
404 printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
405#if 1
406 devinfo.id = e1h->cardnr;
Harald Welte52b1f982008-12-23 20:25:15 +0000407 ret = ioctl(sk, IMGETDEVINFO, &devinfo);
408 if (ret < 0) {
409 fprintf(stdout, "error getting info for device %d: %s\n",
Harald Weltead384642008-12-26 10:20:07 +0000410 e1h->cardnr, strerror(errno));
Harald Welte52b1f982008-12-23 20:25:15 +0000411 return -ENODEV;
412 }
413 fprintf(stdout, " id: %d\n", devinfo.id);
414 fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
415 fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
416 fprintf(stdout, " protocol: %d\n", devinfo.protocol);
417 fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
418 fprintf(stdout, " name: %s\n", devinfo.name);
419#endif
420
421 /* TS0 is CRC4, don't need any fd for it */
422 for (ts = 1; ts < NUM_E1_TS; ts++) {
Harald Welte8470bf22008-12-25 23:28:35 +0000423 unsigned int idx = ts-1;
Harald Welte52b1f982008-12-23 20:25:15 +0000424 struct bsc_fd *bfd = &e1h->fd[idx];
425 struct sockaddr_mISDN addr;
426
Harald Welte8470bf22008-12-25 23:28:35 +0000427 bfd->data = e1h;
428 bfd->priv_nr = ts;
429 bfd->cb = misdn_fd_cb;
430
431 if (ts == 1) {
Harald Weltead384642008-12-26 10:20:07 +0000432 bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
Harald Welte8470bf22008-12-25 23:28:35 +0000433 bfd->when = BSC_FD_READ;
Harald Welte6cc38d72008-12-30 14:58:19 +0000434 } else
Harald Welte52b1f982008-12-23 20:25:15 +0000435 bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
436
Harald Welte8470bf22008-12-25 23:28:35 +0000437 if (bfd->fd < 0) {
Harald Welte52b1f982008-12-23 20:25:15 +0000438 fprintf(stderr, "could not open socket %s\n",
439 strerror(errno));
440 return bfd->fd;
441 }
442
443 memset(&addr, 0, sizeof(addr));
444 addr.family = AF_ISDN;
445 addr.dev = e1h->cardnr;
446 if (ts == 1) {
447 addr.channel = 0;
448 addr.sapi = 0;/* SAPI not supported yet in kernel */
449 addr.tei = TEI_L2ML;
450 } else
Harald Welte6cc38d72008-12-30 14:58:19 +0000451 addr.channel = ts;
Harald Welte52b1f982008-12-23 20:25:15 +0000452
453 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
454 if (ret < 0) {
Harald Welte8470bf22008-12-25 23:28:35 +0000455 fprintf(stderr, "could not bind l2 socket %s\n",
Harald Welte52b1f982008-12-23 20:25:15 +0000456 strerror(errno));
457 return -EIO;
458 }
Harald Welte8470bf22008-12-25 23:28:35 +0000459
Harald Weltee9a82612008-12-27 16:45:29 +0000460 if (ts == 2) {
461 bfd->when = BSC_FD_READ;
462 activate_bchan(e1h, ts);
463 }
464
Harald Welte8470bf22008-12-25 23:28:35 +0000465 ret = bsc_register_fd(bfd);
466 if (ret < 0) {
467 fprintf(stderr, "could not register FD: %s\n",
468 strerror(ret));
469 return ret;
470 }
Harald Welte52b1f982008-12-23 20:25:15 +0000471 }
Harald Welte8470bf22008-12-25 23:28:35 +0000472
473 return 0;
Harald Welte52b1f982008-12-23 20:25:15 +0000474}
475
Harald Weltead384642008-12-26 10:20:07 +0000476int mi_setup(struct gsm_bts *bts, int cardnr,
477 void (cb)(int event, struct gsm_bts *bts))
Harald Welte8470bf22008-12-25 23:28:35 +0000478{
479 struct mi_e1_handle *e1h;
480
481 e1h = malloc(sizeof(*e1h));
482 memset(e1h, 0, sizeof(*e1h));
483
484 e1h->cardnr = cardnr;
485 e1h->bts = bts;
Harald Weltead384642008-12-26 10:20:07 +0000486 e1h->cb = cb;
Harald Welte8470bf22008-12-25 23:28:35 +0000487 INIT_LLIST_HEAD(&e1h->oml_tx_list);
488 INIT_LLIST_HEAD(&e1h->rsl_tx_list);
489
490 global_e1h = e1h;
491
492 return mi_e1_setup(e1h);
493}