Add support for Octasic OCTSDR-2G GSM PHY

This adds support for a new PHY to OsmoBTS, the Octasic OCTSDR-2G
PHY. This is a proprietary GSM PHY running on a familty of Octasic DSPs.
This commit is contained in:
Harald Welte 2015-09-06 16:04:32 +02:00
parent e9f12acbeb
commit b92100ad36
18 changed files with 4568 additions and 0 deletions

2
.gitignore vendored
View File

@ -31,6 +31,8 @@ src/osmo-bts-sysmo/sysmobts-mgr
src/osmo-bts-trx/osmobts-trx
src/osmo-bts-octphy/osmo-bts-octphy
tests/atconfig
tests/package.m4
tests/agch/agch_test

View File

@ -49,6 +49,25 @@ AC_ARG_ENABLE(trx,
AC_MSG_RESULT([$enable_trx])
AM_CONDITIONAL(ENABLE_TRX, test "x$enable_trx" = "xyes")
AC_MSG_CHECKING([whether to enable support for Octasic OCTPHY-2G])
AC_ARG_ENABLE(octphy,
AC_HELP_STRING([--enable-octphy],
[enable code for Octasic OCTPHY-2G [default=no]]),
[enable_octphy="yes"],[enable_octphy="no"])
AC_ARG_WITH([octsdr-2g], [AS_HELP_STRING([--with-octsdr-2g], [Location of the OCTSDR-2G API header files])],
[octsdr2g_incdir="$withval"],[octsdr2g_incdir="`cd $srcdir; pwd`/src/osmo-bts-octphy/"])
AC_SUBST([OCTSDR2G_INCDIR], $octsdr2g_incdir)
AC_MSG_RESULT([$enable_octphy])
AM_CONDITIONAL(ENABLE_OCTPHY, test "x$enable_octphy" = "xyes")
if test "$enable_octphy" = "yes" ; then
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$OCTSDR2G_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([octphy/octvc1/gsm/octvc1_gsm_default.h],[],
[AC_MSG_ERROR([octphy/octvc1/gsm/octvc1_gsm_default.h can not be found in $octsdr2g_incdir])],
[#include <octphy/octvc1/gsm/octvc1_gsm_default.h>])
CPPFLAGS=$oldCPPFLAGS
fi
# We share gsm_data.h with OpenBSC and need to be pointed to the source
# directory of OpenBSC for now.
AC_ARG_WITH([openbsc],
@ -83,6 +102,7 @@ AC_OUTPUT(
src/common/Makefile
src/osmo-bts-sysmo/Makefile
src/osmo-bts-trx/Makefile
src/osmo-bts-octphy/Makefile
include/Makefile
include/osmo-bts/Makefile
tests/Makefile

View File

@ -0,0 +1,28 @@
!
! OsmoBTS () configuration saved from vty
!!
!
log stderr
logging color 1
logging timestamp 0
logging level all everything
logging level rsl info
logging level oml info
logging level rll notice
logging level rr notice
logging level meas notice
logging level pag info
logging level l1c info
logging level l1p info
logging level dsp info
logging level abis notice
!
line vty
no login
!
bts 0
band 1800
ipa unit-id 1234 0
oml remote-ip 127.0.0.1
phy-hw-addr 00:0C:90:2e:80:1e
phy-netdev eth0.2342

View File

@ -3,6 +3,11 @@ SUBDIRS = common
if ENABLE_SYSMOBTS
SUBDIRS += osmo-bts-sysmo
endif
if ENABLE_TRX
SUBDIRS += osmo-bts-trx
endif
if ENABLE_OCTPHY
SUBDIRS += osmo-bts-octphy
endif

View File

@ -0,0 +1,13 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) -I$(OCTSDR2G_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) -lortp
EXTRA_DIST = l1_if.h l1_oml.h l1_utils.h l1_octInit.h l1_octIoMsg.h
bin_PROGRAMS = osmo-bts-octphy
COMMON_SOURCES = main.c l1_if.c l1_oml.c l1_utils.c l1_tch.c octphy_hw_api.c octphy_vty.c octpkt.c
osmo_bts_octphy_SOURCES = $(COMMON_SOURCES)
osmo_bts_octphy_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)

1365
src/osmo-bts-octphy/l1_if.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/if_packet.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmo-bts/gsm_data.h>
#include <octphy/octvc1/gsm/octvc1_gsm_api.h>
struct octphy_hdl {
/* packet socket to talk with PHY */
struct osmo_wqueue phy_wq;
/* MAC address of th PHY */
struct sockaddr_ll phy_addr;
/* Network device name */
char *netdev_name;
/* address parameters of the PHY */
uint32_t session_id;
uint32_t next_trans_id;
uint32_t socket_id;
/* clock manager state */
uint32_t clkmgr_state;
struct {
uint32_t rf_port_index;
uint32_t rx_gain_db;
uint32_t tx_atten_db;
} config;
struct llist_head wlc_list;
/* private pointer, points back to TRX */
void *priv;
struct osmo_timer_list alive_timer;
uint32_t alive_prim_cnt;
};
static inline struct octphy_hdl *trx_octphy_hdl(struct gsm_bts_trx *trx)
{
return trx->role_bts.l1h;
}
void l1if_fill_msg_hdr(tOCTVC1_MSG_HEADER *mh, struct msgb *msg,
struct octphy_hdl *fl1h, uint32_t msg_type, uint32_t api_cmd);
typedef int l1if_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, void *data);
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct octphy_hdl *fl1h, struct msgb *msg,
l1if_compl_cb *cb, void *data);
#include <octphy/octvc1/gsm/octvc1_gsm_api.h>
struct gsm_lchan *get_lchan_by_lchid(struct gsm_bts_trx *trx,
tOCTVC1_GSM_LOGICAL_CHANNEL_ID *lch_id);
int l1if_open(struct octphy_hdl *fl1h);
int l1if_close(struct octphy_hdl *hdl);
int l1if_trx_open(struct gsm_bts_trx *trx);
int l1if_trx_close_all(struct gsm_bts *bts);
int l1if_enable_events(struct gsm_bts_trx *trx);
int l1if_activate_rf(struct octphy_hdl *fl1h, int on);
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr,
tOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_DATA_INDICATION_EVT *
data_ind);
struct msgb *l1p_msgb_alloc(void);
/* tch.c */
void l1if_tch_encode(struct gsm_lchan *lchan, uint32_t *payload_type,
uint8_t *data, uint32_t *len, const uint8_t *rtp_pl,
unsigned int rtp_pl_len);
tOCTVC1_RADIO_STANDARD_FREQ_BAND_GSM_ENUM
osmocom_to_octphy_band(enum gsm_band osmo_band, unsigned int arfcn);

1399
src/osmo-bts-octphy/l1_oml.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
#pragma once
#include "l1_if.h"
/* channel control */
int l1if_rsl_chan_act(struct gsm_lchan *lchan);
int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
int l1if_set_ciphering(struct gsm_lchan *lchan, int dir_downlink);
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
int gsm_abis_mo_check_attr(const struct gsm_abis_mo *mo,
const uint8_t * attr_ids, unsigned int num_attr_ids);
int lchan_activate(struct gsm_lchan *lchan);

