first hack that does something useful

This commit is contained in:
Sylvain Munaut 2014-06-09 20:02:23 +02:00
parent 9ec3f72277
commit 31d3354b01
15 changed files with 951 additions and 36 deletions

View File

@ -29,6 +29,11 @@ if test "$osmo_ac_build_transceiver" = "yes" ; then
fi
AM_CONDITIONAL(BUILD_TRANSCEIVER, test "x$osmo_ac_build_transceiver" = "xyes")
dnl Optional transceiver_ms
AC_ARG_ENABLE([transceiver-ms], [AS_HELP_STRING([--enable-transceiver-ms], [Build the MS transceiver application])],
[osmo_ac_build_transceiver_ms="$enableval"])
AM_CONDITIONAL(BUILD_TRANSCEIVER_MS, test "x$osmo_ac_build_transceiver_ms" = "xyes")
dnl checks for header files
AC_HEADER_STDC
@ -40,6 +45,7 @@ AC_OUTPUT(
src/misc/Makefile
src/mobile/Makefile
src/transceiver/Makefile
src/transceiver_ms/Makefile
include/Makefile
include/osmocom/Makefile
include/osmocom/bb/Makefile

View File

@ -3,3 +3,7 @@ SUBDIRS = common misc mobile
if BUILD_TRANSCEIVER
SUBDIRS += transceiver
endif
if BUILD_TRANSCEIVER_MS
SUBDIRS += transceiver_ms
endif

View File

@ -175,7 +175,7 @@ _l1ctl_rx_bts_burst_nb_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*bi)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Burst NB Ind: %u\n",
msgb_l2len(msg));
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
@ -214,7 +214,7 @@ _l1ctl_rx_bts_burst_ab_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*bi)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Burst AB Ind: %u\n",
msgb_l2len(msg));
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
@ -245,14 +245,14 @@ _l1ctl_rx_data_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*dl)) {
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
msgb_l2len(msg));
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
if (msgb_l2len(msg) < sizeof(*di)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
msgb_l3len(msg));
msgb_l2len(msg));
rc = -EINVAL;
goto exit;
}
@ -289,14 +289,14 @@ _l1ctl_rx_fbsb_conf(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*dl)) {
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
msgb_l2len(msg));
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
if (msgb_l2len(msg) < sizeof(*sc)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Conf: %u\n",
msgb_l3len(msg));
msgb_l2len(msg));
rc = -EINVAL;
goto exit;
}

View File

