/* OpenBSC Abis interface to mISDNuser * * (C) 2008 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #define AF_COMPATIBILITY_FUNC #include #define NUM_E1_TS 32 /* data structure for one E1 interface with A-bis */ struct mi_e1_handle { struct gsm_bts *bts; /* The mISDN card number of the card we use */ int cardnr; /* The RSL adress */ struct sockaddr_mISDN l2addr; /* The OML adress */ struct sockaddr_mISDN omladdr; struct gsm_fd fd[NUM_E1_TS]; }; #define SAPI_L2ML 0 #define SAPI_OML 62 #define SAPI_RSL 63 #define TEI_L2ML 127 #define TEI_OML 25 #define TEI_RSL 1 static void hexdump(unsigned char *buf, int len) { int i; for (i = 0; i < len; i++) { fprintf(stdout, "%02x ", buf[i]); } fprintf(stdout, "\n"); } #define TS1_ALLOC_SIZE 300 static int handle_ts1_read(struct bsc_fd *bfd) { struct mi_e1_handle *e1h = bfd->data; struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE); struct sockaddr_mISDN l2dadr; socklen_t alen; if (!msg) return -ENOMEM; msg->bts = e1h->bts; alen = sizeof(l2addr); ret = recvfrom(bfd->fd, msg->data, 300, 0, (struct sockaddr *) &l2addr, &alen); if (ret < 0) { fprintf(stderr, "recvfrom error %s\n", strerror(errno)); return ret; } if (alen != sizeof(l2addr)) return -EINVAL; msgb_put(msg, ret); DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x)\n", ret, hh->prim, hh->id); switch (hh->prim) { case DL_INFORMATION_IND: DEBUGP(DMI, "got DL_INFORMATION_IND\n"); struct sockaddr_mISDN *sa; char *lstr = "UNKN"; switch (l2addr.tei) { case TEI_OML: sa = &e1h->omladdr; lstr = "OML"; break; case TEI_RSL: sa = &e1h->l2addr; lstr = "RSL"; break; default: continue; } DEBUGP(DMI, "%s use channel(%d) sapi(%d) tei(%d) for now\n", lstr, l2addr.channel, l2addr.sapi, l2addr.tei); memcpy(sa, &l2addr, sizeof(l2addr)); break; case DL_ESTABLISH_IND: DEBUGP(DMI, "got DL_ESTABLISH_IND\n"); break; case DL_ESTABLISH_CNF: DEBUGP(DMI, "got DL_ESTABLISH_CNF\n"); break; case DL_RELEASE_IND: DEBUGP(DMI, "got DL_RELEASE_IND\n"); break; case MPH_ACTIVATE_IND: DEBUGP(DMI, "got MPH_ACTIVATE_IND\n"); break; case MPH_DEACTIVATE_IND: DEBUGP(DMI, "got MPH_DEACTIVATE_IND\n"); break; case DL_DATA_IND: DEBUGP(DMI, "got DL_DATA_IND\n"); msg->l2_off = MISDN_HEADER_LEN; hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN); switch (l2addr.tei) { case TEI_OML: ret = abis_nm_rcvmsg(msg); break; case TEI_RSL: ret = abis_rsl_rcvmsg(msg); break; default: fprintf(stderr, "DATA_IND for unknown TEI\n"); break; } break; default: DEBUGP(DMI, "got unexpected 0x%x prim\n", hh->prim); break; } return ret; } static int handle_ts1_write(struct bsc_fd *bfd) { struct mi_e1_handle *e1h = bfd->data; /* FIXME: dequeue a pending msgb for RSL / OML */ /* prepend the mISDNhead */ hh = (struct mISDNhed *) msg_ hh->prim = DL_DATA_REQ; /* FIXME: send it off */ } static int handle_tsX_read(struct bsc_fd *bfd) { /* FIXME: read from a B channel TS */ } static int handle_TsX_write(struct bsc_fd *bfd) { /* FIXME: write to a B channel TS */ } /* callback from select.c in case one of the fd's can be read/written */ static int misdn_fd_cb(struct gsm_fd *bfd, unsigned int what) { unsigned int e1_ts = bfd->priv_nr; int rc = 0; switch (e1_ts) { case 1: if (what & BSC_FD_READ) rc = handle_ts1_read(bfd); if (what & BSC_FD_WRITE) rc = handle_ts1_write(bfd); break; default: if (what & BSC_FD_READ) rc = handle_tsX_read(bfd); if (what & BSC_FD_WRITE) rc = handle_tsX_write(bfd); break; } return rc; } static int mi_setup(devinfo_t *di) { int ts, sk, ret; struct mISDN_devinfo devinfo; sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); if (sk < 0) fprintf(stderr, "could not open socket %s\n", strerror(errno)); return sk; } ret = ioctl(sk, IMGETCOUNT, &cnt); if (ret) { fprintf(stderr, "error getting interf count: %s\n", strerror(errno)); close(sk); return -ENODEV; } DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); #if 0 devinfo.id = di->cardnr; ret = ioctl(sk, IMGETDEVINFO, &devinfo); if (ret < 0) { fprintf(stdout, "error getting info for device %d: %s\n", di->cardnr, strerror(errno)); return -ENODEV; } fprintf(stdout, " id: %d\n", devinfo.id); fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); fprintf(stdout, " protocol: %d\n", devinfo.protocol); fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); fprintf(stdout, " name: %s\n", devinfo.name); #endif /* TS0 is CRC4, don't need any fd for it */ for (ts = 1; ts < NUM_E1_TS; ts++) { unsigned int idx = i-1; struct bsc_fd *bfd = &e1h->fd[idx]; struct sockaddr_mISDN addr; if (ts == 1) bfd->fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_LAPD_NT); else bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); if (bfd->fd < 0) fprintf(stderr, "could not open socket %s\n", strerror(errno)); return bfd->fd; } memset(&addr, 0, sizeof(addr)); addr.family = AF_ISDN; addr.dev = e1h->cardnr; if (ts == 1) { addr.channel = 0; addr.sapi = 0;/* SAPI not supported yet in kernel */ addr.tei = TEI_L2ML; } else addr.channel = ts; ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { fprintf(stdout, "could not bind l2 socket %s\n", strerror(errno)); return -EIO; } } }