View File

@ -0,0 +1,402 @@
/* Traffic Channel (TCH) part of osmo-bts OCTPHY integration */
/* Copyright (c) 2014 Octasic Inc. All rights reserved.
* Copyright (c) 2015 Harald Welte <laforge@gnumonks.org>
*
* based on a copy of osmo-bts-sysmo/l1_tch.c, which is
* Copyright (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
*
* 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 <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/bits.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/l1sap.h>
#include "l1_if.h"
#define GSM_FR_BITS 260
#define GSM_EFR_BITS 244
#define GSM_FR_BYTES 33 /* TS 101318 Chapter 5.1: 260 bits + 4bit sig */
#define GSM_HR_BYTES 14 /* TS 101318 Chapter 5.2: 112 bits, no sig */
#define GSM_EFR_BYTES 31 /* TS 101318 Chapter 5.3: 244 bits + 4bit sig */
/* input octet-aligned, output not octet-aligned */
void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i;
unsigned int num_whole_bytes = num_nibbles / 2;
/* first byte: upper nibble empty, lower nibble from src */
out[0] = (in[0] >> 4);
/* bytes 1.. */
for (i = 1; i < num_whole_bytes; i++)
out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
else
out[i] = (in[i - 1] & 0xF) << 4;
}
/* input unaligned, output octet-aligned */
void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i;
unsigned int num_whole_bytes = num_nibbles / 2;
for (i = 0; i < num_whole_bytes; i++)
out[i] = ((in[i] & 0xF) << 4) | (in[i + 1] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = (in[i] & 0xF) << 4;
}
struct msgb *l1_to_rtppayload_fr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg;
uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
cur = msgb_put(msg, GSM_FR_BYTES);
memcpy(cur, l1_payload, GSM_FR_BYTES);
#else
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
cur = msgb_put(msg, GSM_FR_BYTES);
/* step2: we need to shift the entire L1 payload by 4 bits right */
osmo_nibble_shift_right(cur, l1_payload, GSM_FR_BITS / 4);
cur[0] |= 0xD0;
#endif /* USE_L1_RTP_MODE */
return msg;
}
/*! \brief convert GSM-FR from RTP payload to L1 format
* \param[out] l1_payload payload part of L1 buffer
* \param[in] rtp_payload pointer to RTP payload data
* \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled
*/
int rtppayload_to_l1_fr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len)
{
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
memcpy(l1_payload, rtp_payload, GSM_FR_BYTES);
#else
/* step2: we need to shift the RTP payload left by one nibble */
osmo_nibble_shift_left_unal(l1_payload, rtp_payload, GSM_FR_BITS / 4);
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
#endif /* USE_L1_RTP_MODE */
return GSM_FR_BYTES;
}
static struct msgb *l1_to_rtppayload_efr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg;
uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
#ifdef USE_L1_RTP_MODE
/* new L1 can deliver bits like we need them */
cur = msgb_put(msg, GSM_EFR_BYTES);
memcpy(cur, l1_payload, GSM_EFR_BYTES);
#else
/* step1: reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, payload_len);
cur = msgb_put(msg, GSM_EFR_BYTES);
/* step 2: we need to shift the entire L1 payload by 4 bits right */
osmo_nibble_shift_right(cur, l1_payload, GSM_EFR_BITS/4);
cur[0] |= 0xC0;
#endif /* USE_L1_RTP_MODE */
return msg;
}
static int rtppayload_to_l1_efr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len)
{
#ifndef USE_L1_RTP_MODE
#warning "We don't support EFR with L1 that doesn't support RTP mode!"
#else
memcpy(l1_payload, rtp_payload, payload_len);
#endif
return payload_len;
}
static struct msgb *l1_to_rtppayload_hr(uint8_t *l1_payload, uint8_t payload_len)
{
struct msgb *msg;
uint8_t *cur;
msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP");
if (!msg)
return NULL;
if (payload_len != GSM_HR_BYTES) {
LOGP(DL1C, LOGL_ERROR, "L1 HR frame length %u != expected %u\n",
payload_len, GSM_HR_BYTES);
return NULL;
}
cur = msgb_put(msg, GSM_HR_BYTES);
memcpy(cur, l1_payload, GSM_HR_BYTES);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(cur, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
return msg;
}
/*! \brief convert GSM-FR from RTP payload to L1 format
* \param[out] l1_payload payload part of L1 buffer
* \param[in] rtp_payload pointer to RTP payload data
* \param[in] payload_len length of \a rtp_payload
* \returns number of \a l1_payload bytes filled
*/
static int rtppayload_to_l1_hr(uint8_t *l1_payload, const uint8_t *rtp_payload,
unsigned int payload_len)
{
if (payload_len != GSM_HR_BYTES) {
LOGP(DL1C, LOGL_ERROR, "RTP HR frame length %u != expected %u\n",
payload_len, GSM_HR_BYTES);
return 0;
}
memcpy(l1_payload, rtp_payload, GSM_HR_BYTES);
#ifndef USE_L1_RTP_MODE
/* reverse the bit-order of each payload byte */
osmo_revbytebits_buf(l1_payload, GSM_HR_BYTES);
#endif /* USE_L1_RTP_MODE */
return GSM_HR_BYTES;
}
/* brief receive a traffic L1 primitive for a given lchan */
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr,
tOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_DATA_INDICATION_EVT *
data_ind)
{
uint32_t payload_type = data_ind->Data.ulPayloadType;
uint8_t *payload = data_ind->Data.abyDataContent;
uint8_t payload_len;
struct msgb *rmsg = NULL;
struct gsm_lchan *lchan =
&trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
if (data_ind->Data.ulDataLength < 1) {
LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
chan_nr);
return -EINVAL;
}
payload_len = data_ind->Data.ulDataLength;
switch (payload_type) {
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE:
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_ENH_FULL_RATE:
if (lchan->type != GSM_LCHAN_TCH_F)
goto err_payload_match;
break;
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE:
if (lchan->type != GSM_LCHAN_TCH_H)
goto err_payload_match;
break;
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_FULL_RATE:
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_HALF_RATE:
if (lchan->type != GSM_LCHAN_TCH_H &&
lchan->type != GSM_LCHAN_TCH_F)
goto err_payload_match;
break;
default:
LOGP(DL1C, LOGL_NOTICE,
"%s Rx Payload Type %d is unsupported\n",
gsm_lchan_name(lchan), payload_type);
break;
}
LOGP(DL1C, LOGL_DEBUG, "%s Rx codec frame (%u): %s\n",
gsm_lchan_name(lchan), payload_len, osmo_hexdump(payload,
payload_len));
switch (payload_type) {
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE:
rmsg = l1_to_rtppayload_fr(payload, payload_len);
break;
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE:
/* Currently not supported */
#if 0
rmsg = l1_to_rtppayload_hr(payload, payload_len);
break;
#endif
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_ENH_FULL_RATE:
/* Currently not supported */
#if 0
rmsg = l1_to_rtppayload_efr(payload, payload_len);
break;
#endif
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_FULL_RATE:
case cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR_HALF_RATE:
/* Currently not supported */
#if 0
rmsg = l1_to_rtppayload_amr(payload, payload_len,
&lchan->tch.amr_mr);
#else
LOGP(DL1C, LOGL_ERROR, "OctPHY only supports FR!\n");
return -1;
#endif
break;
}
if (rmsg) {
struct osmo_phsap_prim *l1sap;
LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data,
rmsg->len));
/* add l1sap header */
rmsg->l2h = rmsg->data;
msgb_push(rmsg, sizeof(*l1sap));
rmsg->l1h = rmsg->data;
l1sap = msgb_l1sap_prim(rmsg);
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH,
PRIM_OP_INDICATION, rmsg);
l1sap->u.tch.chan_nr = chan_nr;
return l1sap_up(trx, l1sap);
}
return 0;
err_payload_match:
LOGP(DL1C, LOGL_ERROR,
"%s Rx Payload Type %d incompatible with lchan\n",
gsm_lchan_name(lchan), payload_type);
return -EINVAL;
}
#define RTP_MSGB_ALLOC_SIZE 512
/*! \brief function for incoming RTP via TCH.req
* \param rs RTP Socket
* \param[in] rtp_pl buffer containing RTP payload
* \param[in] rtp_pl_len length of \a rtp_pl
*
* This function prepares a msgb with a L1 PH-DATA.req primitive and
* queues it into lchan->dl_tch_queue.
*
* Note that the actual L1 primitive header is not fully initialized
* yet, as things like the frame number, etc. are unknown at the time we
* pre-fill the primtive.
*/
void l1if_tch_encode(struct gsm_lchan *lchan, uint32_t *payload_type,
uint8_t *data, uint32_t *len, const uint8_t *rtp_pl,
unsigned int rtp_pl_len)
{
uint8_t *l1_payload;
int rc = -1;
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(rtp_pl, rtp_pl_len));
l1_payload = &data[0];
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
if (lchan->type == GSM_LCHAN_TCH_F) {
*payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_FULL_RATE;
rc = rtppayload_to_l1_fr(l1_payload,
rtp_pl, rtp_pl_len);
} else {
*payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_HALF_RATE;
/* Not supported currently */
rc = rtppayload_to_l1_hr(l1_payload,
rtp_pl, rtp_pl_len);
}
break;
case GSM48_CMODE_SPEECH_EFR:
/* Not supported currently */
#if 0
*payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_EFR;
rc = rtppayload_to_l1_efr(l1_payload, rtp_pl, rtp_pl_len);
break;
#endif
case GSM48_CMODE_SPEECH_AMR:
/* Not supported currently */
#if 0
*payload_type = cOCTVC1_GSM_PAYLOAD_TYPE_ENUM_AMR;
rc = rtppayload_to_l1_amr(l1_payload, rtp_pl, rtp_pl_len);
break;
#endif
LOGP(DRTP, LOGL_ERROR, "OctPHY only supports FR!\n");
default:
/* we don't support CSD modes */
rc = -1;
break;
}
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
gsm_lchan_name(lchan));
return;
}
*len = rc;
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
osmo_hexdump(data, *len));
}