@ -29,6 +29,8 @@
#include <sys/un.h>
#include <arpa/inet.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/bb/common/logging.h>
@ -103,44 +105,29 @@ _l1l_write(struct osmo_fd *fd, struct msgb *msg)
int
l1l_open(struct l1ctl_link *l1l,
const char *path, l1ctl_cb_t cb, void *cb_data)
const char *path, l1ctl_cb_t cb, void *cb_data)
{
int rc, fd;
struct sockaddr_un local;
int rc;
memset(l1l, 0x00, sizeof(struct l1ctl_link));
l1l->cb = cb;
l1l->cb_data = cb_data;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to create unix domain socket.\n");
return fd;
}
l1l->wq.bfd.fd = fd;
local.sun_family = AF_UNIX;
strncpy(local.sun_path, path, sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
rc = connect(fd, (struct sockaddr *) &local, sizeof(local));
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to connect to '%s': %s\n",
local.sun_path, strerror(errno));
close(fd);
return rc;
}
osmo_wqueue_init(&l1l->wq, 100);
l1l->wq.bfd.data = l1l;
l1l->wq.bfd.when = BSC_FD_READ;
l1l->wq.read_cb = _l1l_read;
l1l->wq.write_cb = _l1l_write;
rc = osmo_fd_register(&l1l->wq.bfd);
if (rc != 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to register fd.\n");
close(fd);
rc = osmo_sock_unix_init_ofd(
&l1l->wq.bfd,
SOCK_STREAM, 0,
path,
OSMO_SOCK_F_CONNECT
);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to create and init unix domain socket.\n");
return rc;
}
@ -160,6 +147,9 @@ l1l_close(struct l1ctl_link *l1l)
osmo_wqueue_clear(&l1l->wq);
if (l1l->to_free)
talloc_free(l1l);
return 0;
}
@ -185,3 +175,93 @@ l1l_send(struct l1ctl_link *l1l, struct msgb *msg)
return 0;
}
static int
_l1l_accept(struct osmo_fd *fd, unsigned int flags)
{
struct l1ctl_server *l1s = fd->data;
struct l1ctl_link *l1l;
struct sockaddr_un un_addr;
socklen_t len;
int cfd, rc;
len = sizeof(un_addr);
cfd = accept(fd->fd, (struct sockaddr *) &un_addr, &len);
if (cfd < 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to accept a new L1CTL connection.\n");
return cfd;
}
l1l = talloc_zero(NULL, struct l1ctl_link);
if (!l1l) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate a new L1CTL connection.\n");
return -ENOMEM;
}
l1l->to_free = 1;
osmo_wqueue_init(&l1l->wq, 100);
l1l->wq.bfd.fd = cfd;
l1l->wq.bfd.data = l1l;
l1l->wq.bfd.when = BSC_FD_READ;
l1l->wq.read_cb = _l1l_read;
l1l->wq.write_cb = _l1l_write;
INIT_LLIST_HEAD(&l1l->wq.bfd.list);
rc = l1s->cb(l1s->cb_data, l1l);
if (rc)
goto error;
if (!l1l->cb || !l1l->cb_data) {
LOGP(DL1C, LOGL_NOTICE, "New L1CTL callback didn't set message callback !\n");
}
rc = osmo_fd_register(&l1l->wq.bfd);
if (rc) {
LOGP(DL1C, LOGL_ERROR, "Failed to register fd of new L1CTL connection.\n");
goto error;
}
return 0;
error:
if (l1l)
l1l_close(l1l);
return rc;
}
int
l1l_start_server(struct l1ctl_server *l1s, const char *path,
l1ctl_server_cb_t cb, void *cb_data)
{
int rc;
memset(l1s, 0x00, sizeof(struct l1ctl_server));
l1s->cb = cb;
l1s->cb_data = cb_data;
l1s->bfd.cb = _l1l_accept;
l1s->bfd.data = l1s;
rc = osmo_sock_unix_init_ofd(
&l1s->bfd,
SOCK_STREAM, 0,
path,
OSMO_SOCK_F_BIND
);
if (rc < 0)
return rc;
return 0;
}
void
l1l_stop_server(struct l1ctl_server *l1s)
{
/* FIXME */
}

View File

@ -25,9 +25,12 @@
#define __TRX_L1CTL_LINK_H__
#include <osmocom/core/socket.h>
#include <osmocom/core/write_queue.h>
/* Link */
typedef int (*l1ctl_cb_t)(void *data, struct msgb *msgb);
struct l1ctl_link
@ -36,6 +39,8 @@ struct l1ctl_link
l1ctl_cb_t cb;
void *cb_data;
int to_free;
};
@ -47,4 +52,21 @@ int l1l_close(struct l1ctl_link *l1l);
int l1l_send(struct l1ctl_link *l1l, struct msgb *msg);
/* Server */
typedef int (*l1ctl_server_cb_t)(void *data, struct l1ctl_link *l1l);
struct l1ctl_server
{
struct osmo_fd bfd;
l1ctl_server_cb_t cb;
void *cb_data;
};
int l1l_start_server(struct l1ctl_server *l1s, const char *path,
l1ctl_server_cb_t cb, void *cb_data);
void l1l_stop_server (struct l1ctl_server *l1s);
#endif /* __TRX_L1CTL_LINK_H__ */

View File

@ -56,10 +56,10 @@ static int _trx_data_read_cb(struct osmo_fd *ofd, unsigned int what);
/* ------------------------------------------------------------------------ */
/* Init */
/* Init / Cleanup */
/* ------------------------------------------------------------------------ */
int
static int
_trx_udp_init(struct trx *trx,
struct osmo_fd *ofd, const char *addr, uint16_t port,
int (*cb)(struct osmo_fd *fd, unsigned int what))
@ -150,6 +150,7 @@ trx_alloc(const char *addr, uint16_t base_port, struct l1ctl_link *l1l)
return trx;
err:
/* FIXME */
return NULL;
}

