/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */ /* * (C) 2010 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 "../config.h" #ifdef HAVE_SYS_SELECT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct bsc_fd gsmtap_bfd = { .fd = -1 }; static LLIST_HEAD(gsmtap_txqueue); uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) { uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; switch (rsl_chantype) { case RSL_CHAN_Bm_ACCHs: ret = GSMTAP_CHANNEL_TCH_F; break; case RSL_CHAN_Lm_ACCHs: ret = GSMTAP_CHANNEL_TCH_H; break; case RSL_CHAN_SDCCH4_ACCH: ret = GSMTAP_CHANNEL_SDCCH4; break; case RSL_CHAN_SDCCH8_ACCH: ret = GSMTAP_CHANNEL_SDCCH8; break; case RSL_CHAN_BCCH: ret = GSMTAP_CHANNEL_BCCH; break; case RSL_CHAN_RACH: ret = GSMTAP_CHANNEL_RACH; break; case RSL_CHAN_PCH_AGCH: /* it could also be AGCH... */ ret = GSMTAP_CHANNEL_PCH; break; } if (link_id & 0x40) ret |= GSMTAP_CHANNEL_ACCH; return ret; } /* receive a message from L1/L2 and put it in GSMTAP */ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, uint8_t snr, const uint8_t *data, unsigned int len) { struct msgb *msg; struct gsmtap_hdr *gh; uint8_t *dst; msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx"); if (!msg) return NULL; gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); gh->version = GSMTAP_VERSION; gh->hdr_len = sizeof(*gh)/4; gh->type = GSMTAP_TYPE_UM; gh->timeslot = ts; gh->sub_slot = ss; gh->arfcn = htons(arfcn); gh->snr_db = snr; gh->signal_dbm = signal_dbm; gh->frame_number = htonl(fn); gh->sub_type = chan_type; gh->antenna_nr = 0; dst = msgb_put(msg, len); memcpy(dst, data, len); return msg; } /* receive a message from L1/L2 and put it in GSMTAP */ int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, uint8_t snr, const uint8_t *data, unsigned int len) { struct msgb *msg; /* gsmtap was never initialized, so don't try to send anything */ if (gsmtap_bfd.fd == -1) return 0; msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm, snr, data, len); if (!msg) return -ENOMEM; msgb_enqueue(&gsmtap_txqueue, msg); gsmtap_bfd.when |= BSC_FD_WRITE; return 0; } /* Callback from select layer if we can write to the socket */ static int gsmtap_fd_cb(struct bsc_fd *fd, unsigned int flags) { struct msgb *msg; int rc; if (!(flags & BSC_FD_WRITE)) return 0; msg = msgb_dequeue(&gsmtap_txqueue); if (!msg) { /* no more messages in the queue, disable READ cb */ gsmtap_bfd.when = 0; return 0; } rc = write(gsmtap_bfd.fd, msg->data, msg->len); if (rc < 0) { perror("writing msgb to gsmtap fd"); msgb_free(msg); return rc; } if (rc != msg->len) { perror("short write to gsmtap fd"); msgb_free(msg); return -EIO; } msgb_free(msg); return 0; } int gsmtap_init(uint32_t dst_ip) { int rc; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(GSMTAP_UDP_PORT); sin.sin_addr.s_addr = htonl(dst_ip); /* FIXME: create socket */ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (rc < 0) { perror("creating UDP socket"); return rc; } gsmtap_bfd.fd = rc; rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin)); if (rc < 0) { perror("connecting UDP socket"); close(gsmtap_bfd.fd); gsmtap_bfd.fd = 0; return rc; } gsmtap_bfd.when = BSC_FD_WRITE; gsmtap_bfd.cb = gsmtap_fd_cb; gsmtap_bfd.data = NULL; return bsc_register_fd(&gsmtap_bfd); } #endif /* HAVE_SYS_SELECT_H */