View File

@ -0,0 +1,130 @@
/* Layer 1 (PHY) Utilities of osmo-bts OCTPHY integration */
/* Copyright (c) 2014 Octasic Inc. All rights reserved.
* Copyright (c) 2015 Harald Welte <laforge@gnumonks.org>
*
* 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 "l1_utils.h"
#include <octphy/octvc1/gsm/octvc1_gsm_api.h>
#include <octphy/octvc1/gsm/octvc1_gsm_id.h>
#include <octphy/octvc1/hw/octvc1_hw_api.h>
const struct value_string octphy_l1sapi_names[23] =
{
{ cOCTVC1_GSM_SAPI_ENUM_IDLE, "IDLE" },
{ cOCTVC1_GSM_SAPI_ENUM_FCCH, "FCCH" },
{ cOCTVC1_GSM_SAPI_ENUM_SCH, "SCH" },
{ cOCTVC1_GSM_SAPI_ENUM_SACCH, "SACCH" },
{ cOCTVC1_GSM_SAPI_ENUM_SDCCH, "SDCCH" },
{ cOCTVC1_GSM_SAPI_ENUM_BCCH, "BCCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PCH_AGCH,"PCH_AGCH" },
{ cOCTVC1_GSM_SAPI_ENUM_CBCH, "CBCH" },
{ cOCTVC1_GSM_SAPI_ENUM_RACH, "RACH" },
{ cOCTVC1_GSM_SAPI_ENUM_TCHF, "TCH/F" },
{ cOCTVC1_GSM_SAPI_ENUM_FACCHF, "FACCH/F" },
{ cOCTVC1_GSM_SAPI_ENUM_TCHH, "TCH/H" },
{ cOCTVC1_GSM_SAPI_ENUM_FACCHH, "FACCH/H" },
{ cOCTVC1_GSM_SAPI_ENUM_NCH, "NCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PDTCH, "PDTCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PACCH, "PACCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PBCCH, "PBCCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PAGCH, "PAGCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PPCH, "PPCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PNCH, "PNCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PTCCH, "PTCCH" },
{ cOCTVC1_GSM_SAPI_ENUM_PRACH, "PRACH" },
{ 0, NULL }
};
const struct value_string octphy_dir_names[5] =
{
{ cOCTVC1_GSM_DIRECTION_ENUM_NONE, "None" },
{ cOCTVC1_GSM_DIRECTION_ENUM_TX_BTS_MS, "TX_BTS_MS(DL)" },
{ cOCTVC1_GSM_DIRECTION_ENUM_RX_BTS_MS, "RX_BTS_MS(UL)" },
{ cOCTVC1_GSM_DIRECTION_ENUM_TX_BTS_MS | cOCTVC1_GSM_DIRECTION_ENUM_RX_BTS_MS, "BOTH" },
{ 0, NULL }
};
const struct value_string octphy_clkmgr_state_vals[8] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNINITIALIZE, "UNINITIALIZED" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE, "IDLE" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_NO_EXT_CLOCK, "NO_EXT_CLOCK" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_LOCKED, "LOCKED" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNLOCKED, "UNLOCKED" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_ERROR, "ERROR" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_DISABLE, "DISABLED" },
{ 0, NULL }
};
const struct value_string octphy_cid_vals[35] = {
{ cOCTVC1_GSM_MSG_TRX_OPEN_CID, "TRX-OPEN" },
{ cOCTVC1_GSM_MSG_TRX_CLOSE_CID, "TRX-CLOSE" },
{ cOCTVC1_GSM_MSG_TRX_STATUS_CID, "TRX-STATUS" },
{ cOCTVC1_GSM_MSG_TRX_INFO_CID, "TRX-INFO" },
{ cOCTVC1_GSM_MSG_TRX_RESET_CID, "TRX-RESET" },
{ cOCTVC1_GSM_MSG_TRX_MODIFY_CID, "TRX-MODIFY" },
{ cOCTVC1_GSM_MSG_TRX_LIST_CID, "TRX-LIST" },
{ cOCTVC1_GSM_MSG_TRX_CLOSE_ALL_CID, "TRX-CLOSE-ALL" },
{ cOCTVC1_GSM_MSG_TRX_START_RECORD_CID, "RECORD-START" },
{ cOCTVC1_GSM_MSG_TRX_STOP_RECORD_CID, "RECORD-STOP" },
{ cOCTVC1_GSM_MSG_TRX_ACTIVATE_LOGICAL_CHANNEL_CID, "LCHAN-ACT" },
{ cOCTVC1_GSM_MSG_TRX_DEACTIVATE_LOGICAL_CHANNEL_CID, "LCHAN-DEACT" },
{ cOCTVC1_GSM_MSG_TRX_STATUS_LOGICAL_CHANNEL_CID, "LCHAN-STATUS" },
{ cOCTVC1_GSM_MSG_TRX_INFO_LOGICAL_CHANNEL_CID, "LCHAN-INFO" },
{ cOCTVC1_GSM_MSG_TRX_LIST_LOGICAL_CHANNEL_CID, "LCHAN-LIST" },
{ cOCTVC1_GSM_MSG_TRX_REQUEST_LOGICAL_CHANNEL_EMPTY_FRAME_CID,
"LCHAN-EMPTY-FRAME" },
{ cOCTVC1_GSM_MSG_TRX_REQUEST_LOGICAL_CHANNEL_DATA_CID, "LCHAN-DATA" },
{ cOCTVC1_GSM_MSG_TRX_ACTIVATE_PHYSICAL_CHANNEL_CID, "PCHAN-ACT" },
{ cOCTVC1_GSM_MSG_TRX_DEACTIVATE_PHYSICAL_CHANNEL_CID, "PCHAN-DEACT" },
{ cOCTVC1_GSM_MSG_TRX_STATUS_PHYSICAL_CHANNEL_CID, "PCHAN-STATUS" },
{ cOCTVC1_GSM_MSG_TRX_RESET_PHYSICAL_CHANNEL_CID, "PCHAN-RESET" },
{ cOCTVC1_GSM_MSG_TRX_LIST_PHYSICAL_CHANNEL_CID, "PCHAN-LIST" },
{ cOCTVC1_GSM_MSG_TRX_INFO_PHYSICAL_CHANNEL_CID, "PCHAN-INFO" },
{ cOCTVC1_GSM_MSG_TRX_MODIFY_PHYSICAL_CHANNEL_CIPHERING_CID,
"PCHAN-CIPH-MODIFY" },
{ cOCTVC1_GSM_MSG_TRX_INFO_PHYSICAL_CHANNEL_CIPHERING_CID,
"PCHAN-CIPH-INFO" },
{ cOCTVC1_GSM_MSG_TRX_INFO_PHYSICAL_CHANNEL_MEASUREMENT_CID,
"PCHAN-MEASUREMENT" },
{ cOCTVC1_GSM_MSG_TRX_INFO_RF_CID, "RF-INFO" },
{ cOCTVC1_GSM_MSG_TRX_MODIFY_RF_CID, "RF-MODIFY" },
{ cOCTVC1_GSM_MSG_TAP_FILTER_LIST_CID, "TAP-FILTER-LIST" },
{ cOCTVC1_GSM_MSG_TAP_FILTER_INFO_CID, "TAP-FILTER-INFO" },
{ cOCTVC1_GSM_MSG_TAP_FILTER_STATS_CID, "TAP-FILTER-STATS" },
{ cOCTVC1_GSM_MSG_TAP_FILTER_MODIFY_CID, "TAP-FILTER-MODIFY" },
{ cOCTVC1_GSM_MSG_TRX_START_LOGICAL_CHANNEL_RAW_DATA_INDICATIONS_CID,
"LCHAN-RAW-DATA-START" },
{ cOCTVC1_GSM_MSG_TRX_STOP_LOGICAL_CHANNEL_RAW_DATA_INDICATIONS_CID,
"LCHAN-RAW-DATA-STOP" },
{ 0, NULL }
};
const struct value_string octphy_eid_vals[7] = {
{ cOCTVC1_GSM_MSG_TRX_TIME_INDICATION_EID, "TIME.ind" },
{ cOCTVC1_GSM_MSG_TRX_STATUS_CHANGE_EID, "TRX-STATUS-CHG.ind" },
{ cOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_DATA_INDICATION_EID,
"LCHAN-DATA.ind" },
{ cOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_READY_TO_SEND_INDICATION_EID,
"LCHAN-RTS.ind" },
{ cOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_RACH_INDICATION_EID,
"LCHAN-RACH.ind" },
{ cOCTVC1_GSM_MSG_TRX_LOGICAL_CHANNEL_RAW_DATA_INDICATION_EID,
"LCHAN-RAW-DATA.ind" },
{ 0, NULL }
};

View File

@ -0,0 +1,9 @@
#pragma once
#include <osmocom/core/utils.h>
const struct value_string octphy_l1sapi_names[23];
const struct value_string octphy_dir_names[5];
const struct value_string octphy_clkmgr_state_vals[8];
const struct value_string octphy_cid_vals[35];
const struct value_string octphy_eid_vals[7];

305
src/osmo-bts-octphy/main.c Normal file
View File

@ -0,0 +1,305 @@
/* Main program of osmo-bts for OCTPHY-2G */
/* Copyright (c) 2014 Octasic Inc. All rights reserved.
* Copyright (c) 2015 Harald Welte <laforge@gnumonks.org>
*
* based on a copy of osmo-bts-sysmo/main.c, which is
* Copyright (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
*
* 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 <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sched.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/abis.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/vty.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/l1sap.h>
#include "l1_if.h"
int pcu_direct = 0;
#define RF_LOCK_PATH "/var/lock/bts_rf_lock"
static const char *config_file = "osmo-bts.cfg";
static int daemonize = 0;
static int rt_prio = -1;
static char *gsmtap_ip = 0;
static void print_help()
{
printf( "Some useful options:\n"
" -h --help this text\n"
" -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"
" -D --daemonize For the process into a background daemon\n"
" -c --config-file Specify the filename of the config file\n"
" -s --disable-color Don't use colors in stderr log output\n"
" -T --timestamp Prefix every log line with a timestamp\n"
" -V --version Print version information and exit\n"
" -e --log-level Set a global log-level\n"
" -r --realtime PRIO Use SCHED_RR with the specified priority\n"
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
);
}
/* FIXME: finally get some option parsing code into libosmocore */
static void handle_options(int argc, char **argv)
{
while (1) {
int option_idx = 0, c;
static const struct option long_options[] = {
/* FIXME: all those are generic Osmocom app options */
{ "help", 0, 0, 'h' },
{ "debug", 1, 0, 'd' },
{ "daemonize", 0, 0, 'D' },
{ "config-file", 1, 0, 'c' },
{ "disable-color", 0, 0, 's' },
{ "timestamp", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' },
{ "realtime", 1, 0, 'r' },
{ "gsmtap-ip", 1, 0, 'i' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:r:i:m:l:",
long_options, &option_idx);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
exit(0);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
config_file = strdup(optarg);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'r':
rt_prio = atoi(optarg);
break;
case 'i':
gsmtap_ip = optarg;
break;
default:
break;
}
}
}
static struct gsm_bts *bts;
static void signal_handler(int signal)
{
fprintf(stderr, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
//osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
bts_shutdown(bts, "SIGINT");
break;
case SIGABRT:
case SIGUSR1:
case SIGUSR2:
talloc_report_full(tall_bts_ctx, stderr);
break;
default:
break;
}
}
static int write_pid_file(char *procname)
{
FILE *outf;
char tmp[PATH_MAX + 1];
snprintf(tmp, sizeof(tmp) - 1, "/var/run/%s.pid", procname);
tmp[PATH_MAX - 1] = '\0';
outf = fopen(tmp, "w");
if (!outf)
return -1;
fprintf(outf, "%d\n", getpid());
fclose(outf);
return 0;
}
int main(int argc, char **argv)
{
struct stat st;
struct sched_param param;
struct gsm_bts_role_bts *btsb;
struct e1inp_line *line;
void *tall_msgb_ctx;
int rc;
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
msgb_set_talloc_ctx(tall_msgb_ctx);
bts_log_init(NULL);
bts = gsm_bts_alloc(tall_bts_ctx);
vty_init(&bts_vty_info);
e1inp_vty_init();
bts_vty_init(bts, &bts_log_info);
handle_options(argc, argv);
/* enable realtime priority for us */
if (rt_prio != -1) {
memset(&param, 0, sizeof(param));
param.sched_priority = rt_prio;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
fprintf(stderr,
"Setting SCHED_RR priority(%d) failed: %s\n",
param.sched_priority, strerror(errno));
exit(1);
}
}
if (gsmtap_ip) {
gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
if (!gsmtap) {
fprintf(stderr, "Failed during gsmtap_init()\n");
exit(1);
}
gsmtap_source_add_sink(gsmtap);
}
if (bts_init(bts) < 0) {
fprintf(stderr, "unable to to open bts\n");
exit(1);
}
btsb = bts_role_bts(bts);
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
abis_init(bts);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
config_file);
exit(1);
}
if (stat(RF_LOCK_PATH, &st) == 0) {
LOGP(DL1C, LOGL_NOTICE,
"Not starting BTS due to RF_LOCK file present\n");
exit(23);
}
write_pid_file("osmo-bts");
rc = telnet_init(tall_bts_ctx, NULL, 4241);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
if (pcu_sock_init()) {
fprintf(stderr, "PCU L1 socket failed\n");
exit(-1);
}
signal(SIGINT, &signal_handler);
//signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
if (!btsb->bsc_oml_host) {
fprintf(stderr,
"Cannot start BTS without knowing BSC OML IP\n");
exit(1);
}
line = abis_open(bts, btsb->bsc_oml_host, "OsmoBTS-OCTPHY");
if (!line) {
fprintf(stderr, "unable to connect to BSC\n");
exit(1);
}
/* Open L1 interface */
rc = l1if_open(bts->c0->role_bts.l1h);
if (rc < 0) {
LOGP(DL1C, LOGL_FATAL, "Cannot open L1 Interface\n");
exit(1);
}
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
while (1) {
log_reset_context();
osmo_select_main(0);
}
}
void bts_model_abis_close(struct gsm_bts *bts)
{
/* for now, we simply terminate the program and re-spawn */
bts_shutdown(bts, "Abis close");
}