View File

@ -0,0 +1,6 @@
AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBSOSMODSP_CFLAGS)
LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMODSP_LIBS)
bin_PROGRAMS = transceiver-ms
transceiver_ms_SOURCES = main.c l1ctl.c l1ctl_link.c trx.c ../common/logging.c

View File

@ -0,0 +1,50 @@
/*
* app.h
*
* Application state / defines
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TRXMS_APP_H__
#define __TRXMS_APP_H__
#include <stdint.h>
#include "l1ctl_link.h"
struct log_target;
struct trx;
struct app_state
{
/* Logging */
struct log_target *stderr_target;
/* L1CTL server & active link */
struct l1ctl_server l1s;
struct l1ctl_link *l1l;
/* TRX link to Transceiver */
struct trx *trx;
};
#endif /* __TRXMS_APP_H__ */

View File

@ -0,0 +1,294 @@
/*
* l1ctl.c
*
* L1CTL interface
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <arpa/inet.h>
#include <osmocom/core/select.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bb/common/logging.h>
#include <l1ctl_proto.h>
#include "app.h"
#include "l1ctl.h"
#include "l1ctl_link.h"
#include "trx.h"
/* ------------------------------------------------------------------------ */
/* L1CTL exported API */
/* ------------------------------------------------------------------------ */
static struct msgb *
_l1ctl_alloc(uint8_t msg_type)
{
struct l1ctl_hdr *l1h;
struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
if (!msg) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
return NULL;
}
msg->l1h = msgb_put(msg, sizeof(*l1h));
l1h = (struct l1ctl_hdr *) msg->l1h;
l1h->msg_type = msg_type;
return msg;
}
int
l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last)
{
struct msgb *msg;
struct l1ctl_pm_conf *pmc;
msg = _l1ctl_alloc(L1CTL_PM_CONF);
if (!msg)
return -ENOMEM;
LOGP(DL1C, LOGL_DEBUG, "Tx PM Conf (%s %d = %d dBm)\n",
gsm_band_name(gsm_arfcn2band(band_arfcn)),
band_arfcn &~ ARFCN_FLAG_MASK,
dbm
);
pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
pmc->band_arfcn = htons(band_arfcn);
pmc->pm[0] = dbm2rxlev(dbm);
pmc->pm[1] = 0;
if (last) {
struct l1ctl_hdr *l1h = ( struct l1ctl_hdr *)msg->l1h;
l1h->flags |= L1CTL_F_DONE;
}
return l1l_send(l1l, msg);
}
int
l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type)
{
struct msgb *msg;
struct l1ctl_reset *res;
msg = _l1ctl_alloc(L1CTL_RESET_IND);
if (!msg)
return -ENOMEM;
LOGP(DL1C, LOGL_DEBUG, "Tx Reset Ind (%u)\n", type);
res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
res->type = type;
return l1l_send(l1l, msg);
}
int
l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type)
{
struct msgb *msg;
struct l1ctl_reset *res;
msg = _l1ctl_alloc(L1CTL_RESET_CONF);
if (!msg)
return -ENOMEM;
LOGP(DL1C, LOGL_DEBUG, "Tx Reset Conf (%u)\n", type);
res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
res->type = type;
return l1l_send(l1l, msg);
}
/* ------------------------------------------------------------------------ */
/* L1CTL Receive handling */
/* ------------------------------------------------------------------------ */
static int
_l1ctl_rx_fbsb_req(struct app_state *as, struct msgb *msg)
{
struct l1ctl_fbsb_req *fbsb;
uint16_t band_arfcn;
int rc = 0;
/* Grab message */
fbsb = (struct l1ctl_fbsb_req *) msg->l1h;
if (msgb_l1len(msg) < sizeof(*fbsb)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Req: %u\n",
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
band_arfcn = ntohs(fbsb->band_arfcn);
LOGP(DL1C, LOGL_DEBUG, "Rx FBSB Req (%s %d)\n",
gsm_band_name(gsm_arfcn2band(band_arfcn)),
band_arfcn &~ ARFCN_FLAG_MASK
);
/* Send request to TRX */
trx_ctrl_send_cmd(as->trx, "TXTUNE", "%d",
(int)gsm_arfcn2freq10(band_arfcn, 1) * 100);
trx_ctrl_send_cmd(as->trx, "RXTUNE", "%d",
(int)gsm_arfcn2freq10(band_arfcn, 0) * 100);
trx_ctrl_send_cmd(as->trx, "POWERON", NULL);
trx_ctrl_send_cmd(as->trx, "SYNC", NULL);
exit:
msgb_free(msg);
return rc;
}
static int
_l1ctl_rx_pm_req(struct app_state *as, struct msgb *msg)
{
struct l1ctl_pm_req *pmr;
uint16_t arfcn_start, arfcn_stop, arfcn;
int rc = 0;
/* Grab message */
pmr = (struct l1ctl_pm_req *) msg->l1h;
if (msgb_l1len(msg) < sizeof(*pmr)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short PM Req: %u\n",
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
arfcn_start = ntohs(pmr->range.band_arfcn_from);
arfcn_stop = ntohs(pmr->range.band_arfcn_to);
LOGP(DL1C, LOGL_DEBUG, "Rx PM Req (%s: %d -> %d)\n",
gsm_band_name(gsm_arfcn2band(arfcn_start)),
arfcn_start &~ ARFCN_FLAG_MASK,
arfcn_stop &~ ARFCN_FLAG_MASK
);
/* Send fake responses */
for (arfcn=arfcn_start; arfcn<=arfcn_stop; arfcn++)
{
l1ctl_tx_pm_conf(as->l1l, arfcn, arfcn == 36 ? -60 : -120, arfcn == arfcn_stop);
}
exit:
msgb_free(msg);
return rc;
}
static int
_l1ctl_rx_reset_req(struct app_state *as, struct msgb *msg)
{
struct l1ctl_reset *res;
int rc = 0;
/* Grab message */
res = (struct l1ctl_reset *) msg->l1h;
if (msgb_l1len(msg) < sizeof(*res)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
LOGP(DL1C, LOGL_DEBUG, "Rx Reset Req (%u)\n", res->type);
/* Request power off */
trx_ctrl_send_cmd(as->trx, "POWEROFF", NULL);
/* Simply confirm */
rc = l1ctl_tx_reset_conf(as->l1l, res->type);
exit:
msgb_free(msg);
return rc;
}
static int
_l1ctl_recv(void *data, struct msgb *msg)
{
struct app_state *as = data;
struct l1ctl_hdr *l1h;
int rc = 0;
/* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
as the l1ctl header is of no interest to subsequent code */
l1h = (struct l1ctl_hdr *) msg->l1h;
msg->l1h = l1h->data;
/* Act */
switch (l1h->msg_type) {
case L1CTL_FBSB_REQ:
rc = _l1ctl_rx_fbsb_req(as, msg);
break;
case L1CTL_PM_REQ:
rc = _l1ctl_rx_pm_req(as, msg);
break;
case L1CTL_RESET_REQ:
rc = _l1ctl_rx_reset_req(as, msg);
break;
default:
LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type);
msgb_free(msg);
}
return rc;
}
int
l1ctl_new_cb(void *data, struct l1ctl_link *l1l)
{
struct app_state *as = data;
LOGP(DL1C, LOGL_INFO, "New L1CTL connection\n");
/* Close previous link */
if (as->l1l) {
LOGP(DL1C, LOGL_INFO, "Closing old L1CTL connection\n");
l1l_close(as->l1l);
}
/* Setup new one */
as->l1l = l1l;
l1l->cb = _l1ctl_recv;
l1l->cb_data = data;
/* Send reset ind */
l1ctl_tx_reset_ind(l1l, L1CTL_RES_T_BOOT);
/* Done */
return 0;
}

View File

@ -0,0 +1,31 @@
/*
* l1ctl.h
*
* L1CTL interface
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TRXMS_L1CTL_H__
#define __TRXMS_L1CTL_H__
struct l1ctl_link;
int l1ctl_new_cb(void *data, struct l1ctl_link *l1l);
#endif /* __TRXMS_L1CTL_H__ */

View File

@ -0,0 +1 @@
../transceiver/l1ctl_link.c

View File

@ -0,0 +1 @@
../transceiver/l1ctl_link.h

View File

@ -0,0 +1,76 @@
/*
* main.c
*
* MS-side Transceiver main program
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bb/common/logging.h>
#include "app.h"
#include "l1ctl.h"
#include "l1ctl_link.h"
#include "trx.h"
void *l23_ctx = NULL;
int main(int argc, char *argv[])
{
struct app_state _as, *as = &_as;
int rv;
/* Options */
/* App state init */
memset(as, 0x00, sizeof(struct app_state));
/* Init talloc */
l23_ctx = talloc_named_const(NULL, 1, "l23 app context");
/* Init logging */
log_init(&log_info, l23_ctx);
as->stderr_target = log_target_create_stderr();
log_add_target(as->stderr_target);
log_set_all_filter(as->stderr_target, 1);
log_set_log_level(as->stderr_target, LOGL_DEBUG);
/* Init TRX interface */
as->trx = trx_alloc("127.0.0.1", 5700);
if (!as->trx)
exit(-1);
/* Start L1CTL server */
l1l_start_server(&as->l1s, "/tmp/osmocom_l2", l1ctl_new_cb, as);
/* Main loop */
while (1) {
osmo_select_main(0);
}
return 0;
}

View File

@ -0,0 +1,298 @@
/*
* trx.c
*
* OpenBTS TRX interface handling
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bb/common/logging.h>
#include "trx.h"
static int _trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what);
static int _trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what);
static int _trx_data_read_cb(struct osmo_fd *ofd, unsigned int what);
/* ------------------------------------------------------------------------ */
/* Init / Cleanup */
/* ------------------------------------------------------------------------ */
static int
_trx_udp_init(struct trx *trx,
struct osmo_fd *ofd, const char *addr, uint16_t port,
int (*cb)(struct osmo_fd *fd, unsigned int what))
{
struct sockaddr_storage _sas;
struct sockaddr *sa = (struct sockaddr *)&_sas;
socklen_t sa_len;
int rv;
/* Init */
ofd->fd = -1;
ofd->cb = cb;
ofd->data = trx;
/* Listen / Binds */
rv = osmo_sock_init_ofd(
ofd,
AF_UNSPEC, SOCK_DGRAM, 0, addr, port + 100,
OSMO_SOCK_F_BIND);
if (rv < 0)
goto err;
/* Connect */
sa_len = sizeof(struct sockaddr_storage);
rv = getsockname(ofd->fd, sa, &sa_len);
if (rv)
goto err;
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = htons(ntohs(sin->sin_port)-100);
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_port = htons(ntohs(sin6->sin6_port)-100);
} else {
rv = -EINVAL;
goto err;
}
rv = connect(ofd->fd, sa, sa_len);
if (rv)
goto err;
return 0;
err:
if (ofd->fd >= 0) {
osmo_fd_unregister(ofd);
close(ofd->fd);
}
return rv;
}
struct trx *
trx_alloc(const char *addr, uint16_t base_port)
{
struct trx *trx;
int rv;
/* Alloc */
trx = talloc_zero(NULL, struct trx);
if (!trx)
return NULL;
/* Clock */
rv = _trx_udp_init(trx, &trx->ofd_clk, addr, base_port, _trx_clk_read_cb);
if (rv)
goto err;
/* Control */
rv = _trx_udp_init(trx, &trx->ofd_ctrl, addr, base_port+1, _trx_ctrl_read_cb);
if (rv)
goto err;
/* Data */
rv = _trx_udp_init(trx, &trx->ofd_data, addr, base_port+2, _trx_data_read_cb);
if (rv)
goto err;
/* Done */
return trx;
/* Error path */
err:
trx_free(trx);
return NULL;
}
void
trx_free(struct trx *trx)
{
if (trx->ofd_data.fd >= 0) {
osmo_fd_unregister(&trx->ofd_data);
close(trx->ofd_data.fd);
}
if (trx->ofd_ctrl.fd >= 0) {
osmo_fd_unregister(&trx->ofd_ctrl);
close(trx->ofd_ctrl.fd);
}
if (trx->ofd_clk.fd >= 0) {
osmo_fd_unregister(&trx->ofd_clk);
close(trx->ofd_clk.fd);
}
talloc_free(trx);
}
/* ------------------------------------------------------------------------ */
/* Clock interface */
/* ------------------------------------------------------------------------ */
#define CLK_BUF_LEN 128
static int
_trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
{
char buf[CLK_BUF_LEN];
uint32_t fn;
int l;
l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
if (l <= 0)
return -EIO;
if (l >= sizeof(buf)) {
LOGP(DTRX, LOGL_ERROR,
"Received large message on CLK interface (%d)\n", l);
return -EINVAL;
}
if (memcmp("IND CLOCK ", buf, 10) || buf[l-1]) {
LOGP(DTRX, LOGL_ERROR,
"Received invalid message on CLK interface\n");
return -EINVAL;
}
fn = atoi(&buf[11]);
LOGP(DTRX, LOGL_DEBUG, "Clock IND: fn=%d\n", (int)fn);
/* FIXME call the clk ind callback */
return 0;
}
/* ------------------------------------------------------------------------ */
/* Control interface */
/* ------------------------------------------------------------------------ */
#define CMD_BUF_LEN 128
static int
_trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
{
char buf[CMD_BUF_LEN];
int l;
l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
if (l <= 0)
return -EIO;
/* FIXME should not happen ... */
printf("Here %s\n", buf);
return 0;
}
#include <fcntl.h>
int
trx_ctrl_send_cmd(struct trx *trx, const char *cmd, const char *fmt, ...)
{
va_list ap;
char buf[CMD_BUF_LEN], cmd_match[32];
int l;
/* Send the commands */
l = snprintf(buf, sizeof(buf)-1, "CMD %s%s", cmd, fmt ? " " : "");
if (fmt) {
va_start(ap, fmt);
l += vsnprintf(buf+l, sizeof(buf)-l-1, fmt, ap);
va_end(ap);
}
buf[l] = '\0';
LOGP(DTRX, LOGL_DEBUG, "TRX Control send: |%s|\n", buf);
send(trx->ofd_ctrl.fd, buf, strlen(buf)+1, 0);
/* Wait for response */
{
int fd = trx->ofd_ctrl.fd;
int flags;
/* make FD nonblocking */
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return flags;
flags &= ~O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
if (flags < 0)
return flags;
}
/* Get a response */
l = recv(trx->ofd_ctrl.fd, buf, sizeof(buf), MSG_TRUNC);
if (l <= 0)
return -EIO;
if (memcmp(buf, "RSP ", 4) || buf[l-1] != '\0') {
LOGP(DTRX, LOGL_ERROR, "Invalid response on TRX Control socket\n");
return -EIO;
}
LOGP(DTRX, LOGL_DEBUG, "TRX Control read: |%s|\n", buf);
return 0;
}
/* ------------------------------------------------------------------------ */
/* Data interface */
/* ------------------------------------------------------------------------ */
static int
_trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
{
char buf[128];
int l;
l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
if (l <= 0)
return -EIO;
return 0;
}

View File

@ -0,0 +1,45 @@
/*
* trx.h
*
* OpenBTS TRX interface handling
*
* Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TRXMS_TRX_H__
#define __TRXMS_TRX_H__
#include <stdint.h>
struct trx {
/* UDP sockets */
struct osmo_fd ofd_clk;
struct osmo_fd ofd_ctrl;
struct osmo_fd ofd_data;
/* */
};
struct trx *trx_alloc(const char *addr, uint16_t base_port);
void trx_free(struct trx *trx);
int trx_ctrl_send_cmd(struct trx *trx, const char *cmd, const char *fmt, ...);
#endif /* __TRXMS_TRX_H__ */