View File

@ -0,0 +1,364 @@
/* Layer 1 (PHY) interface of osmo-bts OCTPHY integration */
/* Copyright (c) 2015 Harald Welte <laforge@gnumonks.org>
*
* 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 <stdint.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmo-bts/logging.h>
#include "l1_if.h"
#include "l1_oml.h"
#include "l1_utils.h"
#include <octphy/octvc1/octvc1_rc2string.h>
#include <octphy/octvc1/hw/octvc1_hw_api.h>
#include <octphy/octvc1/hw/octvc1_hw_api_swap.h>
/* Chapter 12.1 */
static int get_pcb_info_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp, void *data)
{
tOCTVC1_HW_MSG_PCB_INFO_RSP *pir =
(tOCTVC1_HW_MSG_PCB_INFO_RSP *) resp->l2h;
mOCTVC1_HW_MSG_PCB_INFO_RSP_SWAP(pir);
LOGP(DL1C, LOGL_INFO, "HW-PCB-INFO.resp: Name=%s %s, Serial=%s, "
"FileName=%s, InfoSource=%u, InfoState=%u, GpsName=%s, "
"WiFiName=%s\n", pir->szName, pir->ulDeviceId ? "SEC" : "PRI",
pir->szSerial, pir->szFilename, pir->ulInfoSource,
pir->ulInfoState, pir->szGpsName, pir->szWifiName);
msgb_free(resp);
return 0;
}
/* Chapter 12.1 */
int octphy_hw_get_pcb_info(struct octphy_hdl *fl1h)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_PCB_INFO_CMD *pic;
pic = (tOCTVC1_HW_MSG_PCB_INFO_CMD *) msgb_put(msg, sizeof(*pic));
l1if_fill_msg_hdr(&pic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_PCB_INFO_CID);
mOCTVC1_HW_MSG_PCB_INFO_CMD_SWAP(pic);
return l1if_req_compl(fl1h, msg, get_pcb_info_compl_cb, NULL);
}
/* Chapter 12.9 */
static int rf_port_info_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_RF_PORT_INFO_RSP *pir =
(tOCTVC1_HW_MSG_RF_PORT_INFO_RSP *) resp->l2h;
mOCTVC1_HW_MSG_RF_PORT_INFO_RSP_SWAP(pir);
LOGP(DL1C, LOGL_INFO, "RF-PORT-INFO.resp Idx=%u, InService=%u, "
"hOwner=0x%x, Id=%u, FreqMin=%u, FreqMax=%u\n",
pir->ulPortIndex, pir->ulInService, pir->hOwner,
pir->ulPortInterfaceId, pir->ulFrequencyMinKhz,
pir->ulFrequencyMaxKhz);
msgb_free(resp);
return 0;
}
/* Chapter 12.9 */
int octphy_hw_get_rf_port_info(struct octphy_hdl *fl1h, uint32_t index)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_RF_PORT_INFO_CMD *pic;
pic = (tOCTVC1_HW_MSG_RF_PORT_INFO_CMD *) msgb_put(msg, sizeof(*pic));
l1if_fill_msg_hdr(&pic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_RF_PORT_INFO_CID);
pic->ulPortIndex = index;
mOCTVC1_HW_MSG_RF_PORT_INFO_CMD_SWAP(pic);
return l1if_req_compl(fl1h, msg, rf_port_info_compl_cb, NULL);
}
static const struct value_string radio_std_vals[] = {
{ cOCTVC1_RADIO_STANDARD_ENUM_GSM, "GSM" },
{ cOCTVC1_RADIO_STANDARD_ENUM_UMTS, "UMTS" },
{ cOCTVC1_RADIO_STANDARD_ENUM_LTE, "LTE" },
{ cOCTVC1_RADIO_STANDARD_ENUM_INVALID, "INVALID" },
{ 0, NULL }
};
/* Chapter 12.10 */
static int rf_port_stats_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_RF_PORT_STATS_RSP *psr =
(tOCTVC1_HW_MSG_RF_PORT_STATS_RSP *) resp->l2h;
mOCTVC1_HW_MSG_RF_PORT_STATS_RSP_SWAP(psr);
LOGP(DL1C, LOGL_INFO, "RF-PORT-STATS.resp Idx=%u RadioStandard=%s, "
"Rx(Bytes=%u, Overflow=%u, AvgBps=%u, Period=%uus, Freq=%u) "
"Tx(Bytes=%i, Underflow=%u, AvgBps=%u, Period=%uus, Freq=%u)\n",
psr->ulPortIndex,
get_value_string(radio_std_vals, psr->ulRadioStandard),
psr->RxStats.ulRxByteCnt, psr->RxStats.ulRxOverflowCnt,
psr->RxStats.ulRxAverageBytePerSecond,
psr->RxStats.ulRxAveragePeriodUs,
psr->RxStats.ulFrequencyKhz,
psr->TxStats.ulTxByteCnt, psr->TxStats.ulTxUnderflowCnt,
psr->TxStats.ulTxAverageBytePerSecond,
psr->TxStats.ulTxAveragePeriodUs,
psr->TxStats.ulFrequencyKhz);
msgb_free(resp);
return 0;
}
/* Chapter 12.10 */
int octphy_hw_get_rf_port_stats(struct octphy_hdl *fl1h, uint32_t index)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_RF_PORT_STATS_CMD *psc;
psc = (tOCTVC1_HW_MSG_RF_PORT_STATS_CMD *) msgb_put(msg, sizeof(*psc));
l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_RF_PORT_STATS_CID);
psc->ulPortIndex = index;
psc->ulResetStatsFlag = cOCT_FALSE;
mOCTVC1_HW_MSG_RF_PORT_STATS_CMD_SWAP(psc);
return l1if_req_compl(fl1h, msg, rf_port_stats_compl_cb, NULL);
}
static const struct value_string rx_gain_mode_vals[] = {
{ cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_MGC, "Manual" },
{ cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_AGC_FAST_ATK, "Automatic (fast)" },
{ cOCTVC1_RADIO_RX_GAIN_CTRL_MODE_ENUM_AGC_SLOW_ATK, "Automatic (slow)" },
{ 0, NULL }
};
/* Chapter 12.13 */
static int rf_ant_rx_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP *arc =
(tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP *) resp->l2h;
mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_RSP_SWAP(arc);
LOGP(DL1C, LOGL_INFO, "ANT-RX-CONFIG.resp(Port=%u, Ant=%u): %s, "
"Gain %d dB, GainCtrlMode=%s\n",
arc->ulPortIndex, arc->ulAntennaIndex,
arc->ulEnableFlag ? "Enabled" : "Disabled",
arc->lRxGaindB/512,
get_value_string(rx_gain_mode_vals, arc->ulRxGainMode));
msgb_free(resp);
return 0;
}
/* Chapter 12.13 */
int octphy_hw_get_rf_ant_rx_config(struct octphy_hdl *fl1h, uint32_t port_idx,
uint32_t ant_idx)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD *psc;
psc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD *)
msgb_put(msg, sizeof(*psc));
l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CID);
psc->ulPortIndex = port_idx;
psc->ulAntennaIndex = ant_idx;
mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CMD_SWAP(psc);
return l1if_req_compl(fl1h, msg, rf_ant_rx_compl_cb, NULL);
}
/* Chapter 12.14 */
static int rf_ant_tx_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP *atc =
(tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP *) resp->l2h;
mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_RSP_SWAP(atc);
LOGP(DL1C, LOGL_INFO, "ANT-TX-CONFIG.resp(Port=%u, Ant=%u): %s, "
"Gain %d dB\n",
atc->ulPortIndex, atc->ulAntennaIndex,
atc->ulEnableFlag ? "Enabled" : "Disabled",
atc->lTxGaindB/512);
msgb_free(resp);
return 0;
}
/* Chapter 12.14 */
int octphy_hw_get_rf_ant_tx_config(struct octphy_hdl *fl1h, uint32_t port_idx,
uint32_t ant_idx)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD *psc;
psc = (tOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD *)
msgb_put(msg, sizeof(*psc));
l1if_fill_msg_hdr(&psc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_RX_CONFIG_CID);
psc->ulPortIndex = port_idx;
psc->ulAntennaIndex = ant_idx;
mOCTVC1_HW_MSG_RF_PORT_INFO_ANTENNA_TX_CONFIG_CMD_SWAP(psc);
return l1if_req_compl(fl1h, msg, rf_ant_tx_compl_cb, NULL);
}
static const struct value_string clocksync_source_vals[] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_1HZ, "1 Hz" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_10MHZ, "10 MHz" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_30_72MHZ, "30.72 MHz" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_FREQ_1HZ_EXT, "1 Hz (ext)"},
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_ENUM_NONE, "None" },
{ 0, NULL }
};
static const struct value_string clocksync_sel_vals[] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_AUTOSELECT,
"Autoselect" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_CONFIG_FILE,
"Config File" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_SELECTION_ENUM_HOST_APPLICATION,
"Host Application" },
{ 0, NULL }
};
static const struct value_string clocksync_source_state_vals[] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_INVALID, "Invalid" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_VALID, "Valid" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_SOURCE_STATE_ENUM_UNSPECIFIED,
"Unspecified" },
{ 0, NULL }
};
static const struct value_string clocksync_state_vals[] = {
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNINITIALIZE,
"Uninitialized" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_IDLE, "Idle" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_NO_EXT_CLOCK,
"No External Clock" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_LOCKED, "Locked" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_UNLOCKED,"Unlocked" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_ERROR, "Error" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_DISABLE, "Disabled" },
{ cOCTVC1_HW_CLOCK_SYNC_MGR_STATE_ENUM_LOSS_EXT_CLOCK,
"Loss of Ext Clock" },
{ 0, NULL }
};
/* Chapter 12.15 */
static int get_clock_sync_compl_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP *cir =
(tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP *) resp->l2h;
mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_RSP_SWAP(cir);
LOGP(DL1C, LOGL_INFO, "CLOCK-SYNC-MGR-INFO.resp Reference=%s ",
get_value_string(clocksync_source_vals, cir->ulClkSourceRef));
LOGPC(DL1C, LOGL_INFO, "Selection=%s)\n",
get_value_string(clocksync_sel_vals, cir->ulClkSourceSelection));
msgb_free(resp);
return 0;
}
/* Chapter 12.15 */
int octphy_hw_get_clock_sync_info(struct octphy_hdl *fl1h)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD *cic;
cic = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD *)
msgb_put(msg, sizeof(*cic));
l1if_fill_msg_hdr(&cic->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CID);
mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_INFO_CMD_SWAP(cic);
return l1if_req_compl(fl1h, msg, get_clock_sync_compl_cb, NULL);
}
/* Chapter 12.16 */
static int get_clock_sync_stats_cb(struct gsm_bts_trx *trx, struct msgb *resp,
void *data)
{
tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP *csr =
(tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP *) resp->l2h;
mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_RSP_SWAP(csr);
LOGP(DL1C, LOGL_INFO, "CLOCK-SYNC-MGR-STATS.resp State=%s, "
"ClockError=%d DroppedCycles=%d, PllFreqHz=%u, PllFract=%u, "
"SlipCnt=%u SyncLosses=%u SourceState=%u, DacValue=%u\n",
get_value_string(clocksync_state_vals, csr->ulState),
csr->lClockError, csr->lDroppedCycles, csr->ulPllFreqHz,
csr->ulPllFractionalFreqHz, csr->ulSlipCnt,
csr->ulSyncLosseCnt, csr->ulSourceState, csr->ulDacValue);
msgb_free(resp);
return 0;
}
/* Chapter 12.16 */
int octphy_hw_get_clock_sync_stats(struct octphy_hdl *fl1h)
{
struct msgb *msg = l1p_msgb_alloc();
tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD *csc;
csc = (tOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD *)
msgb_put(msg, sizeof(*csc));
l1if_fill_msg_hdr(&csc->Header, msg, fl1h, cOCTVC1_MSG_TYPE_COMMAND,
cOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CID);
mOCTVC1_HW_MSG_CLOCK_SYNC_MGR_STATS_CMD_SWAP(csc);
return l1if_req_compl(fl1h, msg, get_clock_sync_stats_cb, NULL);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include "l1_if.h"
int octphy_hw_get_pcb_info(struct octphy_hdl *fl1h);
int octphy_hw_get_rf_port_info(struct octphy_hdl *fl1h, uint32_t index);
int octphy_hw_get_rf_port_stats(struct octphy_hdl *fl1h, uint32_t index);
int octphy_hw_get_rf_ant_rx_config(struct octphy_hdl *fl1h, uint32_t port_idx,
uint32_t ant_idx);
int octphy_hw_get_rf_ant_tx_config(struct octphy_hdl *fl1h, uint32_t port_idx,
uint32_t ant_idx);
int octphy_hw_get_clock_sync_info(struct octphy_hdl *fl1h);
int octphy_hw_get_clock_sync_stats(struct octphy_hdl *fl1h);

View File

@ -0,0 +1,193 @@
/* VTY interface for osmo-bts OCTPHY integration */
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
*
* 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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/macaddr.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/vty.h>
#include "l1_if.h"
#include "l1_utils.h"
#include "octphy_hw_api.h"
#define TRX_STR "Transceiver related commands\n" "TRX number\n"
#define SHOW_TRX_STR \
SHOW_STR \
TRX_STR
static struct gsm_bts *vty_bts;
/* configuration */
DEFUN(cfg_bts_phy_hwaddr, cfg_bts_phy_hwaddr_cmd,
"phy-hw-addr HWADDR",
"Configure the hardware addess of the OCTPHY\n"
"hardware address in aa:bb:cc:dd:ee:ff format\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_trx *trx = bts->c0;
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
int rc;
rc = osmo_macaddr_parse(fl1h->phy_addr.sll_addr, argv[0]);
if (rc < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(cfg_bts_phy_netdev, cfg_bts_phy_netdev_cmd,
"phy-netdev NAME",
"Configure the hardware device towards the OCTPHY\n"
"Ethernet device name\n")
{
struct gsm_bts *bts = vty->index;
struct gsm_bts_trx *trx = bts->c0;
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
if (fl1h->netdev_name)
talloc_free(fl1h->netdev_name);
fl1h->netdev_name = talloc_strdup(fl1h, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_rf_port_idx, cfg_trx_rf_port_idx_cmd,
"rf-port-index <0-255>",
"Configure the RF Port for this TRX\n"
"RF Port Index\n")
{
struct gsm_bts_trx *trx = vty->index;
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
fl1h->config.rf_port_index = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_rx_gain_db, cfg_trx_rx_gain_db_cmd,
"rx-gain <0-73>",
"Configure the Rx Gain in dB\n"
"Rx gain in dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
fl1h->config.rx_gain_db = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_trx_tx_atten_db, cfg_trx_tx_atten_db_cmd,
"tx-attenuation <0-359>",
"Configure the Tx Attenuation in quarter-dB\n"
"Tx attenuation in quarter-dB\n")
{
struct gsm_bts_trx *trx = vty->index;
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
fl1h->config.tx_atten_db = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(get_rf_port_stats, get_rf_port_stats_cmd,
"get-rf-port-stats <0-1>",
"Obtain statistics for the RF Port\n"
"RF Port Number\n")
{
struct octphy_hdl *fl1h = trx_octphy_hdl(vty_bts->c0);
octphy_hw_get_rf_port_stats(fl1h, atoi(argv[0]));
return CMD_SUCCESS;
}
DEFUN(get_clk_sync_stats, get_clk_sync_stats_cmd,
"get-clk-sync-stats",
"Obtain statistics for the Clock Sync Manager\n")
{
struct octphy_hdl *fl1h = trx_octphy_hdl(vty_bts->c0);
octphy_hw_get_clock_sync_stats(fl1h);
return CMD_SUCCESS;
}
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
{
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
struct octphy_hdl *fl1h = trx_octphy_hdl(bts->c0);
if (btsb->auto_band)
vty_out(vty, " auto-band%s", VTY_NEWLINE);
vty_out(vty, " phy-hw-addr %02x:%02x:%02x:%02x:%02x:%02x%s",
fl1h->phy_addr.sll_addr[0], fl1h->phy_addr.sll_addr[1],
fl1h->phy_addr.sll_addr[2], fl1h->phy_addr.sll_addr[3],
fl1h->phy_addr.sll_addr[4], fl1h->phy_addr.sll_addr[5],
VTY_NEWLINE);
}
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
{
struct octphy_hdl *fl1h = trx_octphy_hdl(trx);
vty_out(vty, " rx-gain %u%s", fl1h->config.rx_gain_db,
VTY_NEWLINE);
vty_out(vty, " tx-attenuation %u%s", fl1h->config.tx_atten_db,
VTY_NEWLINE);
}
int bts_model_vty_init(struct gsm_bts *bts)
{
vty_bts = bts;
install_element(BTS_NODE, &cfg_bts_phy_hwaddr_cmd);
install_element(BTS_NODE, &cfg_bts_phy_netdev_cmd);
install_element(TRX_NODE, &cfg_trx_rf_port_idx_cmd);
install_element(TRX_NODE, &cfg_trx_rx_gain_db_cmd);
install_element(TRX_NODE, &cfg_trx_tx_atten_db_cmd);
install_element_ve(&get_rf_port_stats_cmd);
install_element_ve(&get_clk_sync_stats_cmd);
return 0;
}

View File

@ -0,0 +1,190 @@
/* Utility routines for dealing with OCTPKT/OCTVC1 in msgb */
/* Copyright (c) 2015 Harald Welte <laforge@gnumonks.org>
*
* 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 <unistd.h>
#include <arpa/inet.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmo-bts/gsm_data.h>
#include <octphy/octpkt/octpkt_hdr.h>
#include <octphy/octpkt/octpkt_hdr_swap.h>
#include <octphy/octvc1/octvocnet_pkt.h>
#include <octphy/octvc1/octvc1_msg.h>
#include <octphy/octvc1/octvc1_msg_swap.h>
#include <octphy/octvc1/gsm/octvc1_gsm_api.h>
#include "l1_if.h"
#include "octpkt.h"
/* push a common header (1 dword) to the start of a msgb */
void octpkt_push_common_hdr(struct msgb *msg, uint8_t format,
uint8_t trace, uint32_t ptype)
{
uint32_t ch;
uint32_t *chptr;
uint32_t tot_len = msgb_length(msg) + sizeof(ch);
ch = ((format & cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_FORMAT_BIT_MASK)
<< cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_FORMAT_BIT_OFFSET) |
((trace & cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_TRACE_BIT_MASK)
<< cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_TRACE_BIT_OFFSET) |
((ptype & cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_CONTROL_PROTOCOL_TYPE_BIT_MASK)
<< cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_CONTROL_PROTOCOL_TYPE_BIT_OFFSET) |
(tot_len & cOCTPKT_HDR_FORMAT_PROTO_TYPE_LEN_MASK_LENGTH_BIT_MASK);
chptr = (uint32_t *) msgb_push(msg, sizeof(ch));
*chptr = htonl(ch);
}
/* push a control header (3 dwords) to the start of a msgb. This format
* is used for command and response packets */
void octvocnet_push_ctl_hdr(struct msgb *msg, uint32_t dest_fifo_id,
uint32_t src_fifo_id, uint32_t socket_id)
{
tOCTVOCNET_PKT_CTL_HEADER *ch;
ch = (tOCTVOCNET_PKT_CTL_HEADER *) msgb_push(msg, sizeof(*ch));
ch->ulDestFifoId = htonl(dest_fifo_id);
ch->ulSourceFifoId = htonl(src_fifo_id);
ch->ulSocketId = htonl(socket_id);
}
/* push a data header (3 dwords) to the start of a msgb. This format is
* used for event packets */
static void octvocnet_push_data_hdr(struct msgb *msg,
uint32_t log_obj_hdl,
uint32_t log_obj_pkt_port,
uint32_t dest_fifo_id)
{
tOCTVOCNET_PKT_DATA_HEADER *dh;
dh = (tOCTVOCNET_PKT_DATA_HEADER *) msgb_push(msg, sizeof(*dh));
dh->hLogicalObj = htonl(log_obj_hdl);
dh->ulLogicalObjPktPort = htonl(log_obj_pkt_port);
dh->ulDestFifoId = htonl(dest_fifo_id);
}
/* common msg_header shared by all control messages. host byte order! */
void octvc1_fill_msg_hdr(tOCTVC1_MSG_HEADER *mh, uint32_t len,
uint32_t sess_id, uint32_t trans_id,
uint32_t user_info, uint32_t msg_type,
uint32_t flags, uint32_t api_cmd)
{
uint32_t type_r_cmdid;
type_r_cmdid = ((msg_type & cOCTVC1_MSG_TYPE_BIT_MASK)
<< cOCTVC1_MSG_TYPE_BIT_OFFSET) |
((api_cmd & cOCTVC1_MSG_ID_BIT_MASK)
<< cOCTVC1_MSG_ID_BIT_OFFSET);
/* Resync? Flags? */
mh->ulLength = len;
mh->ulTransactionId = trans_id;
mh->ul_Type_R_CmdId = type_r_cmdid;
mh->ulSessionId = sess_id;
mh->ulReturnCode = 0;
mh->ulUserInfo = user_info;
}
static void octvc1_push_msg_hdr(struct msgb *msg,
uint32_t sess_id,
uint32_t trans_id,
uint32_t user_info,
uint32_t msg_type,
uint32_t flags,
uint32_t api_cmd)
{
tOCTVC1_MSG_HEADER *mh;
uint32_t len = msgb_length(msg);
mh = (tOCTVC1_MSG_HEADER *) msgb_push(msg, sizeof(*mh));
octvc1_fill_msg_hdr(mh, len, sess_id, trans_id, user_info, msg_type, flags, api_cmd);
}
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
/*! \brief Initialize a packet socket
* \param[in] tye Socket type like SOCK_RAW or SOCK_DGRAM
* \param[in] proto The link-layer protocol in network byte order
* \param[in] bind_dev The name of the interface to bind to (if any)
* \param[in] flags flags like \ref OSMO_SOCK_F_BIND
*
* This function creates a new packet socket of \a type and \a proto
* and optionally bnds to it, if stated in the \a flags parameter.
*/
int osmo_sock_packet_init(uint16_t type, uint16_t proto, const char *bind_dev,
unsigned int flags)
{
int sfd, rc, on = 1;
if (flags & OSMO_SOCK_F_CONNECT)
return -EINVAL;
sfd = socket(AF_PACKET, type, proto);
if (sfd < 0)
return -1;
if (flags & OSMO_SOCK_F_NONBLOCK) {
if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
perror("cannot set this socket unblocking");
close(sfd);
return -EINVAL;
}
}
if (bind_dev) {
struct sockaddr_ll sa;
struct ifreq ifr;
/* resolve the string device name to an ifindex */
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, bind_dev, sizeof(ifr.ifr_name));
rc = ioctl(sfd, SIOCGIFINDEX, &ifr);
if (rc < 0)
goto err;
memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(proto);
sa.sll_ifindex = ifr.ifr_ifindex;
/* according to the packet(7) man page, bind() will only
* use sll_protocol nad sll_ifindex */
rc = bind(sfd, (struct sockaddr *)&sa, sizeof(sa));
if (rc < 0)
goto err;
}
return sfd;
err:
close(sfd);
return -1;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
/* push a common header (1 dword) to the start of a msgb */
void octpkt_push_common_hdr(struct msgb *msg, uint8_t format,
uint8_t trace, uint32_t ptype);
/* common msg_header shared by all control messages */
void octvc1_fill_msg_hdr(tOCTVC1_MSG_HEADER *mh, uint32_t len,
uint32_t sess_id, uint32_t trans_id,
uint32_t user_info, uint32_t msg_type,
uint32_t flags, uint32_t api_cmd);
/* push a control header (3 dwords) to the start of a msgb. This format
* is used for command and response packets */
void octvocnet_push_ctl_hdr(struct msgb *msg, uint32_t dest_fifo_id,
uint32_t src_fifo_id, uint32_t socket_id);
int osmo_sock_packet_init(uint16_t type, uint16_t proto, const char *bind_dev,
unsigned int flags);
int tx_trx_open(struct gsm_bts_trx *trx);