git add for merge
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# Python for CI of OAI-eNB + COTS-UE
|
||||
#
|
||||
# Required Python Version
|
||||
# Python 3.x
|
||||
#
|
||||
# Required Python Package
|
||||
# pexpect
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Version
|
||||
#-----------------------------------------------------------
|
||||
Version = '0.2'
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Constants
|
||||
#-----------------------------------------------------------
|
||||
ALL_PROCESSES_OK = 0
|
||||
ENB_PROCESS_FAILED = -1
|
||||
ENB_PROCESS_OK = +1
|
||||
ENB_PROCESS_SEG_FAULT = -11
|
||||
ENB_PROCESS_ASSERTION = -12
|
||||
ENB_PROCESS_REALTIME_ISSUE = -13
|
||||
ENB_PROCESS_NOLOGFILE_TO_ANALYZE = -14
|
||||
ENB_PROCESS_SLAVE_RRU_NOT_SYNCED = -15
|
||||
HSS_PROCESS_FAILED = -2
|
||||
HSS_PROCESS_OK = +2
|
||||
MME_PROCESS_FAILED = -3
|
||||
MME_PROCESS_OK = +3
|
||||
SPGW_PROCESS_FAILED = -4
|
||||
SPGW_PROCESS_OK = +4
|
||||
UE_IP_ADDRESS_ISSUE = -5
|
||||
OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE = -20
|
||||
OAI_UE_PROCESS_COULD_NOT_SYNC = -21
|
||||
OAI_UE_PROCESS_ASSERTION = -22
|
||||
OAI_UE_PROCESS_FAILED = -23
|
||||
OAI_UE_PROCESS_NO_TUNNEL_INTERFACE = -24
|
||||
OAI_UE_PROCESS_SEG_FAULT = -25
|
||||
OAI_UE_PROCESS_OK = +6
|
||||
|
||||
UE_STATUS_DETACHED = 0
|
||||
UE_STATUS_DETACHING = 1
|
||||
UE_STATUS_ATTACHING = 2
|
||||
UE_STATUS_ATTACHED = 3
|
||||
|
||||
X2_HO_REQ_STATE__IDLE = 0
|
||||
X2_HO_REQ_STATE__TARGET_RECEIVES_REQ = 1
|
||||
X2_HO_REQ_STATE__TARGET_RRC_RECFG_COMPLETE = 2
|
||||
X2_HO_REQ_STATE__TARGET_SENDS_SWITCH_REQ = 3
|
||||
X2_HO_REQ_STATE__SOURCE_RECEIVES_REQ_ACK = 10
|
||||
@@ -0,0 +1,499 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# Python for CI of OAI-eNB + COTS-UE
|
||||
#
|
||||
# Required Python Version
|
||||
# Python 3.x
|
||||
#
|
||||
# Required Python Package
|
||||
# pexpect
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Import
|
||||
#-----------------------------------------------------------
|
||||
import sys # arg
|
||||
import re # reg
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import signal
|
||||
|
||||
from multiprocessing import Process, Lock, SimpleQueue
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# OAI Testing modules
|
||||
#-----------------------------------------------------------
|
||||
import sshconnection as SSH
|
||||
import helpreadme as HELP
|
||||
import constants as CONST
|
||||
import html
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Class Declaration
|
||||
#-----------------------------------------------------------
|
||||
class EPCManagement():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.IPAddress = ''
|
||||
self.UserName = ''
|
||||
self.Password = ''
|
||||
self.SourceCodePath = ''
|
||||
self.Type = ''
|
||||
self.PcapFileName = ''
|
||||
self.htmlObj = None
|
||||
self.testCase_id = ''
|
||||
self.MmeIPAddress = ''
|
||||
self.containerPrefix = 'prod'
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Setter and Getters on Public Members
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def SetIPAddress(self, ipaddress):
|
||||
self.IPAddress = ipaddress
|
||||
def GetIPAddress(self):
|
||||
return self.IPAddress
|
||||
def SetUserName(self, username):
|
||||
self.UserName = username
|
||||
def GetUserName(self):
|
||||
return self.UserName
|
||||
def SetPassword(self, password):
|
||||
self.Password = password
|
||||
def GetPassword(self):
|
||||
return self.Password
|
||||
def SetSourceCodePath(self, sourcecodepath):
|
||||
self.SourceCodePath = sourcecodepath
|
||||
def GetSourceCodePath(self):
|
||||
return self.SourceCodePath
|
||||
def SetType(self, kind):
|
||||
self.Type = kind
|
||||
def GetType(self):
|
||||
return self.Type
|
||||
def SetHtmlObj(self, obj):
|
||||
self.htmlObj = obj
|
||||
def SetTestCase_id(self, idx):
|
||||
self.testCase_id = idx
|
||||
def GetMmeIPAddress(self):
|
||||
return self.MmeIPAddress
|
||||
def SetContainerPrefix(self, prefix):
|
||||
self.containerPrefix = prefix
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# EPC management functions
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def InitializeHSS(self):
|
||||
if self.IPAddress == '' or self.UserName == '' or self.Password == '' or self.SourceCodePath == '' or self.Type == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
HELP.EPCSrvHelp(self.IPAddress, self.UserName, self.Password, self.SourceCodePath, self.Type)
|
||||
sys.exit('Insufficient EPC Parameters')
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 Cassandra-based HSS in Docker')
|
||||
mySSH.command('if [ -d ' + self.SourceCodePath + '/scripts ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/scripts ; fi', '\$', 5)
|
||||
mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-hss /bin/bash -c "nohup tshark -i eth0 -i eth1 -w /tmp/hss_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
|
||||
time.sleep(5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-hss /bin/bash -c "nohup ./bin/oai_hss -j ./etc/hss_rel14.json --reloadkey true > hss_check_run.log 2>&1"', '\$', 5)
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 Cassandra-based HSS')
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
logging.debug('\u001B[1m Launching tshark on all interfaces \u001B[0m')
|
||||
self.PcapFileName = 'epc_' + self.testCase_id + '.pcap'
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f ' + self.PcapFileName, '\$', 5)
|
||||
mySSH.command('echo $USER; nohup sudo tshark -f "tcp port not 22 and port not 53" -i any -w ' + self.SourceCodePath + '/scripts/' + self.PcapFileName + ' > /tmp/tshark.log 2>&1 &', self.UserName, 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S mkdir -p logs', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f hss_' + self.testCase_id + '.log logs/hss*.*', '\$', 5)
|
||||
mySSH.command('echo "oai_hss -j /usr/local/etc/oai/hss_rel14.json" > ./my-hss.sh', '\$', 5)
|
||||
mySSH.command('chmod 755 ./my-hss.sh', '\$', 5)
|
||||
mySSH.command('sudo daemon --unsafe --name=hss_daemon --chdir=' + self.SourceCodePath + '/scripts -o ' + self.SourceCodePath + '/scripts/hss_' + self.testCase_id + '.log ./my-hss.sh', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC HSS')
|
||||
mySSH.command('cd ' + self.SourceCodePath, '\$', 5)
|
||||
mySSH.command('source oaienv', '\$', 5)
|
||||
mySSH.command('cd scripts', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./run_hss 2>&1 | stdbuf -o0 awk \'{ print strftime("[%Y/%m/%d %H:%M:%S] ",systime()) $0 }\' | stdbuf -o0 tee -a hss_' + self.testCase_id + '.log &', 'Core state: 2 -> 3', 35)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the ltebox simulated HSS')
|
||||
mySSH.command('if [ -d ' + self.SourceCodePath + '/scripts ]; then echo ' + self.Password + ' | sudo -S rm -Rf ' + self.SourceCodePath + '/scripts ; fi', '\$', 5)
|
||||
mySSH.command('mkdir -p ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('cd /opt/hss_sim0609', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f hss.log', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S echo "Starting sudo session" && sudo su -c "screen -dm -S simulated_hss ./starthss"', '\$', 5)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def InitializeMME(self):
|
||||
if self.IPAddress == '' or self.UserName == '' or self.Password == '' or self.SourceCodePath == '' or self.Type == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
HELP.EPCSrvHelp(self.IPAddress, self.UserName, self.Password, self.SourceCodePath, self.Type)
|
||||
sys.exit('Insufficient EPC Parameters')
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 MME in Docker')
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-mme /bin/bash -c "nohup tshark -i eth0 -i lo:s10 -w /tmp/mme_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
|
||||
time.sleep(5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-mme /bin/bash -c "nohup ./bin/oai_mme -c ./etc/mme.conf > mme_check_run.log 2>&1"', '\$', 5)
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 MME')
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f mme_' + self.testCase_id + '.log', '\$', 5)
|
||||
mySSH.command('echo "./run_mme --config-file /usr/local/etc/oai/mme.conf --set-virt-if" > ./my-mme.sh', '\$', 5)
|
||||
mySSH.command('chmod 755 ./my-mme.sh', '\$', 5)
|
||||
mySSH.command('sudo daemon --unsafe --name=mme_daemon --chdir=' + self.SourceCodePath + '/scripts -o ' + self.SourceCodePath + '/scripts/mme_' + self.testCase_id + '.log ./my-mme.sh', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd ' + self.SourceCodePath, '\$', 5)
|
||||
mySSH.command('source oaienv', '\$', 5)
|
||||
mySSH.command('cd scripts', '\$', 5)
|
||||
mySSH.command('stdbuf -o0 hostname', '\$', 5)
|
||||
result = re.search('hostname\\\\r\\\\n(?P<host_name>[a-zA-Z0-9\-\_]+)\\\\r\\\\n', mySSH.getBefore())
|
||||
if result is None:
|
||||
logging.debug('\u001B[1;37;41m Hostname Not Found! \u001B[0m')
|
||||
sys.exit(1)
|
||||
host_name = result.group('host_name')
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./run_mme 2>&1 | stdbuf -o0 tee -a mme_' + self.testCase_id + '.log &', 'MME app initialization complete', 100)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./start_mme', '\$', 5)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def SetMmeIPAddress(self):
|
||||
# Not an error if we don't need an EPC
|
||||
if self.IPAddress == '' or self.UserName == '' or self.Password == '' or self.SourceCodePath == '' or self.Type == '':
|
||||
return
|
||||
if self.IPAddress == 'none':
|
||||
return
|
||||
# Only in case of Docker containers, MME IP address is not the EPC HOST IP address
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
mySSH.command('docker inspect --format="MME_IP_ADDR = {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" ' + self.containerPrefix + '-oai-mme', '\$', 5)
|
||||
result = re.search('MME_IP_ADDR = (?P<mme_ip_addr>[0-9\.]+)', mySSH.getBefore())
|
||||
if result is not None:
|
||||
self.MmeIPAddress = result.group('mme_ip_addr')
|
||||
logging.debug('MME IP Address is ' + self.MmeIPAddress)
|
||||
mySSH.close()
|
||||
else:
|
||||
self.MmeIPAddress = self.IPAddress
|
||||
|
||||
def InitializeSPGW(self):
|
||||
if self.IPAddress == '' or self.UserName == '' or self.Password == '' or self.SourceCodePath == '' or self.Type == '':
|
||||
HELP.GenericHelp(CONST.Version)
|
||||
HELP.EPCSrvHelp(self.IPAddress, self.UserName, self.Password, self.SourceCodePath, self.Type)
|
||||
sys.exit('Insufficient EPC Parameters')
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 SPGW-CUPS in Docker')
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "nohup tshark -i eth0 -i lo:p5c -i lo:s5c -w /tmp/spgwc_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "nohup tshark -i eth0 -w /tmp/spgwu_check_run.pcap 2>&1 > /dev/null"', '\$', 5)
|
||||
time.sleep(5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "nohup ./bin/oai_spgwc -o -c ./etc/spgw_c.conf > spgwc_check_run.log 2>&1"', '\$', 5)
|
||||
time.sleep(5)
|
||||
mySSH.command('docker exec -d ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "nohup ./bin/oai_spgwu -o -c ./etc/spgw_u.conf > spgwu_check_run.log 2>&1"', '\$', 5)
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
logging.debug('Using the OAI EPC Release 14 SPGW-CUPS')
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f spgwc_' + self.testCase_id + '.log spgwu_' + self.testCase_id + '.log', '\$', 5)
|
||||
mySSH.command('echo "spgwc -c /usr/local/etc/oai/spgw_c.conf" > ./my-spgwc.sh', '\$', 5)
|
||||
mySSH.command('chmod 755 ./my-spgwc.sh', '\$', 5)
|
||||
mySSH.command('sudo daemon --unsafe --name=spgwc_daemon --chdir=' + self.SourceCodePath + '/scripts -o ' + self.SourceCodePath + '/scripts/spgwc_' + self.testCase_id + '.log ./my-spgwc.sh', '\$', 5)
|
||||
time.sleep(5)
|
||||
mySSH.command('echo "spgwu -c /usr/local/etc/oai/spgw_u.conf" > ./my-spgwu.sh', '\$', 5)
|
||||
mySSH.command('chmod 755 ./my-spgwu.sh', '\$', 5)
|
||||
mySSH.command('sudo daemon --unsafe --name=spgwu_daemon --chdir=' + self.SourceCodePath + '/scripts -o ' + self.SourceCodePath + '/scripts/spgwu_' + self.testCase_id + '.log ./my-spgwu.sh', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd ' + self.SourceCodePath, '\$', 5)
|
||||
mySSH.command('source oaienv', '\$', 5)
|
||||
mySSH.command('cd scripts', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./run_spgw 2>&1 | stdbuf -o0 tee -a spgw_' + self.testCase_id + '.log &', 'Initializing SPGW-APP task interface: DONE', 30)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./start_xGw', '\$', 5)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow(self.Type, 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def CheckHSSProcess(self, status_queue):
|
||||
try:
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-hss /bin/bash -c "ps aux | grep oai_hss"', '\$', 5)
|
||||
else:
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never hss | grep -v grep', '\$', 5)
|
||||
if re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
result = re.search('oai_hss -j', mySSH.getBefore())
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
result = re.search('hss_sim s6as diam_hss', mySSH.getBefore())
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
if result is None:
|
||||
logging.debug('\u001B[1;37;41m HSS Process Not Found! \u001B[0m')
|
||||
status_queue.put(CONST.HSS_PROCESS_FAILED)
|
||||
else:
|
||||
status_queue.put(CONST.HSS_PROCESS_OK)
|
||||
mySSH.close()
|
||||
except:
|
||||
os.kill(os.getppid(),signal.SIGUSR1)
|
||||
|
||||
def CheckMMEProcess(self, status_queue):
|
||||
try:
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-mme /bin/bash -c "ps aux | grep oai_mme"', '\$', 5)
|
||||
else:
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never mme | grep -v grep', '\$', 5)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
result = re.search('oai_mme -c ', mySSH.getBefore())
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
result = re.search('mme -c', mySSH.getBefore())
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
result = re.search('mme', mySSH.getBefore())
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
if result is None:
|
||||
logging.debug('\u001B[1;37;41m MME Process Not Found! \u001B[0m')
|
||||
status_queue.put(CONST.MME_PROCESS_FAILED)
|
||||
else:
|
||||
status_queue.put(CONST.MME_PROCESS_OK)
|
||||
mySSH.close()
|
||||
except:
|
||||
os.kill(os.getppid(),signal.SIGUSR1)
|
||||
|
||||
def CheckSPGWProcess(self, status_queue):
|
||||
try:
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "ps aux | grep oai_spgwc"', '\$', 5)
|
||||
result = re.search('oai_spgwc -o -c ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "ps aux | grep oai_spgwu"', '\$', 5)
|
||||
result = re.search('oai_spgwu -o -c ', mySSH.getBefore())
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never spgw | grep -v grep', '\$', 5)
|
||||
result = re.search('spgwu -c ', mySSH.getBefore())
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never spgw | grep -v grep', '\$', 5)
|
||||
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep --color=never xGw | grep -v grep', '\$', 5)
|
||||
result = re.search('xGw', mySSH.getBefore())
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
if result is None:
|
||||
logging.debug('\u001B[1;37;41m SPGW Process Not Found! \u001B[0m')
|
||||
status_queue.put(CONST.SPGW_PROCESS_FAILED)
|
||||
else:
|
||||
status_queue.put(CONST.SPGW_PROCESS_OK)
|
||||
mySSH.close()
|
||||
except:
|
||||
os.kill(os.getppid(),signal.SIGUSR1)
|
||||
|
||||
def TerminateHSS(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-hss /bin/bash -c "killall --signal SIGINT oai_hss tshark"', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-hss /bin/bash -c "ps aux | grep oai_hss"', '\$', 5)
|
||||
result = re.search('oai_hss -j ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-hss /bin/bash -c "killall --signal SIGKILL oai_hss"', '\$', 5)
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT oai_hss || true', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep hss | grep -v grep', '\$', 5)
|
||||
result = re.search('oai_hss -j', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL oai_hss || true', '\$', 5)
|
||||
mySSH.command('rm -f ' + self.SourceCodePath + '/scripts/my-hss.sh', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT run_hss oai_hss || true', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep hss | grep -v grep', '\$', 5)
|
||||
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL run_hss oai_hss || true', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd ' + self.SourceCodePath, '\$', 5)
|
||||
mySSH.command('cd scripts', '\$', 5)
|
||||
time.sleep(1)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL hss_sim', '\$', 5)
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def TerminateMME(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-mme /bin/bash -c "killall --signal SIGINT oai_mme tshark"', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-mme /bin/bash -c "ps aux | grep oai_mme"', '\$', 5)
|
||||
result = re.search('oai_mme -c ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-mme /bin/bash -c "killall --signal SIGKILL oai_mme"', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT run_mme mme || true', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep mme | grep -v grep', '\$', 5)
|
||||
result = re.search('mme -c', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL run_mme mme || true', '\$', 5)
|
||||
mySSH.command('rm -f ' + self.SourceCodePath + '/scripts/my-mme.sh', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./stop_mme', '\$', 5)
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def TerminateSPGW(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "killall --signal SIGINT oai_spgwc tshark"', '\$', 5)
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "killall --signal SIGINT oai_spgwu tshark"', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "ps aux | grep oai_spgwc"', '\$', 5)
|
||||
result = re.search('oai_spgwc -o -c ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwc /bin/bash -c "killall --signal SIGKILL oai_spgwc"', '\$', 5)
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "ps aux | grep oai_spgwu"', '\$', 5)
|
||||
result = re.search('oai_spgwu -o -c ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('docker exec -it ' + self.containerPrefix + '-oai-spgwu-tiny /bin/bash -c "killall --signal SIGKILL oai_spgwu"', '\$', 5)
|
||||
elif re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT spgwc spgwu || true', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep spgw | grep -v grep', '\$', 5)
|
||||
result = re.search('spgwc -c |spgwu -c ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL spgwc spgwu || true', '\$', 5)
|
||||
mySSH.command('rm -f ' + self.SourceCodePath + '/scripts/my-spgw*.sh', '\$', 5)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep tshark | grep -v grep', '\$', 5)
|
||||
result = re.search('-w ', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT tshark || true', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S chmod 666 ' + self.SourceCodePath + '/scripts/*.pcap', '\$', 5)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE):
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGINT run_spgw spgw || true', '\$', 5)
|
||||
time.sleep(2)
|
||||
mySSH.command('stdbuf -o0 ps -aux | grep spgw | grep -v grep', '\$', 5)
|
||||
result = re.search('\/bin\/bash .\/run_', mySSH.getBefore())
|
||||
if result is not None:
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S killall --signal SIGKILL run_spgw spgw || true', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cd /opt/ltebox/tools', '\$', 5)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S ./stop_xGw', '\$', 5)
|
||||
else:
|
||||
logging.error('This should not happen!')
|
||||
mySSH.close()
|
||||
if self.htmlObj is not None:
|
||||
self.htmlObj.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK)
|
||||
|
||||
def LogCollectHSS(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('rm -f hss.log.zip', '\$', 5)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-hss:/openair-hss/hss_check_run.log .', '\$', 60)
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-hss:/tmp/hss_check_run.pcap .', '\$', 60)
|
||||
mySSH.command('zip hss.log.zip hss_check_run.*', '\$', 60)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('zip hss.log.zip hss*.log', '\$', 60)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm hss*.log', '\$', 5)
|
||||
if re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('zip hss.log.zip logs/hss*.* *.pcap', '\$', 60)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm -f logs/hss*.* *.pcap', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cp /opt/hss_sim0609/hss.log .', '\$', 60)
|
||||
mySSH.command('zip hss.log.zip hss.log', '\$', 60)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
|
||||
def LogCollectMME(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('rm -f mme.log.zip', '\$', 5)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-mme:/openair-mme/mme_check_run.log .', '\$', 60)
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-mme:/tmp/mme_check_run.pcap .', '\$', 60)
|
||||
mySSH.command('zip mme.log.zip mme_check_run.*', '\$', 60)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('zip mme.log.zip mme*.log', '\$', 60)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm mme*.log', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cp /opt/ltebox/var/log/*Log.0 .', '\$', 5)
|
||||
mySSH.command('zip mme.log.zip mmeLog.0 s1apcLog.0 s1apsLog.0 s11cLog.0 libLog.0 s1apCodecLog.0', '\$', 60)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
|
||||
def LogCollectSPGW(self):
|
||||
mySSH = SSH.SSHConnection()
|
||||
mySSH.open(self.IPAddress, self.UserName, self.Password)
|
||||
mySSH.command('cd ' + self.SourceCodePath + '/scripts', '\$', 5)
|
||||
mySSH.command('rm -f spgw.log.zip', '\$', 5)
|
||||
if re.match('OAI-Rel14-Docker', self.Type, re.IGNORECASE):
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwc:/openair-spgwc/spgwc_check_run.log .', '\$', 60)
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwu-tiny:/openair-spgwu-tiny/spgwu_check_run.log .', '\$', 60)
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwc:/tmp/spgwc_check_run.pcap .', '\$', 60)
|
||||
mySSH.command('docker cp ' + self.containerPrefix + '-oai-spgwu-tiny:/tmp/spgwu_check_run.pcap .', '\$', 60)
|
||||
mySSH.command('zip spgw.log.zip spgw*_check_run.*', '\$', 60)
|
||||
elif re.match('OAI', self.Type, re.IGNORECASE) or re.match('OAI-Rel14-CUPS', self.Type, re.IGNORECASE):
|
||||
mySSH.command('zip spgw.log.zip spgw*.log', '\$', 60)
|
||||
mySSH.command('echo ' + self.Password + ' | sudo -S rm spgw*.log', '\$', 5)
|
||||
elif re.match('ltebox', self.Type, re.IGNORECASE):
|
||||
mySSH.command('cp /opt/ltebox/var/log/xGwLog.0 .', '\$', 5)
|
||||
mySSH.command('zip spgw.log.zip xGwLog.0', '\$', 60)
|
||||
else:
|
||||
logging.error('This option should not occur!')
|
||||
mySSH.close()
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# Python for CI of OAI-eNB + COTS-UE
|
||||
#
|
||||
# Required Python Version
|
||||
# Python 3.x
|
||||
#
|
||||
# Required Python Package
|
||||
# pexpect
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Functions Declaration
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def GenericHelp(vers):
|
||||
print('----------------------------------------------------------------------------------------------------------------------')
|
||||
print('main.py Ver: ' + vers)
|
||||
print('----------------------------------------------------------------------------------------------------------------------')
|
||||
print('python main.py [options]')
|
||||
print(' --help Show this help.')
|
||||
print(' --mode=[Mode]')
|
||||
print(' TesteNB')
|
||||
print(' InitiateHtml, FinalizeHtml')
|
||||
print(' TerminateeNB, TerminateUE, TerminateHSS, TerminateMME, TerminateSPGW')
|
||||
print(' LogCollectBuild, LogCollecteNB, LogCollectHSS, LogCollectMME, LogCollectSPGW, LogCollectPing, LogCollectIperf')
|
||||
|
||||
def GitSrvHelp(repository,branch,commit,mergeallow,targetbranch):
|
||||
print(' --ranRepository=[OAI RAN Repository URL] -- ' + repository)
|
||||
print(' --ranBranch=[OAI RAN Repository Branch] -- ' + branch)
|
||||
print(' --ranCommitID=[OAI RAN Repository Commit SHA-1] -- ' + commit)
|
||||
print(' --ranAllowMerge=[Allow Merge Request (with target branch) (true or false)] -- ' + mergeallow)
|
||||
print(' --ranTargetBranch=[Target Branch in case of a Merge Request] -- ' + targetbranch)
|
||||
|
||||
def eNBSrvHelp(ipaddr, username, password, sourcepath):
|
||||
print(' --eNBIPAddress=[eNB\'s IP Address] -- ' + ipaddr)
|
||||
print(' --eNBUserName=[eNB\'s Login User Name] -- ' + username)
|
||||
print(' --eNBPassword=[eNB\'s Login Password] -- ' + password)
|
||||
print(' --eNBSourceCodePath=[eNB\'s Source Code Path] -- ' + sourcepath)
|
||||
|
||||
def OAIUESrvHelp(ipaddr, username, password, sourcepath):
|
||||
print(' --UEIPAddress=[UE\'s IP Address] -- ' + ipaddr)
|
||||
print(' --UEUserName=[UE\'s Login User Name] -- ' + username)
|
||||
print(' --UEPassword=[UE\'s Login Password] -- ' + password)
|
||||
print(' --UESourceCodePath=[UE\'s Source Code Path] -- ' + sourcepath)
|
||||
|
||||
def EPCSrvHelp(ipaddr, username, password, sourcepath, epctype):
|
||||
print(' --EPCIPAddress=[EPC\'s IP Address] -- ' + ipaddr)
|
||||
print(' --EPCUserName=[EPC\'s Login User Name] -- ' + username)
|
||||
print(' --EPCPassword=[EPC\'s Login Password] -- ' + password)
|
||||
print(' --EPCSourceCodePath=[EPC\'s Source Code Path] -- ' + sourcepath)
|
||||
print(' --EPCType=[EPC\'s Type: OAI or ltebox or OAI-Rel14-CUPS] -- ' + epctype)
|
||||
|
||||
def ADBSrvHelp(ipaddr, username, password):
|
||||
print(' --ADBIPAddress=[ADB\'s IP Address] -- ' + ipaddr)
|
||||
print(' --ADBUserName=[ADB\'s Login User Name] -- ' + username)
|
||||
print(' --ADBPassword=[ADB\'s Login Password] -- ' + password)
|
||||
|
||||
def XmlHelp(filename):
|
||||
print(' --XMLTestFile=[XML Test File to be run] -- ' + filename)
|
||||
print(' Note: multiple xml files can be specified (--XMLFile=File1 ... --XMLTestFile=FileN) when HTML headers are created ("InitiateHtml" mode)')
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# Python for CI of OAI-eNB + COTS-UE
|
||||
#
|
||||
# Required Python Version
|
||||
# Python 3.x
|
||||
#
|
||||
# Required Python Package
|
||||
# pexpect
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Import
|
||||
#-----------------------------------------------------------
|
||||
import sys # arg
|
||||
import re # reg
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
from multiprocessing import Process, Lock, SimpleQueue
|
||||
|
||||
import constants as CONST
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Class Declaration
|
||||
#-----------------------------------------------------------
|
||||
class HTMLManagement():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.htmlFile = ''
|
||||
self.htmlHeaderCreated = False
|
||||
self.htmlFooterCreated = False
|
||||
|
||||
self.ranRepository = ''
|
||||
self.ranBranch = ''
|
||||
self.ranCommitID = ''
|
||||
self.ranAllowMerge = False
|
||||
self.ranTargetBranch = ''
|
||||
|
||||
self.nbTestXMLfiles = 0
|
||||
self.htmlTabRefs = []
|
||||
self.htmlTabNames = []
|
||||
self.htmlTabIcons = []
|
||||
self.testXMLfiles = []
|
||||
|
||||
self.htmleNBFailureMsg = ''
|
||||
self.htmlUEFailureMsg = ''
|
||||
|
||||
self.startTime = int(round(time.time() * 1000))
|
||||
self.testCase_id = ''
|
||||
self.desc = ''
|
||||
|
||||
self.OsVersion = ['', '']
|
||||
self.KernelVersion = ['', '']
|
||||
self.UhdVersion = ['', '']
|
||||
self.UsrpBoard = ['', '']
|
||||
self.CpuNb = ['', '']
|
||||
self.CpuModel = ['', '']
|
||||
self.CpuMHz = ['', '']
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Setters and Getters
|
||||
#-----------------------------------------------------------
|
||||
def SethtmlUEFailureMsg(self,huefa):
|
||||
self.htmlUEFailureMsg = huefa
|
||||
def GethtmlUEFailureMsg(self):
|
||||
return self.htmlUEFailureMsg
|
||||
def SetHmleNBFailureMsg(self, msg):
|
||||
self.htmleNBFailureMsg = msg
|
||||
|
||||
def Setdesc(self, dsc):
|
||||
self.desc = dsc
|
||||
|
||||
def SetstartTime(self, sttime):
|
||||
self.startTime = sttime
|
||||
|
||||
def SettestCase_id(self, tcid):
|
||||
self.testCase_id = tcid
|
||||
def GettestCase_id(self):
|
||||
return self.testCase_id
|
||||
|
||||
def SetranRepository(self, repository):
|
||||
self.ranRepository = repository
|
||||
def SetranAllowMerge(self, merge):
|
||||
self.ranAllowMerge = merge
|
||||
def SetranBranch(self, branch):
|
||||
self.ranBranch = branch
|
||||
def SetranCommitID(self, commitid):
|
||||
self.ranCommitID = commitid
|
||||
def SetranTargetBranch(self, tbranch):
|
||||
self.ranTargetBranch = tbranch
|
||||
|
||||
def SethtmlUEConnected(self, nbUEs):
|
||||
if nbUEs > 0:
|
||||
self.htmlUEConnected = nbUEs
|
||||
else:
|
||||
self.htmlUEConnected = 1
|
||||
def SethtmlNb_Smartphones(self, nbUEs):
|
||||
self.htmlNb_Smartphones = nbUEs
|
||||
def SethtmlNb_CATM_Modules(self, nbUEs):
|
||||
self.htmlNb_CATM_Modules = nbUEs
|
||||
|
||||
def SetnbTestXMLfiles(self, nb):
|
||||
self.nbTestXMLfiles = nb
|
||||
def GetnbTestXMLfiles(self):
|
||||
return self.nbTestXMLfiles
|
||||
|
||||
def SettestXMLfiles(self, xmlFile):
|
||||
self.testXMLfiles.append(xmlFile)
|
||||
def SethtmlTabRefs(self, tabRef):
|
||||
self.htmlTabRefs.append(tabRef)
|
||||
def SethtmlTabNames(self, tabName):
|
||||
self.htmlTabNames.append(tabName)
|
||||
def SethtmlTabIcons(self, tabIcon):
|
||||
self.htmlTabIcons.append(tabIcon)
|
||||
|
||||
def SetOsVersion(self, version, idx):
|
||||
self.OsVersion[idx] = version
|
||||
def SetKernelVersion(self, version, idx):
|
||||
self.KernelVersion[idx] = version
|
||||
def SetUhdVersion(self, version, idx):
|
||||
self.UhdVersion[idx] = version
|
||||
def SetUsrpBoard(self, version, idx):
|
||||
self.UsrpBoard[idx] = version
|
||||
def SetCpuNb(self, nb, idx):
|
||||
self.CpuNb[idx] = nb
|
||||
def SetCpuModel(self, model, idx):
|
||||
self.CpuModel[idx] = model
|
||||
def SetCpuMHz(self, freq, idx):
|
||||
self.CpuMHz[idx] = freq
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# HTML structure creation functions
|
||||
#-----------------------------------------------------------
|
||||
|
||||
|
||||
def CreateHtmlHeader(self, ADBIPAddress):
|
||||
if (not self.htmlHeaderCreated):
|
||||
logging.debug('\u001B[1m----------------------------------------\u001B[0m')
|
||||
logging.debug('\u001B[1m Creating HTML header \u001B[0m')
|
||||
logging.debug('\u001B[1m----------------------------------------\u001B[0m')
|
||||
self.htmlFile = open('test_results.html', 'w')
|
||||
self.htmlFile.write('<!DOCTYPE html>\n')
|
||||
self.htmlFile.write('<html class="no-js" lang="en-US">\n')
|
||||
self.htmlFile.write('<head>\n')
|
||||
self.htmlFile.write(' <meta name="viewport" content="width=device-width, initial-scale=1">\n')
|
||||
self.htmlFile.write(' <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">\n')
|
||||
self.htmlFile.write(' <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>\n')
|
||||
self.htmlFile.write(' <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>\n')
|
||||
self.htmlFile.write(' <title>Test Results for TEMPLATE_JOB_NAME job build #TEMPLATE_BUILD_ID</title>\n')
|
||||
self.htmlFile.write('</head>\n')
|
||||
self.htmlFile.write('<body><div class="container">\n')
|
||||
self.htmlFile.write(' <br>\n')
|
||||
self.htmlFile.write(' <table style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <tr style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <a href="http://www.openairinterface.org/">\n')
|
||||
self.htmlFile.write(' <img src="http://www.openairinterface.org/wp-content/uploads/2016/03/cropped-oai_final_logo2.png" alt="" border="none" height=50 width=150>\n')
|
||||
self.htmlFile.write(' </img>\n')
|
||||
self.htmlFile.write(' </a>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none; vertical-align: center;">\n')
|
||||
self.htmlFile.write(' <b><font size = "6">Job Summary -- Job: TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
self.htmlFile.write(' <br>\n')
|
||||
self.htmlFile.write(' <div class="alert alert-info"><strong> <span class="glyphicon glyphicon-dashboard"></span> TEMPLATE_STAGE_NAME</strong></div>\n')
|
||||
self.htmlFile.write(' <table border = "1">\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-time"></span> Build Start Time (UTC) </td>\n')
|
||||
self.htmlFile.write(' <td>TEMPLATE_BUILD_TIME</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-cloud-upload"></span> GIT Repository </td>\n')
|
||||
self.htmlFile.write(' <td><a href="' + self.ranRepository + '">' + self.ranRepository + '</a></td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-wrench"></span> Job Trigger </td>\n')
|
||||
if (self.ranAllowMerge):
|
||||
self.htmlFile.write(' <td>Merge-Request</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td>Push to Branch</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
if (self.ranAllowMerge):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-log-out"></span> Source Branch </td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tree-deciduous"></span> Branch</td>\n')
|
||||
self.htmlFile.write(' <td>' + self.ranBranch + '</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
if (self.ranAllowMerge):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tag"></span> Source Commit ID </td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-tag"></span> Commit ID </td>\n')
|
||||
self.htmlFile.write(' <td>' + self.ranCommitID + '</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
if self.ranAllowMerge != '':
|
||||
commit_message = subprocess.check_output("git log -n1 --pretty=format:\"%s\" " + self.ranCommitID, shell=True, universal_newlines=True)
|
||||
commit_message = commit_message.strip()
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
if (self.ranAllowMerge):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-comment"></span> Source Commit Message </td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-comment"></span> Commit Message </td>\n')
|
||||
self.htmlFile.write(' <td>' + commit_message + '</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
if (self.ranAllowMerge):
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" > <span class="glyphicon glyphicon-log-in"></span> Target Branch </td>\n')
|
||||
if (self.ranTargetBranch == ''):
|
||||
self.htmlFile.write(' <td>develop</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td>' + self.ranTargetBranch + '</td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
|
||||
if (ADBIPAddress != 'none'):
|
||||
self.htmlFile.write(' <h2><span class="glyphicon glyphicon-phone"></span> <span class="glyphicon glyphicon-menu-right"></span> ' + str(self.htmlNb_Smartphones) + ' UE(s) is(are) connected to ADB bench server</h2>\n')
|
||||
self.htmlFile.write(' <h2><span class="glyphicon glyphicon-phone"></span> <span class="glyphicon glyphicon-menu-right"></span> ' + str(self.htmlNb_CATM_Modules) + ' CAT-M UE(s) is(are) connected to bench server</h2>\n')
|
||||
else:
|
||||
self.htmlUEConnected = 1
|
||||
self.htmlFile.write(' <h2><span class="glyphicon glyphicon-phone"></span> <span class="glyphicon glyphicon-menu-right"></span> 1 OAI UE(s) is(are) connected to CI bench</h2>\n')
|
||||
self.htmlFile.write(' <br>\n')
|
||||
self.htmlFile.write(' <ul class="nav nav-pills">\n')
|
||||
count = 0
|
||||
while (count < self.nbTestXMLfiles):
|
||||
pillMsg = ' <li><a data-toggle="pill" href="#'
|
||||
pillMsg += self.htmlTabRefs[count]
|
||||
pillMsg += '">'
|
||||
pillMsg += '__STATE_' + self.htmlTabNames[count] + '__'
|
||||
pillMsg += self.htmlTabNames[count]
|
||||
pillMsg += ' <span class="glyphicon glyphicon-'
|
||||
pillMsg += self.htmlTabIcons[count]
|
||||
pillMsg += '"></span></a></li>\n'
|
||||
self.htmlFile.write(pillMsg)
|
||||
count += 1
|
||||
self.htmlFile.write(' </ul>\n')
|
||||
self.htmlFile.write(' <div class="tab-content">\n')
|
||||
self.htmlFile.close()
|
||||
|
||||
def CreateHtmlTabHeader(self):
|
||||
if (not self.htmlHeaderCreated):
|
||||
if (not os.path.isfile('test_results.html')):
|
||||
self.CreateHtmlHeader('none')
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
if (self.nbTestXMLfiles == 1):
|
||||
self.htmlFile.write(' <div id="' + self.htmlTabRefs[0] + '" class="tab-pane fade">\n')
|
||||
self.htmlFile.write(' <h3>Test Summary for <span class="glyphicon glyphicon-file"></span> ' + self.testXMLfiles[0] + '</h3>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <div id="build-tab" class="tab-pane fade">\n')
|
||||
self.htmlFile.write(' <table class="table" border = "1">\n')
|
||||
self.htmlFile.write(' <tr bgcolor = "#33CCFF" >\n')
|
||||
self.htmlFile.write(' <th>Relative Time (ms)</th>\n')
|
||||
self.htmlFile.write(' <th>Test Id</th>\n')
|
||||
self.htmlFile.write(' <th>Test Desc</th>\n')
|
||||
self.htmlFile.write(' <th>Test Options</th>\n')
|
||||
self.htmlFile.write(' <th>Test Status</th>\n')
|
||||
|
||||
i = 0
|
||||
while (i < self.htmlUEConnected):
|
||||
self.htmlFile.write(' <th>UE' + str(i) + ' Status</th>\n')
|
||||
i += 1
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.close()
|
||||
self.htmlHeaderCreated = True
|
||||
|
||||
def CreateHtmlTabFooter(self, passStatus):
|
||||
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <th bgcolor = "#33CCFF" colspan=3>Final Tab Status</th>\n')
|
||||
if passStatus:
|
||||
self.htmlFile.write(' <th bgcolor = "green" colspan=' + str(2 + self.htmlUEConnected) + '><font color="white">PASS <span class="glyphicon glyphicon-ok"></span> </font></th>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <th bgcolor = "red" colspan=' + str(2 + self.htmlUEConnected) + '><font color="white">FAIL <span class="glyphicon glyphicon-remove"></span> </font></th>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
self.htmlFile.write(' </div>\n')
|
||||
self.htmlFile.close()
|
||||
time.sleep(1)
|
||||
if passStatus:
|
||||
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + "__//' test_results.html"
|
||||
subprocess.run(cmd, shell=True)
|
||||
else:
|
||||
cmd = "sed -i -e 's/__STATE_" + self.htmlTabNames[0] + "__/<span class=\"glyphicon glyphicon-remove\"><\/span>/' test_results.html"
|
||||
subprocess.run(cmd, shell=True)
|
||||
self.htmlFooterCreated = False
|
||||
|
||||
def CreateHtmlFooter(self, passStatus):
|
||||
if (os.path.isfile('test_results.html')):
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
self.htmlFile.write('</div>\n')
|
||||
self.htmlFile.write(' <p></p>\n')
|
||||
self.htmlFile.write(' <table class="table table-condensed">\n')
|
||||
|
||||
machines = [ 'eNB', 'UE' ]
|
||||
for machine in machines:
|
||||
if machine == 'eNB':
|
||||
idx = 0
|
||||
else:
|
||||
idx = 1
|
||||
if self.OsVersion[idx] == '':
|
||||
continue
|
||||
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <th colspan=8>' + str('eNB') + ' Server Characteristics</th>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td>OS Version</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.OsVersion[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td>Kernel Version</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.KernelVersion[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td>UHD Version</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.UhdVersion[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td>USRP Board</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.UsrpBoard[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td>Nb CPUs</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.CpuNb[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td>CPU Model Name</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.CpuModel[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td>CPU Frequency</td>\n')
|
||||
self.htmlFile.write(' <td><span class="label label-default">' + self.CpuMHz[idx] + '</span></td>\n')
|
||||
self.htmlFile.write(' <td></td>\n')
|
||||
self.htmlFile.write(' <td></td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <th colspan=5 bgcolor = "#33CCFF">Final Status</th>\n')
|
||||
if passStatus:
|
||||
self.htmlFile.write(' <th colspan=3 bgcolor="green"><font color="white">PASS <span class="glyphicon glyphicon-ok"></span></font></th>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <th colspan=3 bgcolor="red"><font color="white">FAIL <span class="glyphicon glyphicon-remove"></span> </font></th>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
self.htmlFile.write(' <p></p>\n')
|
||||
self.htmlFile.write(' <div class="well well-lg">End of Test Report -- Copyright <span class="glyphicon glyphicon-copyright-mark"></span> 2018 <a href="http://www.openairinterface.org/">OpenAirInterface</a>. All Rights Reserved.</div>\n')
|
||||
self.htmlFile.write('</div></body>\n')
|
||||
self.htmlFile.write('</html>\n')
|
||||
self.htmlFile.close()
|
||||
|
||||
def CreateHtmlRetrySeparator(self, cntnumfails):
|
||||
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
self.htmlFile.write(' <tr bgcolor = "#F0F0F0" >\n')
|
||||
self.htmlFile.write(' <td colspan=' + str(5+self.htmlUEConnected) + '><b> ---- Try Run #' + str(cntnumfails) + ' ---- </b></td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.close()
|
||||
|
||||
def CreateHtmlTestRow(self, options, status, processesStatus, machine='eNB'):
|
||||
if (self.htmlFooterCreated or (not self.htmlHeaderCreated)):
|
||||
return
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
currentTime = int(round(time.time() * 1000)) - self.startTime
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + format(currentTime / 1000, '.1f') + '</td>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + self.testCase_id + '</td>\n')
|
||||
self.htmlFile.write(' <td>' + self.desc + '</td>\n')
|
||||
self.htmlFile.write(' <td>' + str(options) + '</td>\n')
|
||||
if (str(status) == 'OK'):
|
||||
self.htmlFile.write(' <td bgcolor = "lightgreen" >' + str(status) + '</td>\n')
|
||||
elif (str(status) == 'KO'):
|
||||
if (processesStatus == 0):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >' + str(status) + '</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_FAILED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - eNB process not found</td>\n')
|
||||
elif (processesStatus == CONST.OAI_UE_PROCESS_FAILED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - OAI UE process not found</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_SEG_FAULT) or (processesStatus == CONST.OAI_UE_PROCESS_SEG_FAULT):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - ' + machine + ' process ended in Segmentation Fault</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_ASSERTION) or (processesStatus == CONST.OAI_UE_PROCESS_ASSERTION):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - ' + machine + ' process ended in Assertion</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_REALTIME_ISSUE):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - ' + machine + ' process faced Real Time issue(s)</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_NOLOGFILE_TO_ANALYZE) or (processesStatus == CONST.OAI_UE_PROCESS_NOLOGFILE_TO_ANALYZE):
|
||||
self.htmlFile.write(' <td bgcolor = "orange" >OK?</td>\n')
|
||||
elif (processesStatus == CONST.ENB_PROCESS_SLAVE_RRU_NOT_SYNCED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - ' + machine + ' Slave RRU could not synch</td>\n')
|
||||
elif (processesStatus == CONST.OAI_UE_PROCESS_COULD_NOT_SYNC):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - UE could not sync</td>\n')
|
||||
elif (processesStatus == CONST.HSS_PROCESS_FAILED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - HSS process not found</td>\n')
|
||||
elif (processesStatus == CONST.MME_PROCESS_FAILED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - MME process not found</td>\n')
|
||||
elif (processesStatus == CONST.SPGW_PROCESS_FAILED):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - SPGW process not found</td>\n')
|
||||
elif (processesStatus == CONST.UE_IP_ADDRESS_ISSUE):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >KO - Could not retrieve UE IP address</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >' + str(status) + '</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td bgcolor = "orange" >' + str(status) + '</td>\n')
|
||||
if (len(str(self.htmleNBFailureMsg)) > 2):
|
||||
cellBgColor = 'white'
|
||||
result = re.search('ended with|faced real time issues', self.htmleNBFailureMsg)
|
||||
if result is not None:
|
||||
cellBgColor = 'red'
|
||||
else:
|
||||
result = re.search('showed|Reestablishment|Could not copy eNB logfile', self.htmleNBFailureMsg)
|
||||
if result is not None:
|
||||
cellBgColor = 'orange'
|
||||
self.htmlFile.write(' <td bgcolor = "' + cellBgColor + '" colspan=' + str(self.htmlUEConnected) + '><pre style="background-color:' + cellBgColor + '">' + self.htmleNBFailureMsg + '</pre></td>\n')
|
||||
self.htmleNBFailureMsg = ''
|
||||
elif (len(str(self.htmlUEFailureMsg)) > 2):
|
||||
cellBgColor = 'white'
|
||||
result = re.search('ended with|faced real time issues', self.htmlUEFailureMsg)
|
||||
if result is not None:
|
||||
cellBgColor = 'red'
|
||||
else:
|
||||
result = re.search('showed|Could not copy UE logfile|oaitun_ue1 interface is either NOT mounted or NOT configured', self.htmlUEFailureMsg)
|
||||
if result is not None:
|
||||
cellBgColor = 'orange'
|
||||
self.htmlFile.write(' <td bgcolor = "' + cellBgColor + '" colspan=' + str(self.htmlUEConnected) + '><pre style="background-color:' + cellBgColor + '">' + self.htmlUEFailureMsg + '</pre></td>\n')
|
||||
self.htmlUEFailureMsg = ''
|
||||
else:
|
||||
i = 0
|
||||
while (i < self.htmlUEConnected):
|
||||
self.htmlFile.write(' <td>-</td>\n')
|
||||
i += 1
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.close()
|
||||
|
||||
def CreateHtmlTestRowQueue(self, options, status, ue_status, ue_queue):
|
||||
if ((not self.htmlFooterCreated) and (self.htmlHeaderCreated)):
|
||||
self.htmlFile = open('test_results.html', 'a')
|
||||
currentTime = int(round(time.time() * 1000)) - self.startTime
|
||||
addOrangeBK = False
|
||||
self.htmlFile.write(' <tr>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + format(currentTime / 1000, '.1f') + '</td>\n')
|
||||
self.htmlFile.write(' <td bgcolor = "lightcyan" >' + self.testCase_id + '</td>\n')
|
||||
self.htmlFile.write(' <td>' + self.desc + '</td>\n')
|
||||
self.htmlFile.write(' <td>' + str(options) + '</td>\n')
|
||||
if (str(status) == 'OK'):
|
||||
self.htmlFile.write(' <td bgcolor = "lightgreen" >' + str(status) + '</td>\n')
|
||||
elif (str(status) == 'KO'):
|
||||
self.htmlFile.write(' <td bgcolor = "lightcoral" >' + str(status) + '</td>\n')
|
||||
else:
|
||||
addOrangeBK = True
|
||||
self.htmlFile.write(' <td bgcolor = "orange" >' + str(status) + '</td>\n')
|
||||
i = 0
|
||||
while (i < self.htmlUEConnected):
|
||||
if (i < ue_status):
|
||||
if (not ue_queue.empty()):
|
||||
if (addOrangeBK):
|
||||
self.htmlFile.write(' <td bgcolor = "orange" >' + str(ue_queue.get()).replace('white', 'orange') + '</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td>' + str(ue_queue.get()) + '</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td>-</td>\n')
|
||||
else:
|
||||
self.htmlFile.write(' <td>-</td>\n')
|
||||
i += 1
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.close()
|
||||
|
||||
+1059
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,221 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# Python for CI of OAI-eNB + COTS-UE
|
||||
#
|
||||
# Required Python Version
|
||||
# Python 3.x
|
||||
#
|
||||
# Required Python Package
|
||||
# pexpect
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Import
|
||||
#-----------------------------------------------------------
|
||||
import pexpect # pexpect
|
||||
import logging
|
||||
import time # sleep
|
||||
import re
|
||||
import sys
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# Class Declaration
|
||||
#-----------------------------------------------------------
|
||||
class SSHConnection():
|
||||
def __init__(self):
|
||||
self.ssh = ''
|
||||
self.picocom_closure = False
|
||||
|
||||
def disablePicocomClosure(self):
|
||||
self.picocom_closure = False
|
||||
|
||||
def enablePicocomClosure(self):
|
||||
self.picocom_closure = True
|
||||
|
||||
def open(self, ipaddress, username, password):
|
||||
extraSshOptions = ''
|
||||
count = 0
|
||||
connect_status = False
|
||||
if ipaddress == '192.168.18.197':
|
||||
extraSshOptions = ' -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
|
||||
while count < 4:
|
||||
self.ssh = pexpect.spawn('ssh', [username + '@' + ipaddress + extraSshOptions], timeout = 5)
|
||||
self.sshresponse = self.ssh.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', 'Last login', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if self.sshresponse == 0:
|
||||
self.ssh.sendline('yes')
|
||||
self.sshresponse = self.ssh.expect(['password:', username + '@'])
|
||||
if self.sshresponse == 0:
|
||||
self.ssh.sendline(password)
|
||||
self.sshresponse = self.ssh.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if self.sshresponse == 0:
|
||||
count = 10
|
||||
connect_status = True
|
||||
else:
|
||||
logging.debug('self.sshresponse = ' + str(self.sshresponse))
|
||||
elif self.sshresponse == 1:
|
||||
self.ssh.sendline(password)
|
||||
self.sshresponse = self.ssh.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if self.sshresponse == 0:
|
||||
count = 10
|
||||
connect_status = True
|
||||
else:
|
||||
logging.debug('self.sshresponse = ' + str(self.sshresponse))
|
||||
elif self.sshresponse == 2:
|
||||
# Checking if we are really on the remote client defined by its IP address
|
||||
self.command('stdbuf -o0 ifconfig | egrep --color=never "inet addr:|inet "', '\$', 5)
|
||||
result = re.search(str(ipaddress), str(self.ssh.before))
|
||||
if result is None:
|
||||
self.close()
|
||||
else:
|
||||
count = 10
|
||||
connect_status = True
|
||||
else:
|
||||
# debug output
|
||||
logging.debug(str(self.ssh.before))
|
||||
logging.debug('self.sshresponse = ' + str(self.sshresponse))
|
||||
# adding a tempo when failure
|
||||
if not connect_status:
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
if connect_status:
|
||||
pass
|
||||
else:
|
||||
sys.exit('SSH Connection Failed')
|
||||
|
||||
def command(self, commandline, expectedline, timeout):
|
||||
logging.debug(commandline)
|
||||
self.ssh.timeout = timeout
|
||||
self.ssh.sendline(commandline)
|
||||
self.sshresponse = self.ssh.expect([expectedline, pexpect.EOF, pexpect.TIMEOUT])
|
||||
if self.sshresponse == 0:
|
||||
return 0
|
||||
elif self.sshresponse == 1:
|
||||
logging.debug('\u001B[1;37;41m Unexpected EOF \u001B[0m')
|
||||
logging.debug('Expected Line : ' + expectedline)
|
||||
logging.debug(str(self.ssh.before))
|
||||
sys.exit(self.sshresponse)
|
||||
elif self.sshresponse == 2:
|
||||
logging.debug('\u001B[1;37;41m Unexpected TIMEOUT \u001B[0m')
|
||||
logging.debug('Expected Line : ' + expectedline)
|
||||
result = re.search('ping |iperf |picocom', str(commandline))
|
||||
if result is None:
|
||||
logging.debug(str(self.ssh.before))
|
||||
sys.exit(self.sshresponse)
|
||||
else:
|
||||
return -1
|
||||
else:
|
||||
logging.debug('\u001B[1;37;41m Unexpected Others \u001B[0m')
|
||||
logging.debug('Expected Line : ' + expectedline)
|
||||
sys.exit(self.sshresponse)
|
||||
|
||||
def close(self):
|
||||
self.ssh.timeout = 5
|
||||
self.ssh.sendline('exit')
|
||||
self.sshresponse = self.ssh.expect([pexpect.EOF, pexpect.TIMEOUT])
|
||||
if self.sshresponse == 0:
|
||||
pass
|
||||
elif self.sshresponse == 1:
|
||||
if not self.picocom_closure:
|
||||
logging.debug('\u001B[1;37;41m Unexpected TIMEOUT during closing\u001B[0m')
|
||||
else:
|
||||
logging.debug('\u001B[1;37;41m Unexpected Others during closing\u001B[0m')
|
||||
|
||||
def copyin(self, ipaddress, username, password, source, destination):
|
||||
count = 0
|
||||
copy_status = False
|
||||
logging.debug('scp '+ username + '@' + ipaddress + ':' + source + ' ' + destination)
|
||||
while count < 10:
|
||||
scp_spawn = pexpect.spawn('scp '+ username + '@' + ipaddress + ':' + source + ' ' + destination, timeout = 100)
|
||||
scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0:
|
||||
scp_spawn.sendline('yes')
|
||||
scp_spawn.expect('password:')
|
||||
scp_spawn.sendline(password)
|
||||
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('1 - scp_response = ' + str(scp_response))
|
||||
elif scp_response == 1:
|
||||
scp_spawn.sendline(password)
|
||||
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0 or scp_response == 3:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('2 - scp_response = ' + str(scp_response))
|
||||
elif scp_response == 2:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('3 - scp_response = ' + str(scp_response))
|
||||
# adding a tempo when failure
|
||||
if not copy_status:
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
if copy_status:
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
|
||||
def copyout(self, ipaddress, username, password, source, destination):
|
||||
count = 0
|
||||
copy_status = False
|
||||
logging.debug('scp ' + source + ' ' + username + '@' + ipaddress + ':' + destination)
|
||||
while count < 4:
|
||||
scp_spawn = pexpect.spawn('scp ' + source + ' ' + username + '@' + ipaddress + ':' + destination, timeout = 100)
|
||||
scp_response = scp_spawn.expect(['Are you sure you want to continue connecting (yes/no)?', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0:
|
||||
scp_spawn.sendline('yes')
|
||||
scp_spawn.expect('password:')
|
||||
scp_spawn.sendline(password)
|
||||
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('1 - scp_response = ' + str(scp_response))
|
||||
elif scp_response == 1:
|
||||
scp_spawn.sendline(password)
|
||||
scp_response = scp_spawn.expect(['\$', 'Permission denied', 'password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||
if scp_response == 0 or scp_response == 3:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('2 - scp_response = ' + str(scp_response))
|
||||
elif scp_response == 2:
|
||||
count = 10
|
||||
copy_status = True
|
||||
else:
|
||||
logging.debug('3 - scp_response = ' + str(scp_response))
|
||||
# adding a tempo when failure
|
||||
if not copy_status:
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
if copy_status:
|
||||
pass
|
||||
else:
|
||||
sys.exit('SCP failed')
|
||||
|
||||
def getBefore(self):
|
||||
return str(self.ssh.before)
|
||||
@@ -2825,6 +2825,7 @@ target_link_libraries(smallblocktest
|
||||
m pthread ${ATLAS_LIBRARIES} dl
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
###################################################
|
||||
# For CUDA library
|
||||
###################################################
|
||||
@@ -2833,10 +2834,15 @@ CUDA_ADD_LIBRARY(LDPC_CU
|
||||
)
|
||||
CUDA_ADD_CUFFT_TO_TARGET(LDPC_CU)
|
||||
cuda_add_executable(ldpctest
|
||||
=======
|
||||
add_executable(ldpctest
|
||||
${PHY_NR_CODINGIF}
|
||||
>>>>>>> origin/develop
|
||||
${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c
|
||||
${T_SOURCE}
|
||||
${SHLIB_LOADER_SOURCES}
|
||||
)
|
||||
<<<<<<< HEAD
|
||||
|
||||
target_link_libraries(ldpctest -ldl
|
||||
-Wl,--start-group
|
||||
@@ -2851,6 +2857,8 @@ target_link_libraries(ldpctest -ldl
|
||||
# ${T_SOURCE}
|
||||
# ${SHLIB_LOADER_SOURCES}
|
||||
# )
|
||||
=======
|
||||
>>>>>>> origin/develop
|
||||
add_dependencies( ldpctest ldpc_orig ldpc_optim ldpc_optim8seg ldpc )
|
||||
|
||||
target_link_libraries(ldpctest
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# use UP and DOWN arrow keys to scroll the view displayed by timeplot
|
||||
|
||||
while read -n 1 key
|
||||
do
|
||||
case "$key" in
|
||||
'B' )
|
||||
kill -SIGUSR1 `ps aux|grep timeplot|grep -v grep|grep -v sh|tr -s ' ' :|cut -f 2 -d :`
|
||||
;;
|
||||
'A' )
|
||||
kill -SIGUSR2 `ps aux|grep timeplot|grep -v grep|grep -v sh|tr -s ' ' :|cut -f 2 -d :`
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -0,0 +1,349 @@
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="./images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">L2 nFAPI Simulator (no S1 Mode / 2-host deployment)</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Table of Contents ##
|
||||
|
||||
1. [Environment](#1-environment)
|
||||
2. [Retrieve the OAI eNB-UE source code](#2-retrieve-the-oai-enb-ue-source-code)
|
||||
3. [Setup of the USIM information in UE folder](#3-setup-of-the-usim-information-in-ue-folder)
|
||||
4. [Setup of the Configuration files](#4-setup-of-the-configuration-files)
|
||||
1. [The eNB Configuration file](#41-the-enb-configuration-file)
|
||||
2. [The UE Configuration file](#42-the-ue-configuration-file)
|
||||
5. [Build OAI UE and eNodeB](#5-build-oai-ue-and-enodeb)
|
||||
6. [Start the eNB](#6-start-the-enb)
|
||||
7. [Start the UE](#7-start-the-ue)
|
||||
8. [Test with ping](#8-test-with-ping)
|
||||
9. [Limitations](#9-limitations)
|
||||
|
||||
# 1. Environment #
|
||||
|
||||
You may not have access to an EPC or you don't want to hassle to deploy one.
|
||||
|
||||
2 servers are used in this deployment. You can use Virtual Machines instead of each server; like it is done in the CI process.
|
||||
|
||||
* Machine B contains the OAI eNB executable (`lte-softmodem`)
|
||||
* Machine C contains the OAI UE(s) executable (`lte-uesoftmodem`)
|
||||
|
||||
Example of L2 nFAPI Simulator testing environment:
|
||||
|
||||
<img src="./images/L2-sim-noS1-2-host-deployment.png" alt="" border=3>
|
||||
|
||||
Note that the IP addresses are indicative and need to be adapted to your environment.
|
||||
|
||||
# 2. Retrieve the OAI eNB-UE source code #
|
||||
|
||||
At the time of writing, the tag used in the `develop` branch to do this documentation was `2020.w16`.
|
||||
|
||||
The tutorial should be valid for the `master` branch tags such as `v1.2.0` or `v1.2.1`. But you may face issues that could be fixed in newer `develop` tags.
|
||||
|
||||
Please try to use the same commit ID on both eNB/UE hosts.
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git enb_folder
|
||||
cd enb_folder
|
||||
git checkout develop
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ue_folder
|
||||
cd ue_folder
|
||||
git checkout develop
|
||||
```
|
||||
|
||||
# 3. Setup of the USIM information in UE folder #
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
cd ue_folder
|
||||
# Edit openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf with your preferred editor
|
||||
```
|
||||
|
||||
Edit the USIM information within this file in order to match the HSS database. They **HAVE TO** match:
|
||||
|
||||
* PLMN+MSIN and IMSI of users table of HSS database **SHALL** be the same.
|
||||
* OPC of this file and OPC of users table of HSS database **SHALL** be the same.
|
||||
* USIM_API_K of this file and the key of users table of HSS database **SHALL** be the same.
|
||||
|
||||
When testing multiple UEs, it is necessary to add other UEs information like described below for 2 Users. Only UE0 (first UE) information is written in the original file.
|
||||
|
||||
```
|
||||
UE0:
|
||||
{
|
||||
USER: {
|
||||
IMEI="356113022094149";
|
||||
MANUFACTURER="EURECOM";
|
||||
MODEL="LTE Android PC";
|
||||
PIN="0000";
|
||||
};
|
||||
|
||||
SIM: {
|
||||
MSIN="0000000001"; // <-- Modify here
|
||||
USIM_API_K="8baf473f2f8fd09487cccbd7097c6862";
|
||||
OPC="e734f8734007d6c5ce7a0508809e7e9c";
|
||||
MSISDN="33611123456";
|
||||
};
|
||||
...
|
||||
};
|
||||
// Copy the UE0 and edit
|
||||
UE1: // <- Edit here
|
||||
{
|
||||
USER: {
|
||||
IMEI="356113022094149";
|
||||
MANUFACTURER="EURECOM";
|
||||
MODEL="LTE Android PC";
|
||||
PIN="0000";
|
||||
};
|
||||
|
||||
SIM: {
|
||||
MSIN="0000000002"; // <-- Modify here
|
||||
USIM_API_K="8baf473f2f8fd09487cccbd7097c6862";
|
||||
OPC="e734f8734007d6c5ce7a0508809e7e9c";
|
||||
MSISDN="33611123456";
|
||||
};
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
You can repeat the operation for as many users you want to test with.
|
||||
|
||||
# 4. Setup of the Configuration files #
|
||||
|
||||
**CAUTION: both proposed configuration files resides in the ci-scripts realm. You can copy them but you CANNOT push any modification on these 2 files as part of an MR without informing the CI team.**
|
||||
|
||||
## 4.1. The eNB Configuration file ##
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
cd enb_folder
|
||||
# Edit ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf with your preferred editor
|
||||
```
|
||||
|
||||
First verify the nFAPI interface setup on the physical ethernet interface of machineB and put the proper IP addresses for both hosts.
|
||||
|
||||
```
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
local_s_if_name = "ens3"; // <-- HERE
|
||||
remote_s_address = "192.168.122.169"; // <-- HERE
|
||||
local_s_address = "192.168.122.31"; // <-- HERE
|
||||
local_s_portc = 50001;
|
||||
remote_s_portc = 50000;
|
||||
local_s_portd = 50011;
|
||||
remote_s_portd = 50010;
|
||||
tr_s_preference = "nfapi";
|
||||
tr_n_preference = "local_RRC";
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
If you are testing more than 16 UEs, a proper setting on the RUs is necessary. **Note that this part is NOT present in the original configuration file**.
|
||||
|
||||
```
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1
|
||||
nb_rx = 1
|
||||
att_tx = 20
|
||||
att_rx = 0;
|
||||
bands = [38];
|
||||
max_pdschReferenceSignalPower = -23;
|
||||
max_rxgain = 116;
|
||||
eNB_instances = [0];
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Last, the S1 interface shall be properly set.
|
||||
|
||||
```
|
||||
////////// MME parameters:
|
||||
mme_ip_address = ( { ipv4 = "CI_MME_IP_ADDR"; // replace with 192.168.122.195
|
||||
ipv6 = "192:168:30::17";
|
||||
active = "yes";
|
||||
preference = "ipv4";
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
ENB_INTERFACE_NAME_FOR_S1_MME = "ens3"; // replace with the proper interface name
|
||||
ENB_IPV4_ADDRESS_FOR_S1_MME = "CI_ENB_IP_ADDR"; // replace with 192.168.122.31
|
||||
ENB_INTERFACE_NAME_FOR_S1U = "ens3"; // replace with the proper interface name
|
||||
ENB_IPV4_ADDRESS_FOR_S1U = "CI_ENB_IP_ADDR"; // replace with 192.168.122.31
|
||||
ENB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
ENB_IPV4_ADDRESS_FOR_X2C = "CI_ENB_IP_ADDR"; // replace with 192.168.122.31
|
||||
ENB_PORT_FOR_X2C = 36422; # Spec 36422
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
## 4.2. The UE Configuration file ##
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
cd ue_folder
|
||||
# Edit ci-scripts/conf_files/ue.nfapi.conf with your preferred editor
|
||||
```
|
||||
|
||||
Verify the nFAPI interface setup on the loopback interface.
|
||||
|
||||
```
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "nfapi";
|
||||
local_n_if_name = "ens3"; // <- HERE
|
||||
remote_n_address = "192.168.122.31"; // <- HERE
|
||||
local_n_address = "192.168.122.169"; // <- HERE
|
||||
local_n_portc = 50000;
|
||||
remote_n_portc = 50001;
|
||||
local_n_portd = 50010;
|
||||
remote_n_portd = 50011;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# 5. Build OAI UE and eNodeB #
|
||||
|
||||
See [Build documentation](./BUILD.md).
|
||||
|
||||
# 6. Start the eNB #
|
||||
|
||||
In the first terminal (the one you used to build the eNB):
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
cd enb_folder/cmake_targets
|
||||
sudo -E ./ran_build/build/lte-softmodem -O ../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf --noS1 > enb.log 2>&1
|
||||
sleep 10
|
||||
ifconfig
|
||||
ens3 Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX
|
||||
inet addr:192.168.122.31 Bcast:192.168.122.255 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_enb1 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.1.1 P-t-P:192.172.0.2 Mask:255.255.255.0
|
||||
....
|
||||
```
|
||||
|
||||
If you don't use redirection, you can test but many logs are printed on the console and this may affect performance of the L2-nFAPI simulator.
|
||||
|
||||
We do recommend the redirection in steady mode once your setup is correct.
|
||||
|
||||
# 7. Start the UE #
|
||||
|
||||
In the second terminal (the one you used to build the UE):
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
cd ue_folder/cmake_targets
|
||||
# Test 64 UEs, 1 thread in FDD mode
|
||||
sudo -E ./ran_build/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --noS1 --L2-emul 3 --num-ues 64 --nums_ue_thread 1 --nokrnmod 1 > ue.log 2>&1
|
||||
# Test 64 UEs, 1 thread in TDD mode
|
||||
sudo -E ./ran_build/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --noS1 --L2-emul 3 --num-ues 64 --nums_ue_thread 1 --nokrnmod 1 -T 1 > ue.log 2>&1
|
||||
# The "-T 1" option means TDD config
|
||||
```
|
||||
|
||||
- The number of UEs can set by using `--num-ues` option and the maximum UE number is 255 (with the `--mu*` options, otherwise 16).
|
||||
- The number of threads can set with the `--nums-ue-thread`. This number **SHALL NOT** be greater than the number of UEs.
|
||||
* At the time of writing, it seems to be enough to run on a single thread.
|
||||
- The `--nokrnmod 1` option makes use of the preferred and supported tunnel interface.
|
||||
- How many UE that can be tested depends on hardware (server , PC, etc) performance in your environment.
|
||||
|
||||
For example, running with 4 UEs:
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
cd ue_folder/cmake_targets
|
||||
sudo -E ./ran_build/build/lte-uesoftmodem -O ../ci-scripts/conf_files/ue.nfapi.conf --noS1 --L2-emul 3 --num-ues 64 --nums_ue_thread 1 --nokrnmod 1 > ue.log 2>&1
|
||||
sleep 10
|
||||
ifconfig
|
||||
ens3 Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX
|
||||
inet addr:192.168.122.169 Bcast:192.168.122.255 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_ue1 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.1.2 P-t-P:192.172.0.2 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_ue2 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.1.3 P-t-P:192.172.0.3 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_ue3 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.1.4 P-t-P:192.172.0.4 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_ue4 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.1.5 P-t-P:192.172.0.5 Mask:255.255.255.0
|
||||
....
|
||||
oaitun_uem1 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.0.2.2 P-t-P:10.0.2.2 Mask:255.255.255.0
|
||||
....
|
||||
....
|
||||
```
|
||||
|
||||
Having the 4 oaitun_ue tunnel interfaces up and with an allocated address means the connection with EPC went alright.
|
||||
|
||||
# 8. Test with ping #
|
||||
|
||||
In a third terminal, after around 10 seconds, the UE(s) shall be connected to the eNB: Check with ifconfig
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
# Ping UE1 IP address based on the EPC pool used: in this example:
|
||||
ping -I oaitun_enb1 -c 20 10.0.1.2
|
||||
# Ping UE4 IP address based on the EPC pool used: in this example:
|
||||
ping -I oaitun_enb1 -c 20 10.0.1.5
|
||||
```
|
||||
|
||||
Ping from the UE side:
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
ping -I oaitun_ue1 -c 20 10.0.1.1
|
||||
ping -I oaitun_ue3 -c 20 10.0.1.1
|
||||
```
|
||||
|
||||
iperf operations can also be performed.
|
||||
|
||||
DL traffic:
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineC
|
||||
iperf -B 10.0.1.2 -u -s -i 1 -fm -p 5002
|
||||
$ ssh sudousername@machineB
|
||||
iperf -c 10.0.1.2 -u -t 30 -b 3M -i 1 -fm -B 10.0.1.1 -p 5002
|
||||
```
|
||||
|
||||
UL traffic:
|
||||
|
||||
```bash
|
||||
$ ssh sudousername@machineB
|
||||
iperf -B 10.0.1.1 -u -s -i 1 -fm -p 5002
|
||||
$ ssh sudousername@machineC
|
||||
iperf -c 10.0.1.1 -u -t 30 -b 2M -i 1 -fm -B 10.0.1.2 -p 5002
|
||||
```
|
||||
|
||||
# 9. Limitations #
|
||||
|
||||
|
||||
----
|
||||
|
||||
[oai wiki home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home)
|
||||
|
||||
[oai softmodem features](FEATURE_SET.md)
|
||||
|
||||
[oai softmodem build procedure](BUILD.md)
|
||||
|
||||
[L2 nfapi simulator](L2NFAPI.md)
|
||||
@@ -0,0 +1,336 @@
|
||||
# OpenAirInterface for SystemX
|
||||
|
||||
# Terminology
|
||||
|
||||
****This document use the 5G terminology****
|
||||
|
||||
**Central Unit (CU):** It is a logical node that includes the gNB
|
||||
functions like Transfer of user data, Mobility control, Radio access
|
||||
network sharing, Positioning, Session Management etc., except those
|
||||
functions allocated exclusively to the DU. CU controls the operation of
|
||||
DUs over front-haul (Fs) interface. A central unit (CU) may also be
|
||||
known as BBU/REC/RCC/C-RAN/V-RAN/VNF
|
||||
|
||||
**Distributed Unit (DU):** This logical node includes a subset of the
|
||||
gNB functions, depending on the functional split option. Its operation
|
||||
is controlled by the CU. Distributed Unit (DU) also known with other
|
||||
names like RRH/RRU/RE/RU/PNF.
|
||||
|
||||
In OpenAir code, the terminology is often RU and BBU.
|
||||
|
||||
# OpenAirUsage
|
||||
|
||||
## EPC and general environment
|
||||
|
||||
### OAI EPC
|
||||
|
||||
Use the stable OAI EPC, that can run in one machine (VM or standalone)
|
||||
|
||||
Draft description:
|
||||
<https://open-cells.com/index.php/2017/08/22/all-in-one-openairinterface-august-22nd/>
|
||||
|
||||
## Standalone 4G
|
||||
|
||||
EPC+eNB on one machine, the UE can be commercial or OAI UE.
|
||||
|
||||
### USRP B210
|
||||
|
||||
Main current issue: traffic is good only on coaxial link between UE and
|
||||
eNB (probably power management issue).
|
||||
|
||||
### Simulated RF
|
||||
|
||||
Running eNB+UE both OAI can be done over a virtual RF link.
|
||||
|
||||
The UE current status is that threads synchronization is implicit in
|
||||
some cases. As the RF simulator is very quick, a “sleep()” is required
|
||||
in the UE main loop
|
||||
|
||||
(line 1744, targets/RT/USER/lte-ue.c).
|
||||
|
||||
Running also the UE in the same machine is possible with simulated RF.
|
||||
|
||||
Running in same machine is simpler, offers about infinite speed for
|
||||
virtual RF samples transmission.
|
||||
|
||||
A specific configuration is required because the EPC Sgi interface has
|
||||
the same IP tunnel end point as the UE.
|
||||
|
||||
So, we have to create a network namespace for the UE and to route data
|
||||
in/out of the namespace.
|
||||
|
||||
```bash
|
||||
ip netns delete aNameSpace 2> /dev/null
|
||||
|
||||
ip link delete v-eth1 2> /dev/null
|
||||
|
||||
ip netns add aNameSpace
|
||||
|
||||
ip link add v-eth1 type veth peer name v-peer1
|
||||
|
||||
ip link set v-peer1 netns aNameSpace
|
||||
|
||||
ip addr add 10.200.1.1/24 dev v-eth1
|
||||
|
||||
ip link set v-eth1 up
|
||||
|
||||
iptables -t nat -A POSTROUTING -s 10.200.1.0/255.255.255.0 -o enp0s31f6 \
|
||||
-j MASQUERADE
|
||||
|
||||
iptables -A FORWARD -i enp0s31f6 -o v-eth1 -j ACCEPT
|
||||
|
||||
iptables -A FORWARD -o enp0s31f6 -i v-eth1 -j ACCEPT
|
||||
|
||||
ip netns exec aNameSpace ip link set dev lo up
|
||||
|
||||
ip netns exec aNameSpace ip addr add 10.200.1.2/24 dev v-peer1
|
||||
|
||||
ip netns exec aNameSpace ip link set v-peer1 up
|
||||
|
||||
ip netns exec aNameSpace bash
|
||||
```
|
||||
|
||||
After the last command, the Linux shell is in the new namespace, ready
|
||||
to run the UE.
|
||||
|
||||
To make user plan traffic, the traffic generator has to run in the same
|
||||
namespace
|
||||
|
||||
```bash
|
||||
ip netns exec aNameSpace bash
|
||||
```
|
||||
|
||||
The traffic genenrator has to specify the interface:
|
||||
|
||||
```bash
|
||||
route add default oaitun_ue1
|
||||
```
|
||||
|
||||
or specify the outgoing route in the traffic generator (like option “-I”
|
||||
in ping command).
|
||||
|
||||
## Split 6 DL 4G
|
||||
|
||||
The contract describes to reuse the uplink existing if4p5 and to develop
|
||||
is this work the downlink “functional split 6”.
|
||||
|
||||
The customer required after signature to develop also the uplink
|
||||
functional split 6. This is accepted, as long as the whole work is
|
||||
research with no delivery completeness warranty.
|
||||
|
||||
### Simulation
|
||||
|
||||
To be able to verify the new features and to help in all future
|
||||
developments, Open Cells added and improved the Rf board simulator
|
||||
during this contract.
|
||||
|
||||
We added the channel modeling simulation, that offer to simulate various
|
||||
3GPP defined channels.
|
||||
|
||||
### Main loop
|
||||
|
||||
The main log is in RF simulator is in
|
||||
|
||||
`targets/RT/USER/lte-ru.c and targets/RT/USER/lte-enb.c`
|
||||
|
||||
As this piece of SW is very complex and doesn’t meet our goals
|
||||
(functional split 6), a cleaned version replaces these 2 files in
|
||||
executables/ocp-main.c (openair1/SCHED/prach\_procedures.c is also
|
||||
replaced by this new file as it only launching the RACH actual work in a
|
||||
way not compatible with our FS6).
|
||||
|
||||
The main loop cadences the I/Q samples reception, signal processing and
|
||||
I/Q samples sending.
|
||||
|
||||
The main loop uses extensively function pointers to call the right
|
||||
processing function depending on the split case.
|
||||
|
||||
A lot of OAI reduntant global variables contains the same semantic data: time,frame, subframe.
|
||||
The reworked main loop take care of a uniq variable that comes directly from harware: RF board sampling number.
|
||||
|
||||
To use OAI, we need to set all OAI variables that derivates from this timestamp value. The function setAllfromTS() implements this.
|
||||
|
||||
### Splitted main level
|
||||
|
||||
When FS6 is actived, a main loop for DU (du_fs6()) a main loop for CU case replaces the uniq eNB main loop.
|
||||
|
||||
Each of these main loops calls initialization of OAI LTE data and the FS6 transport layer initialization.
|
||||
|
||||
Then, it runs a infinite loop on: set time, call UL and DL. The time comes from the RF board, so the DU sends the time to the CU.
|
||||
|
||||
This is enough for RF board dialog, but the FS6 is higher in SW layers,
|
||||
we need to cut higher functions inside downlink and uplink procedures.
|
||||
|
||||
As much as possible, the FS6 code is in the directory OPENAIR_DIR/executables. When a given OAI piece of code is small or need complex changes, it is reworked in the file fs6-main.c. The functions naming keeps the OAI function name, adding suffix _fromsplit() or _tosplit().
|
||||
|
||||
When this organization would lead to large code copy, it is better to insert modifications in OAI code. This is done in two files:
|
||||
|
||||
- openair1/SCHED/phy_procedures_lte_eNb.c: to send signaling channels computation results
|
||||
- the function sendFs6Ulharq() centralizes all signaling channels forwarding to CU
|
||||
- openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c: to deal with FS6 user plane split
|
||||
- sendFs6Ul() is used once to forward user plane to CU
|
||||
|
||||
|
||||
### DownLink
|
||||
|
||||
The main procedure is phy\_procedures\_eNB\_TX()
|
||||
|
||||
This is building the common channels (beacon, multi-UE signaling).
|
||||
|
||||
The FS6 split breaks this function into pieces:
|
||||
|
||||
* The multi-UE signals, built by common\_signal\_procedures(),
|
||||
subframe2harq\_pid(), generate\_dci\_top(), subframe2harq\_pid()
|
||||
* These functions run in the DU, nevertheless all context has to be sent
|
||||
(it is also needed partially for UL spitting)
|
||||
* Run in the DU also to meet the requirement of pushing
|
||||
in DU the data encoded with large redundancy (>3 redundancy)
|
||||
|
||||
* the per UE data: pdsch\_procedures() needs further splitting:
|
||||
|
||||
* dlsch\_encoding\_all() that makes the encoding: turbo code
|
||||
and lte\_rate\_matching\_turbo() that will be in the DU (some
|
||||
unlikely cases can reach redundancy up to x3, when MCS is very
|
||||
low (negative SINR cases)).
|
||||
|
||||
* dlsch\_encoding() output needs to be transmitted between the
|
||||
DU and the CU for functional split 6.
|
||||
* dlsch\_scrambling() that will go in the DU
|
||||
* dlsch\_modulation() that will go in the DU
|
||||
|
||||
The du user plane data is made of expanded bit in OAI at FS6 split level. 1 pair of functions compact back these bits into 8bits/byte before sending data and expand it again in the DU data reception (functions: fs6Dl(un)pack()).
|
||||
|
||||
### Uplink
|
||||
|
||||
The uplink require configuration that is part of the DL transmission.
|
||||
|
||||
It interprets the signalling to extract the RACH and the per UE data
|
||||
channels.
|
||||
|
||||
Ocp-main.c:rxtx() calls directly the entry procedure
|
||||
phy\_procedures\_eNB\_uespec\_RX() calls:
|
||||
|
||||
* rx\_ulsch() that demodulate and extract soft bits per UE.
|
||||
|
||||
* This function runs in the DU
|
||||
* the output data will be processes in the DU, so it needs to be
|
||||
transmitted to the DU
|
||||
* ulsch\_decoding() that do lte\_rate\_matching\_turbo\_rx()
|
||||
sub\_block\_deinterleaving\_turbo()
|
||||
then turbo decode that is in the CU
|
||||
* fill\_ulsch\_cqi\_indication() fill\_crc\_indication() , fill\_rx\_indication()
|
||||
* DU performs the signal processing of each channel data, prepare and sent to the CU the computed result
|
||||
|
||||
* Random access channel detection runs in the DU
|
||||
* the DU reports to the CU only the detected temprary identifier for RACH response
|
||||
|
||||
|
||||
### signaling data in each direction (UL and DL)
|
||||
|
||||
|
||||
* each LTE channel needs to be propagated between CU and DU
|
||||
* the simplest are the almost static data such as PSS/SSS, that need only static eNB parameters and primary information (frame numbering)
|
||||
* all the other channels require data transmission CU to DU and DU to CU
|
||||
* the general design push all the low level processing for these channels in the DU
|
||||
* the CU interface transports only signal processing results (UL) or configuration to create the RF signal (DL case)
|
||||
* HARQ is detected in the DU, then only the ACK or NACK is reported to CU
|
||||
|
||||
* the CU have to control the power and MCS (modulation and coding scheme)
|
||||
* the DU performs the signal processing and report only the decoded data like the CQI
|
||||
* as the DU performas the modulation, scrambling and puncturing, each data packet is associated with the LTE parameters required for these features
|
||||
* in DL, the CU associates the control parameters and the user plane data
|
||||
* in UL, the CU sends upfront the scheduled UL data to the DU. So, the DU have the required knowledge to decode the next subframes in time.
|
||||
|
||||
### UDP transport layer
|
||||
|
||||
A general UDP transport layer is in executables/transport\_split.c
|
||||
|
||||
Linux offers a UDP socket builtin timeout, that we use.
|
||||
|
||||
In and out buffers are memory zones that contains compacted
|
||||
(concatenated) UDP chunks.
|
||||
|
||||
For output, sendSubFrame() sends each UDP chunk
|
||||
|
||||
For input, receiveSubFrame() collects all UDP chunks for a group (a
|
||||
subframe in OAI LTE case). It returns in the following cases:
|
||||
|
||||
- all chunks are received
|
||||
- a timeout expired
|
||||
- a chunk from the next subframe already arrived
|
||||
|
||||
### Functional split 6 usage
|
||||
|
||||
The ocp cleaned main hale to be used: run ocp-softmodem instead of
|
||||
lte-softmodem.
|
||||
|
||||
The functionality and parameters is the same, enhanced with FS6 mode.
|
||||
|
||||
The end line option “--split73” enables the fs6 (also called split 7.3) mode and decided to be cu or du.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 cu:127.0.0.1
|
||||
```
|
||||
|
||||
Run the CU init of the split 6 eNB, that will call du on 127.0.0.1 address
|
||||
|
||||
```bash
|
||||
./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 du:127.0.0.1
|
||||
```
|
||||
|
||||
will run the du, calling the cu on 127.0.0.1
|
||||
|
||||
If the CU and the DU are not on the same machine, the remote address of each side need to be specified as per this example
|
||||
|
||||
```bash
|
||||
./ocp-softmodem -O $OPENAIR_DIR/enb.fs6.example.conf --rfsim --log_config.phy_log_level debug --split73 du:192.168.1.55
|
||||
```
|
||||
|
||||
runs the functional split 6 DU
|
||||
|
||||
```bash
|
||||
./lte-uesoftmodem -C 2685000000 -r 50 --rfsim --rfsimulator.serveraddr 192.168.1.1 -d
|
||||
```
|
||||
|
||||
Runs the UE (to have the UE signal scope, compile it with make uescope)
|
||||
|
||||
CU+DU+UE can run with option `--noS1` to avoid to use a EPC and/or with `--rfsim` to simulate RF board
|
||||
|
||||
|
||||
## 5G and F1
|
||||
|
||||
Today 5G achievement is limited to physical layer.
|
||||
|
||||
The available modulation is 40MHz, that require one X310 or N300 for the
|
||||
gNB and a X310 or N300 for the nrUE.
|
||||
|
||||
### Usage with X310
|
||||
|
||||
Linux configuration:
|
||||
<https://files.ettus.com/manual/page_usrp_x3x0_config.html>
|
||||
|
||||
We included most of this configuration included in OAI source code.
|
||||
|
||||
Remain to set the NIC (network interface card) MTU to 9000 (jumbo
|
||||
frames).
|
||||
|
||||
### Running 5G
|
||||
|
||||
Usage with RFsimulator:
|
||||
|
||||
**gNB**
|
||||
|
||||
```bash
|
||||
sudo RFSIMULATOR=server ./nr-softmodem -O \
|
||||
../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf \
|
||||
--parallel-config PARALLEL\_SINGLE\_THREAD
|
||||
```
|
||||
|
||||
**nrUE**
|
||||
|
||||
```bash
|
||||
sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem --numerology 1 -r 106 -C \
|
||||
3510000000 -d
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Author and copyright: Laurent Thomas, open-cells.com
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __SPLIT_HEADERS_H
|
||||
#define __SPLIT_HEADERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <openair1/PHY/defs_eNB.h>
|
||||
|
||||
#define CU_PORT "7878"
|
||||
#define DU_PORT "8787"
|
||||
#define SPLIT73_CU 1
|
||||
#define SPLIT73_DU 2
|
||||
extern int split73;
|
||||
|
||||
#define MTU 65536
|
||||
#define UDP_TIMEOUT 900000L // in micro second (struct timeval, NOT struct timespec)
|
||||
// linux may timeout for a much longer time (up to 10ms)
|
||||
#define blockAlign 32 //bytes to align memory for SIMD copy (256 bits vectors)
|
||||
|
||||
// FS6 transport configuration and handler
|
||||
typedef struct {
|
||||
char *sourceIP;
|
||||
char *sourcePort;
|
||||
char *destIP;
|
||||
char *destPort;
|
||||
struct addrinfo *destAddr;
|
||||
int sockHandler;
|
||||
} UDPsock_t;
|
||||
|
||||
#define CTsentCUv0 0xA500
|
||||
#define CTsentDUv0 0x5A00
|
||||
|
||||
// Main FS6 transport layer header
|
||||
// All packets starts with this header
|
||||
typedef struct commonUDP_s {
|
||||
uint64_t timestamp; // id of the group (subframe for LTE)
|
||||
uint16_t nbBlocks; // total number of blocks for this timestamp
|
||||
uint16_t blockID; // id: 0..nbBocks-1
|
||||
uint16_t contentType; // defines the content format
|
||||
uint16_t contentBytes; // will be sent in a UDP packet, so must be < 2^16 bytes
|
||||
uint64_t senderClock;
|
||||
} commonUDP_t;
|
||||
|
||||
// FS6 UL common header (DU to CU)
|
||||
// gives the RACH detection data and is always sent to inform the CU that a subframe arrived
|
||||
typedef struct {
|
||||
uint16_t max_preamble[4];
|
||||
uint16_t max_preamble_energy[4];
|
||||
uint16_t max_preamble_delay[4];
|
||||
uint16_t avg_preamble_energy[4];
|
||||
} fs6_ul_t;
|
||||
|
||||
// FS6 DL common header (CU to DU)
|
||||
// gives the DCI configuration from each subframe
|
||||
typedef struct {
|
||||
uint8_t pbch_pdu[4];
|
||||
int num_pdcch_symbols;
|
||||
int num_dci;
|
||||
DCI_ALLOC_t dci_alloc[8];
|
||||
int num_mdci;
|
||||
int amp;
|
||||
LTE_eNB_PHICH phich_vars;
|
||||
uint64_t DuClock;
|
||||
uint64_t CuSpentMicroSec;
|
||||
} fs6_dl_t;
|
||||
|
||||
// a value to type all sub packets,
|
||||
// to detect errors, and to be able to extend to other versions
|
||||
// the first byte of each sub structure should match one of these values
|
||||
enum pckType {
|
||||
fs6UlConfig=25,
|
||||
fs6DlConfig=26,
|
||||
fs6ULConfigCCH=27,
|
||||
fs6ULsch=28,
|
||||
fs6ULcch=29,
|
||||
fs6ULindicationHarq=40,
|
||||
fs6ULindicationSr=41,
|
||||
};
|
||||
|
||||
// CU to DU definition of a future UL subframe decode
|
||||
// defines a UE future data plane
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
uint16_t UE_id;
|
||||
int8_t harq_pid;
|
||||
UE_type_t ue_type;
|
||||
|
||||
uint8_t dci_alloc;
|
||||
uint8_t rar_alloc;
|
||||
SCH_status_t status;
|
||||
uint8_t Msg3_flag;
|
||||
uint8_t subframe;
|
||||
uint32_t frame;
|
||||
uint8_t handled;
|
||||
uint8_t phich_active;
|
||||
uint8_t phich_ACK;
|
||||
uint16_t previous_first_rb;
|
||||
uint32_t B;
|
||||
uint32_t G;
|
||||
UCI_format_t uci_format;
|
||||
uint8_t Or2;
|
||||
uint8_t o_RI[2];
|
||||
uint8_t o_ACK[4];
|
||||
uint8_t O_ACK;
|
||||
uint8_t o_RCC;
|
||||
int16_t q_ACK[MAX_ACK_PAYLOAD];
|
||||
int16_t q_RI[MAX_RI_PAYLOAD];
|
||||
uint32_t RTC[MAX_NUM_ULSCH_SEGMENTS];
|
||||
uint8_t ndi;
|
||||
uint8_t round;
|
||||
uint8_t rvidx;
|
||||
uint8_t Nl;
|
||||
uint8_t n_DMRS;
|
||||
uint8_t previous_n_DMRS;
|
||||
uint8_t n_DMRS2;
|
||||
int32_t delta_TF;
|
||||
uint32_t repetition_number ;
|
||||
uint32_t total_number_of_repetitions;
|
||||
|
||||
uint16_t harq_mask;
|
||||
uint16_t nb_rb;
|
||||
uint8_t Qm;
|
||||
uint16_t first_rb;
|
||||
uint8_t O_RI;
|
||||
uint8_t Or1;
|
||||
uint16_t Msc_initial;
|
||||
uint8_t Nsymb_initial;
|
||||
uint8_t V_UL_DAI;
|
||||
uint8_t srs_active;
|
||||
uint32_t TBS;
|
||||
uint8_t Nsymb_pusch;
|
||||
uint8_t Mlimit;
|
||||
uint8_t max_turbo_iterations;
|
||||
uint8_t bundling;
|
||||
uint16_t beta_offset_cqi_times8;
|
||||
uint16_t beta_offset_ri_times8;
|
||||
uint16_t beta_offset_harqack_times8;
|
||||
uint8_t Msg3_active;
|
||||
uint16_t rnti;
|
||||
uint8_t cyclicShift;
|
||||
uint8_t cooperation_flag;
|
||||
uint8_t num_active_cba_groups;
|
||||
uint16_t cba_rnti[4];//NUM_MAX_CBA_GROUP];
|
||||
} fs6_dl_ulsched_t;
|
||||
|
||||
// CU to DU defintion of a DL packet for a given UE
|
||||
// The data itself is padded at the end of this structure
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
int UE_id;
|
||||
int8_t harq_pid;
|
||||
uint16_t rnti;
|
||||
int16_t sqrt_rho_a;
|
||||
int16_t sqrt_rho_b;
|
||||
CEmode_t CEmode:8;
|
||||
uint16_t nb_rb;
|
||||
uint8_t Qm;
|
||||
int8_t Nl;
|
||||
uint8_t pdsch_start;
|
||||
uint8_t sib1_br_flag;
|
||||
uint16_t i0;
|
||||
uint32_t rb_alloc[4];
|
||||
int dataLen;
|
||||
} fs6_dl_uespec_t;
|
||||
|
||||
// CU to DU definition of CCH channel
|
||||
typedef struct {
|
||||
int16_t UE_id;
|
||||
LTE_eNB_UCI cch_vars;
|
||||
} fs6_dl_uespec_ulcch_element_t;
|
||||
|
||||
// header to group all UE CCH channels definitions in one UDP packet
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
int16_t nb_active_ue;
|
||||
} fs6_dl_uespec_ulcch_t;
|
||||
|
||||
// code internal, not transmitted as this
|
||||
typedef struct {
|
||||
int ta;
|
||||
} ul_propagation_t;
|
||||
|
||||
// One UE UL data, data plane, UE data appended after the header
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
short UE_id;
|
||||
uint8_t harq_id;
|
||||
uint8_t segment;
|
||||
int segLen;
|
||||
int r_offset;
|
||||
int G;
|
||||
int ulsch_power[2];
|
||||
uint8_t o_ACK[4];
|
||||
uint8_t O_ACK;
|
||||
int ta;
|
||||
uint8_t o[MAX_CQI_BYTES];
|
||||
uint8_t cqi_crc_status;
|
||||
} fs6_ul_uespec_t;
|
||||
|
||||
// UL UCI (control plane), per UE
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
int UEid;
|
||||
int frame;
|
||||
int subframe;
|
||||
LTE_eNB_UCI uci;
|
||||
uint8_t harq_ack[4];
|
||||
uint8_t tdd_mapping_mode;
|
||||
uint16_t tdd_multiplexing_mask;
|
||||
unsigned short n0_subband_power_dB;
|
||||
uint16_t rnti;
|
||||
int32_t stat;
|
||||
} fs6_ul_uespec_uci_element_t;
|
||||
|
||||
// all segments UCI grouped in one UDP packet
|
||||
typedef struct {
|
||||
enum pckType type:8;
|
||||
int16_t nb_active_ue;
|
||||
} fs6_ul_uespec_uci_t;
|
||||
|
||||
|
||||
bool createUDPsock (char *sourceIP, char *sourcePort, char *destIP, char *destPort, UDPsock_t *result);
|
||||
int receiveSubFrame(UDPsock_t *sock, void *bufferZone, int bufferSize, uint16_t contentType);
|
||||
int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize, uint16_t contentType);
|
||||
|
||||
#define initBufferZone(xBuf) \
|
||||
uint8_t xBuf[FS6_BUF_SIZE]; \
|
||||
((commonUDP_t *)xBuf)->nbBlocks=0;
|
||||
|
||||
#define hUDP(xBuf) ((commonUDP_t *)xBuf)
|
||||
#define hDL(xBuf) ((fs6_dl_t*)(((commonUDP_t *)xBuf)+1))
|
||||
#define hUL(xBuf) ((fs6_ul_t*)(((commonUDP_t *)xBuf)+1))
|
||||
#define hDLUE(xBuf) ((fs6_dl_uespec_t*) (((fs6_dl_t*)(((commonUDP_t *)xBuf)+1))+1))
|
||||
#define hTxULUE(xBuf) ((fs6_dl_ulsched_t*) (((fs6_dl_t*)(((commonUDP_t *)xBuf)+1))+1))
|
||||
#define hTxULcch(xBuf) ((fs6_dl_uespec_ulcch_t*) (((fs6_dl_t*)(((commonUDP_t *)xBuf)+1))+1))
|
||||
#define hULUE(xBuf) ((fs6_ul_uespec_t*) (((fs6_ul_t*)(((commonUDP_t *)xBuf)+1))+1))
|
||||
#define hULUEuci(xBuf) ((fs6_ul_uespec_uci_t*) (((fs6_ul_t*)(((commonUDP_t *)xBuf)+1))+1))
|
||||
|
||||
static inline size_t alignedSize(uint8_t *ptr) {
|
||||
commonUDP_t *header=(commonUDP_t *) ptr;
|
||||
return ((header->contentBytes+sizeof(commonUDP_t)+blockAlign-1)/blockAlign)*blockAlign;
|
||||
}
|
||||
|
||||
static inline void *commonUDPdata(uint8_t *ptr) {
|
||||
return (void *) (((commonUDP_t *)ptr)+1);
|
||||
}
|
||||
|
||||
void setAllfromTS(uint64_t TS, L1_rxtx_proc_t *proc);
|
||||
void sendFs6Ulharq(enum pckType type, int UEid, PHY_VARS_eNB *eNB,LTE_eNB_UCI *uci, int frame, int subframe, uint8_t *harq_ack, uint8_t tdd_mapping_mode, uint16_t tdd_multiplexing_mask,
|
||||
uint16_t rnti, int32_t stat);
|
||||
void sendFs6Ul(PHY_VARS_eNB *eNB, int UE_id, int harq_pid, int segmentID, int16_t *data, int dataLen, int r_offset);
|
||||
void *cu_fs6(void *arg);
|
||||
void *du_fs6(void *arg);
|
||||
void fill_rf_config(RU_t *ru, char *rf_config_file);
|
||||
int init_rf(RU_t *ru);
|
||||
void rx_rf(RU_t *ru, L1_rxtx_proc_t *proc);
|
||||
void tx_rf(RU_t *ru, L1_rxtx_proc_t *proc);
|
||||
void common_signal_procedures (PHY_VARS_eNB *eNB,int frame, int subframe);
|
||||
void pmch_procedures(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc);
|
||||
bool dlsch_procedures(PHY_VARS_eNB *eNB,
|
||||
L1_rxtx_proc_t *proc,
|
||||
int harq_pid,
|
||||
LTE_eNB_DLSCH_t *dlsch,
|
||||
LTE_eNB_UE_stats *ue_stats) ;
|
||||
void postDecode(L1_rxtx_proc_t *proc, notifiedFIFO_elt_t *req);
|
||||
void pdsch_procedures(PHY_VARS_eNB *eNB,
|
||||
L1_rxtx_proc_t *proc,
|
||||
int harq_pid,
|
||||
LTE_eNB_DLSCH_t *dlsch,
|
||||
LTE_eNB_DLSCH_t *dlsch1);
|
||||
void srs_procedures(PHY_VARS_eNB *eNB,L1_rxtx_proc_t *proc);
|
||||
void uci_procedures(PHY_VARS_eNB *eNB,
|
||||
L1_rxtx_proc_t *proc);
|
||||
void ocp_rx_prach(PHY_VARS_eNB *eNB,
|
||||
L1_rxtx_proc_t *proc,
|
||||
RU_t *ru,
|
||||
uint16_t *max_preamble,
|
||||
uint16_t *max_preamble_energy,
|
||||
uint16_t *max_preamble_delay,
|
||||
uint16_t *avg_preamble_energy,
|
||||
uint16_t Nf,
|
||||
uint8_t tdd_mapindex,
|
||||
uint8_t br_flag);
|
||||
void rx_prach0(PHY_VARS_eNB *eNB,
|
||||
RU_t *ru,
|
||||
int frame_prach,
|
||||
int subframe,
|
||||
uint16_t *max_preamble,
|
||||
uint16_t *max_preamble_energy,
|
||||
uint16_t *max_preamble_delay,
|
||||
uint16_t *avg_preamble_energy,
|
||||
uint16_t Nf,
|
||||
uint8_t tdd_mapindex,
|
||||
uint8_t br_flag,
|
||||
uint8_t ce_level
|
||||
);
|
||||
void ocp_tx_rf(RU_t *ru, L1_rxtx_proc_t *proc);
|
||||
|
||||
// mistakes in main OAI
|
||||
void phy_init_RU(RU_t *);
|
||||
void fep_full(RU_t *ru, int subframe);
|
||||
void feptx_prec(RU_t *ru,int frame,int subframe);
|
||||
void feptx_ofdm(RU_t *ru, int frame, int subframe);
|
||||
void oai_subframe_ind(uint16_t sfn, uint16_t sf);
|
||||
extern uint16_t sf_ahead;
|
||||
#endif
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Author and copyright: Laurent Thomas, open-cells.com
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <executables/split_headers.h>
|
||||
#include <sys/types.h> /* See NOTES */
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netdb.h>
|
||||
#include <targets/RT/USER/lte-softmodem.h>
|
||||
|
||||
bool createUDPsock (char *sourceIP, char *sourcePort, char *destIP, char *destPort, UDPsock_t *result) {
|
||||
struct addrinfo hints= {0}, *servinfo, *p;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
int status;
|
||||
|
||||
if ((status = getaddrinfo(sourceIP, sourcePort, &hints, &servinfo)) != 0) {
|
||||
LOG_E(GTPU,"getaddrinfo error: %s\n", gai_strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
// loop through all the results and bind to the first we can
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((result->sockHandler = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1) {
|
||||
LOG_W(GTPU,"socket: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind(result->sockHandler, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(result->sockHandler);
|
||||
LOG_W(GTPU,"bind: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
break; // if we get here, we must have connected successfully
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
// looped off the end of the list with no successful bind
|
||||
LOG_E(GTPU,"failed to bind socket: %s %s \n",sourceIP,sourcePort);
|
||||
return false;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo); // all done with this structure
|
||||
|
||||
if ((status = getaddrinfo(destIP, destPort, &hints, &servinfo)) != 0) {
|
||||
LOG_E(GTPU,"getaddrinfo error: %s\n", gai_strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (servinfo) {
|
||||
result->destAddr=servinfo;
|
||||
} else {
|
||||
LOG_E(PHY,"No valid UDP addr: %s:%s\n",destIP, destPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
int enable=1;
|
||||
AssertFatal(setsockopt(result->sockHandler, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))==0,"");
|
||||
struct timeval tv= {0,UDP_TIMEOUT};
|
||||
|
||||
if (IS_SOFTMODEM_RFSIM)
|
||||
tv.tv_sec=2; //debug: wait 2 seconds for human understanding
|
||||
|
||||
AssertFatal(setsockopt(result->sockHandler, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) ==0,"");
|
||||
// Make a send/recv buffer larger than a a couple of subframe
|
||||
// so the kernel will store for us in and out paquets
|
||||
int buff=1000*1000*10;
|
||||
AssertFatal ( setsockopt(result->sockHandler, SOL_SOCKET, SO_SNDBUF, &buff, sizeof(buff)) == 0, "");
|
||||
AssertFatal ( setsockopt(result->sockHandler, SOL_SOCKET, SO_RCVBUF, &buff, sizeof(buff)) == 0, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
// sock: udp socket
|
||||
// bufferZone: a reception area of bufferSize
|
||||
int receiveSubFrame(UDPsock_t *sock, void *bufferZone, int bufferSize, uint16_t contentType) {
|
||||
int rcved=0;
|
||||
commonUDP_t *bufOrigin=(commonUDP_t *)bufferZone;
|
||||
static uint8_t crossData[65536];
|
||||
static int crossDataSize=0;
|
||||
|
||||
if (crossDataSize) {
|
||||
LOG_D(HW,"copy a block received in previous subframe\n");
|
||||
memcpy(bufferZone, crossData, crossDataSize);
|
||||
rcved=1;
|
||||
bufferZone+=crossDataSize;
|
||||
crossDataSize=0;
|
||||
}
|
||||
|
||||
do {
|
||||
//read all subframe data from the control unit
|
||||
int ret=recv(sock->sockHandler, bufferZone, bufferSize, 0);
|
||||
|
||||
if ( ret==-1) {
|
||||
if ( errno == EWOULDBLOCK || errno== EINTR ) {
|
||||
LOG_I(HW,"Received: Timeout, subframe incomplete\n");
|
||||
return rcved;
|
||||
} else {
|
||||
LOG_E(HW,"Critical issue in socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (hUDP(bufferZone)->contentType != contentType)
|
||||
abort();
|
||||
|
||||
if (rcved && bufOrigin->timestamp != hUDP(bufferZone)->timestamp ) {
|
||||
if ( hUDP(bufferZone)->timestamp > bufOrigin->timestamp ) {
|
||||
LOG_W(HW,"Received data for TS: %lu before end of TS : %lu completion\n",
|
||||
hUDP(bufferZone)->timestamp,
|
||||
bufOrigin->timestamp);
|
||||
memcpy(crossData, bufferZone, ret );
|
||||
crossDataSize=ret;
|
||||
return rcved;
|
||||
} else {
|
||||
LOG_W(HW,"Dropping late packet\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rcved++;
|
||||
bufferZone+=ret;
|
||||
}
|
||||
|
||||
LOG_D(HW,"Received: blocks: %d/%d, size %d, TS: %lu\n",
|
||||
rcved, bufOrigin->nbBlocks, ret, bufOrigin->timestamp);
|
||||
} while ( rcved == 0 || rcved < bufOrigin->nbBlocks );
|
||||
|
||||
return rcved;
|
||||
}
|
||||
|
||||
int sendSubFrame(UDPsock_t *sock, void *bufferZone, ssize_t secondHeaderSize, uint16_t contentType) {
|
||||
commonUDP_t *UDPheader=(commonUDP_t *)bufferZone ;
|
||||
UDPheader->contentType=contentType;
|
||||
UDPheader->senderClock=rdtsc();
|
||||
int nbBlocks=UDPheader->nbBlocks;
|
||||
int blockId=0;
|
||||
|
||||
if (nbBlocks <= 0 ) {
|
||||
LOG_E(PHY,"FS6: can't send blocks: %d\n", nbBlocks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (blockId > 0 ) {
|
||||
commonUDP_t *currentHeader=(commonUDP_t *)bufferZone;
|
||||
currentHeader->timestamp=UDPheader->timestamp;
|
||||
currentHeader->nbBlocks=UDPheader->nbBlocks;
|
||||
currentHeader->blockID=blockId;
|
||||
currentHeader->contentType=UDPheader->contentType;
|
||||
memcpy(commonUDPdata((void *)currentHeader), commonUDPdata(bufferZone), secondHeaderSize);
|
||||
}
|
||||
|
||||
blockId++;
|
||||
int sz=alignedSize(bufferZone);
|
||||
// Let's use the first address returned by getaddrinfo()
|
||||
int ret=sendto(sock->sockHandler, bufferZone, sz, 0,
|
||||
sock->destAddr->ai_addr, sock->destAddr->ai_addrlen);
|
||||
|
||||
if ( ret != sz )
|
||||
LOG_W(HW,"Wrote socket doesn't return size %d (val: %d, errno:%d, %s)\n",
|
||||
sz, ret, errno, strerror(errno));
|
||||
|
||||
LOG_D(HW,"Sent: TS: %lu, blocks %d/%d, block size : %d \n",
|
||||
UDPheader->timestamp, UDPheader->nbBlocks-nbBlocks, UDPheader->nbBlocks, sz);
|
||||
bufferZone+=sz;
|
||||
nbBlocks--;
|
||||
} while (nbBlocks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/* Lookup tables for 3GPP scrambling/unscrambling */
|
||||
|
||||
/* Author R. Knopp / EURECOM / OpenAirInterface.org */
|
||||
#ifndef __SCRAMBLING_LUTS__C__
|
||||
#define __SCRAMBLING_LUTS__C__
|
||||
|
||||
#include "PHY/impl_defs_nr.h"
|
||||
#include "PHY/sse_intrin.h"
|
||||
|
||||
__m64 byte2m64_re[256];
|
||||
__m64 byte2m64_im[256];
|
||||
|
||||
void init_byte2m64(void) {
|
||||
|
||||
for (int s=0;s<256;s++) {
|
||||
byte2m64_re[s] = _mm_insert_pi16(byte2m64_re[s],(1-2*(s&1)),0);
|
||||
byte2m64_im[s] = _mm_insert_pi16(byte2m64_im[s],(1-2*((s>>1)&1)),0);
|
||||
byte2m64_re[s] = _mm_insert_pi16(byte2m64_re[s],(1-2*((s>>2)&1)),1);
|
||||
byte2m64_im[s] = _mm_insert_pi16(byte2m64_im[s],(1-2*((s>>3)&1)),1);
|
||||
byte2m64_re[s] = _mm_insert_pi16(byte2m64_re[s],(1-2*((s>>4)&1)),2);
|
||||
byte2m64_im[s] = _mm_insert_pi16(byte2m64_im[s],(1-2*((s>>5)&1)),2);
|
||||
byte2m64_re[s] = _mm_insert_pi16(byte2m64_re[s],(1-2*((s>>6)&1)),3);
|
||||
byte2m64_im[s] = _mm_insert_pi16(byte2m64_im[s],(1-2*((s>>7)&1)),3);
|
||||
printf("init_scrambling_luts: s %x (%d) ((%d,%d),(%d,%d),(%d,%d),(%d,%d))\n",
|
||||
((uint16_t*)&s)[0],
|
||||
(1-2*(s&1)),
|
||||
((int16_t*)&byte2m64_re[s])[0],((int16_t*)&byte2m64_im[s])[0],
|
||||
((int16_t*)&byte2m64_re[s])[1],((int16_t*)&byte2m64_im[s])[1],
|
||||
((int16_t*)&byte2m64_re[s])[2],((int16_t*)&byte2m64_im[s])[2],
|
||||
((int16_t*)&byte2m64_re[s])[3],((int16_t*)&byte2m64_im[s])[3]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void init_scrambling_luts(void) {
|
||||
|
||||
init_byte2m64();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file PHY/NR_TRANSPORT/nr_dci_tools_common.c
|
||||
* \brief
|
||||
* \author
|
||||
* \date 2020
|
||||
* \version 0.1
|
||||
* \company Eurecom
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include "nr_dci.h"
|
||||
|
||||
void nr_group_sequence_hopping (pucch_GroupHopping_t PUCCH_GroupHopping,
|
||||
uint32_t n_id,
|
||||
uint8_t n_hop,
|
||||
int nr_tti_tx,
|
||||
uint8_t *u,
|
||||
uint8_t *v) {
|
||||
/*
|
||||
* Implements TS 38.211 subclause 6.3.2.2.1 Group and sequence hopping
|
||||
* The following variables are set by higher layers:
|
||||
* - PUCCH_GroupHopping:
|
||||
* - n_id: higher-layer parameter hoppingId
|
||||
* - n_hop: frequency hopping index
|
||||
* if intra-slot frequency hopping is disabled by the higher-layer parameter PUCCH-frequency-hopping
|
||||
* n_hop=0
|
||||
* if frequency hopping is enabled by the higher-layer parameter PUCCH-frequency-hopping
|
||||
* n_hop=0 for the first hop
|
||||
* n_hop=1 for the second hop
|
||||
*/
|
||||
// depending on the value of the PUCCH_GroupHopping, we will obtain different values for u,v
|
||||
//pucch_GroupHopping_t PUCCH_GroupHopping = ue->pucch_config_common_nr->pucch_GroupHopping; // from higher layers FIXME!!!
|
||||
// n_id defined as per TS 38.211 subclause 6.3.2.2.1 (is given by the higher-layer parameter hoppingId)
|
||||
// it is hoppingId from PUCCH-ConfigCommon:
|
||||
// Cell-Specific scrambling ID for group hoppping and sequence hopping if enabled
|
||||
// Corresponds to L1 parameter 'HoppingID' (see 38.211, section 6.3.2.2) BIT STRING (SIZE (10))
|
||||
//uint16_t n_id = ue->pucch_config_common_nr->hoppingId; // from higher layers FIXME!!!
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("\t\t [nr_group_sequence_hopping] PUCCH_GroupHopping=%u, n_id=%u \n",PUCCH_GroupHopping,n_id);
|
||||
#endif
|
||||
uint8_t f_ss=0,f_gh=0;
|
||||
*u=0;
|
||||
*v=0;
|
||||
uint32_t c_init = 0;
|
||||
uint32_t x1,s; // TS 38.211 Subclause 5.2.1
|
||||
int l = 32, minShift = ((2*nr_tti_tx+n_hop)<<3);
|
||||
int tmpShift =0;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("\t\t [nr_group_sequence_hopping] calculating u,v -> ");
|
||||
#endif
|
||||
|
||||
if (PUCCH_GroupHopping == neither) { // PUCCH_GroupHopping 'neither'
|
||||
f_ss = n_id%30;
|
||||
}
|
||||
|
||||
if (PUCCH_GroupHopping == enable) { // PUCCH_GroupHopping 'enabled'
|
||||
c_init = floor(n_id/30); // we initialize c_init to calculate u,v according to 6.3.2.2.1 of 38.211
|
||||
s = lte_gold_generic(&x1, &c_init, 1); // TS 38.211 Subclause 5.2.1
|
||||
for (int m=0; m<8; m++) {
|
||||
while(minShift >= l) {
|
||||
s = lte_gold_generic(&x1, &c_init, 0);
|
||||
l = l+32;
|
||||
}
|
||||
|
||||
tmpShift = (minShift&((1<<5)-1)); //minShift%32;
|
||||
f_gh = f_gh + ((1<<m)*((uint8_t)((s>>tmpShift)&1)));
|
||||
minShift ++;
|
||||
}
|
||||
|
||||
f_gh = f_gh%30;
|
||||
f_ss = n_id%30;
|
||||
/* for (int m=0; m<8; m++){
|
||||
f_gh = f_gh + ((1<<m)*((uint8_t)((s>>(8*(2*nr_tti_tx+n_hop)+m))&1))); // Not sure we have to use nr_tti_tx FIXME!!!
|
||||
}
|
||||
f_gh = f_gh%30;
|
||||
f_ss = n_id%30;*/
|
||||
}
|
||||
|
||||
if (PUCCH_GroupHopping == disable) { // PUCCH_GroupHopping 'disabled'
|
||||
c_init = (1<<5)*floor(n_id/30)+(n_id%30); // we initialize c_init to calculate u,v
|
||||
s = lte_gold_generic(&x1, &c_init, 1); // TS 38.211 Subclause 5.2.1
|
||||
f_ss = n_id%30;
|
||||
l = 32, minShift = (2*nr_tti_tx+n_hop);
|
||||
|
||||
while(minShift >= l) {
|
||||
s = lte_gold_generic(&x1, &c_init, 0);
|
||||
l = l+32;
|
||||
}
|
||||
|
||||
tmpShift = (minShift&((1<<5)-1)); //minShift%32;
|
||||
*v = (uint8_t)((s>>tmpShift)&1);
|
||||
// *v = (uint8_t)((s>>(2*nr_tti_tx+n_hop))&1); // Not sure we have to use nr_tti_tx FIXME!!!
|
||||
}
|
||||
|
||||
*u = (f_gh+f_ss)%30;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("%d,%d\n",*u,*v);
|
||||
#endif
|
||||
}
|
||||
|
||||
double nr_cyclic_shift_hopping(uint32_t n_id,
|
||||
uint8_t m0,
|
||||
uint8_t mcs,
|
||||
uint8_t lnormal,
|
||||
uint8_t lprime,
|
||||
int nr_tti_tx) {
|
||||
/*
|
||||
* Implements TS 38.211 subclause 6.3.2.2.2 Cyclic shift hopping
|
||||
* - n_id: higher-layer parameter hoppingId
|
||||
* - m0: provided by higher layer parameter PUCCH-F0-F1-initial-cyclic-shift of PUCCH-F0-resource-config
|
||||
* - mcs: mcs=0 except for PUCCH format 0 when it depends on information to be transmitted according to TS 38.213 subclause 9.2
|
||||
* - lnormal: lnormal is the OFDM symbol number in the PUCCH transmission where l=0 corresponds to the first OFDM symbol of the PUCCH transmission
|
||||
* - lprime: lprime is the index of the OFDM symbol in the slot that corresponds to the first OFDM symbol of the PUCCH transmission in the slot given by [5, TS 38.213]
|
||||
*/
|
||||
// alpha_init initialized to 2*PI/12=0.5235987756
|
||||
double alpha = 0.5235987756;
|
||||
uint32_t c_init = n_id; // we initialize c_init again to calculate n_cs
|
||||
|
||||
uint32_t x1,s = lte_gold_generic(&x1, &c_init, 1); // TS 38.211 Subclause 5.2.1
|
||||
uint8_t n_cs=0;
|
||||
int l = 32, minShift = (14*8*nr_tti_tx )+ 8*(lnormal+lprime);
|
||||
int tmpShift =0;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("\t\t [nr_cyclic_shift_hopping] calculating alpha (cyclic shift) using c_init=%u -> \n",c_init);
|
||||
#endif
|
||||
|
||||
for (int m=0; m<8; m++) {
|
||||
while(minShift >= l) {
|
||||
s = lte_gold_generic(&x1, &c_init, 0);
|
||||
l = l+32;
|
||||
}
|
||||
|
||||
tmpShift = (minShift&((1<<5)-1)); //minShift%32;
|
||||
minShift ++;
|
||||
n_cs = n_cs+((1<<m)*((uint8_t)((s>>tmpShift)&1)));
|
||||
// calculating n_cs (Not sure we have to use nr_tti_tx FIXME!!!)
|
||||
// n_cs = n_cs+((1<<m)*((uint8_t)((s>>((14*8*nr_tti_tx) + 8*(lnormal+lprime) + m))&1)));
|
||||
}
|
||||
|
||||
alpha = (alpha * (double)((m0+mcs+n_cs)%12));
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
printf("n_cs=%d -> %lf\n",n_cs,alpha);
|
||||
#endif
|
||||
return(alpha);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file openair1/PHY/CODING/coding_nr_load.c
|
||||
* \brief: load library implementing coding/decoding algorithms
|
||||
* \author Francois TABURET
|
||||
* \date 2020
|
||||
* \version 0.1
|
||||
* \company NOKIA BellLabs France
|
||||
* \email: francois.taburet@nokia-bell-labs.com
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include "assertions.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#define OAIDFTS_LOADER
|
||||
#include "tools_defs.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
|
||||
|
||||
/* function description array, to be used when loading the dfts/idfts lib */
|
||||
static loader_shlibfunc_t shlib_fdesc[2];
|
||||
static char *arg[64]={"phytest","-O","cmdlineonly::dbgl0"};
|
||||
|
||||
|
||||
int load_dftslib(void) {
|
||||
|
||||
char *ptr = (char*)config_get_if();
|
||||
if ( ptr==NULL ) {// phy simulators, config module possibly not loaded
|
||||
load_configmodule(3,(char **)arg,CONFIG_ENABLECMDLINEONLY) ;
|
||||
logInit();
|
||||
}
|
||||
shlib_fdesc[0].fname = "dft";
|
||||
shlib_fdesc[1].fname = "idft";
|
||||
int ret=load_module_shlib("dfts",shlib_fdesc,sizeof(shlib_fdesc)/sizeof(loader_shlibfunc_t),NULL);
|
||||
AssertFatal( (ret >= 0),"Error loading dftsc decoder");
|
||||
dft = (dftfunc_t)shlib_fdesc[0].fptr;
|
||||
idft = (idftfunc_t)shlib_fdesc[1].fptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
RLC AM
|
||||
======
|
||||
|
||||
- 36.322 5.4 Re-establishment procedure
|
||||
when possible, reassemble RLC SDUs from any byte segments of AMD PDUs
|
||||
with SN < VR(MR) in the receiving side, remove RLC headers when doing
|
||||
so and deliver all reassembled RLC SDUs to upper layer in ascending order
|
||||
of the RLC SN, if not delivered before;
|
||||
|
||||
- 36.322 5.2.3 Status reporting
|
||||
delay triggering the STATUS report until x < VR(MS) or x >= VR(MR)
|
||||
|
||||
- 36.322 5.1.3.2.3 Actions when a RLC data PDU is placed in the reception
|
||||
buffer
|
||||
[...] and in-sequence byte segments of the AMD PDU with SN = VR(R) [...]
|
||||
|
||||
- use SOstart/SOend in NACK reporting, do not NACK full PDU if
|
||||
parts of it have been received
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "rlc.h"
|
||||
|
||||
int decode_t_reordering(int v)
|
||||
{
|
||||
static int tab[32] = {
|
||||
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85,
|
||||
90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 1600
|
||||
};
|
||||
|
||||
if (v < 0 || v > 31) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
|
||||
int decode_t_status_prohibit(int v)
|
||||
{
|
||||
static int tab[62] = {
|
||||
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90,
|
||||
95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165,
|
||||
170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240,
|
||||
245, 250, 300, 350, 400, 450, 500, 800, 1000, 1200, 1600, 2000, 2400
|
||||
};
|
||||
|
||||
if (v < 0 || v > 61) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
|
||||
int decode_t_poll_retransmit(int v)
|
||||
{
|
||||
static int tab[59] = {
|
||||
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
|
||||
100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170,
|
||||
175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240, 245,
|
||||
250, 300, 350, 400, 450, 500, 800, 1000, 2000, 4000
|
||||
};
|
||||
|
||||
if (v < 0 || v > 58) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
|
||||
int decode_poll_pdu(int v)
|
||||
{
|
||||
static int tab[8] = {
|
||||
4, 8, 16, 32, 64, 128, 256, -1 /* -1 means infinity */
|
||||
};
|
||||
|
||||
if (v < 0 || v > 7) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
|
||||
int decode_poll_byte(int v)
|
||||
{
|
||||
static int tab[15] = {
|
||||
25, 50, 75, 100, 125, 250, 375, 500, 750, 1000, 1250, 1500, 2000, 3000,
|
||||
-1 /* -1 means infinity */
|
||||
};
|
||||
|
||||
if (v < 0 || v > 14) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (tab[v] == -1) return -1;
|
||||
return tab[v] * 1024;
|
||||
}
|
||||
|
||||
int decode_max_retx_threshold(int v)
|
||||
{
|
||||
static int tab[8] = {
|
||||
1, 2, 3, 4, 6, 8, 16, 32
|
||||
};
|
||||
|
||||
if (v < 0 || v > 7) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
|
||||
int decode_sn_field_length(int v)
|
||||
{
|
||||
static int tab[2] = {
|
||||
5, 10
|
||||
};
|
||||
|
||||
if (v < 0 || v > 1) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tab[v];
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _ASN1_UTILS_H_
|
||||
#define _ASN1_UTILS_H_
|
||||
|
||||
int decode_t_reordering(int v);
|
||||
int decode_t_status_prohibit(int v);
|
||||
int decode_t_poll_retransmit(int v);
|
||||
int decode_poll_pdu(int v);
|
||||
int decode_poll_byte(int v);
|
||||
int decode_max_retx_threshold(int v);
|
||||
int decode_sn_field_length(int v);
|
||||
|
||||
#endif /* _ASN1_UTILS_H_ */
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "rlc_entity.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rlc_entity_am.h"
|
||||
#include "rlc_entity_um.h"
|
||||
|
||||
#include "LOG/log.h"
|
||||
|
||||
rlc_entity_t *new_rlc_entity_am(
|
||||
int rx_maxsize,
|
||||
int tx_maxsize,
|
||||
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
|
||||
char *buf, int size),
|
||||
void *deliver_sdu_data,
|
||||
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
|
||||
struct rlc_entity_t *entity,
|
||||
int sdu_id),
|
||||
void *sdu_successful_delivery_data,
|
||||
void (*max_retx_reached)(void *max_retx_reached_data,
|
||||
struct rlc_entity_t *entity),
|
||||
void *max_retx_reached_data,
|
||||
int t_reordering,
|
||||
int t_status_prohibit,
|
||||
int t_poll_retransmit,
|
||||
int poll_pdu,
|
||||
int poll_byte,
|
||||
int max_retx_threshold)
|
||||
{
|
||||
rlc_entity_am_t *ret;
|
||||
|
||||
ret = calloc(1, sizeof(rlc_entity_am_t));
|
||||
if (ret == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret->common.recv_pdu = rlc_entity_am_recv_pdu;
|
||||
ret->common.buffer_status = rlc_entity_am_buffer_status;
|
||||
ret->common.generate_pdu = rlc_entity_am_generate_pdu;
|
||||
|
||||
ret->common.recv_sdu = rlc_entity_am_recv_sdu;
|
||||
|
||||
ret->common.set_time = rlc_entity_am_set_time;
|
||||
|
||||
ret->common.discard_sdu = rlc_entity_am_discard_sdu;
|
||||
|
||||
ret->common.reestablishment = rlc_entity_am_reestablishment;
|
||||
|
||||
ret->common.delete = rlc_entity_am_delete;
|
||||
|
||||
ret->common.deliver_sdu = deliver_sdu;
|
||||
ret->common.deliver_sdu_data = deliver_sdu_data;
|
||||
|
||||
ret->common.sdu_successful_delivery = sdu_successful_delivery;
|
||||
ret->common.sdu_successful_delivery_data = sdu_successful_delivery_data;
|
||||
|
||||
ret->common.max_retx_reached = max_retx_reached;
|
||||
ret->common.max_retx_reached_data = max_retx_reached_data;
|
||||
|
||||
ret->rx_maxsize = rx_maxsize;
|
||||
ret->tx_maxsize = tx_maxsize;
|
||||
ret->t_reordering = t_reordering;
|
||||
ret->t_status_prohibit = t_status_prohibit;
|
||||
ret->t_poll_retransmit = t_poll_retransmit;
|
||||
ret->poll_pdu = poll_pdu;
|
||||
ret->poll_byte = poll_byte;
|
||||
ret->max_retx_threshold = max_retx_threshold;
|
||||
|
||||
return (rlc_entity_t *)ret;
|
||||
}
|
||||
|
||||
rlc_entity_t *new_rlc_entity_um(
|
||||
int rx_maxsize,
|
||||
int tx_maxsize,
|
||||
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
|
||||
char *buf, int size),
|
||||
void *deliver_sdu_data,
|
||||
int t_reordering,
|
||||
int sn_field_length)
|
||||
{
|
||||
rlc_entity_um_t *ret;
|
||||
|
||||
ret = calloc(1, sizeof(rlc_entity_um_t));
|
||||
if (ret == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret->common.recv_pdu = rlc_entity_um_recv_pdu;
|
||||
ret->common.buffer_status = rlc_entity_um_buffer_status;
|
||||
ret->common.generate_pdu = rlc_entity_um_generate_pdu;
|
||||
|
||||
ret->common.recv_sdu = rlc_entity_um_recv_sdu;
|
||||
|
||||
ret->common.set_time = rlc_entity_um_set_time;
|
||||
|
||||
ret->common.discard_sdu = rlc_entity_um_discard_sdu;
|
||||
|
||||
ret->common.reestablishment = rlc_entity_um_reestablishment;
|
||||
|
||||
ret->common.delete = rlc_entity_um_delete;
|
||||
|
||||
ret->common.deliver_sdu = deliver_sdu;
|
||||
ret->common.deliver_sdu_data = deliver_sdu_data;
|
||||
|
||||
ret->sn_field_length = sn_field_length;
|
||||
ret->rx_maxsize = rx_maxsize;
|
||||
ret->tx_maxsize = tx_maxsize;
|
||||
ret->t_reordering = t_reordering;
|
||||
|
||||
if (sn_field_length == 5)
|
||||
ret->sn_modulus = 32;
|
||||
else if (sn_field_length == 10)
|
||||
ret->sn_modulus = 1024;
|
||||
else {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
ret->window_size = ret->sn_modulus / 2;
|
||||
|
||||
return (rlc_entity_t *)ret;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_ENTITY_H_
|
||||
#define _RLC_ENTITY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SDU_MAX 16000 /* maximum PDCP SDU size is 8188, let's take more */
|
||||
|
||||
typedef struct {
|
||||
int status_size;
|
||||
int tx_size;
|
||||
int retx_size;
|
||||
} rlc_entity_buffer_status_t;
|
||||
|
||||
typedef struct rlc_entity_t {
|
||||
/* functions provided by the RLC module */
|
||||
void (*recv_pdu)(struct rlc_entity_t *entity, char *buffer, int size);
|
||||
rlc_entity_buffer_status_t (*buffer_status)(
|
||||
struct rlc_entity_t *entity, int maxsize);
|
||||
int (*generate_pdu)(struct rlc_entity_t *entity, char *buffer, int size);
|
||||
|
||||
void (*recv_sdu)(struct rlc_entity_t *entity, char *buffer, int size,
|
||||
int sdu_id);
|
||||
|
||||
void (*set_time)(struct rlc_entity_t *entity, uint64_t now);
|
||||
|
||||
void (*discard_sdu)(struct rlc_entity_t *entity, int sdu_id);
|
||||
|
||||
void (*reestablishment)(struct rlc_entity_t *entity);
|
||||
|
||||
void (*delete)(struct rlc_entity_t *entity);
|
||||
|
||||
/* callbacks provided to the RLC module */
|
||||
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
|
||||
char *buf, int size);
|
||||
void *deliver_sdu_data;
|
||||
|
||||
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
|
||||
struct rlc_entity_t *entity,
|
||||
int sdu_id);
|
||||
void *sdu_successful_delivery_data;
|
||||
|
||||
void (*max_retx_reached)(void *max_retx_reached_data,
|
||||
struct rlc_entity_t *entity);
|
||||
void *max_retx_reached_data;
|
||||
} rlc_entity_t;
|
||||
|
||||
rlc_entity_t *new_rlc_entity_am(
|
||||
int rx_maxsize,
|
||||
int tx_maxsize,
|
||||
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
|
||||
char *buf, int size),
|
||||
void *deliver_sdu_data,
|
||||
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
|
||||
struct rlc_entity_t *entity,
|
||||
int sdu_id),
|
||||
void *sdu_successful_delivery_data,
|
||||
void (*max_retx_reached)(void *max_retx_reached_data,
|
||||
struct rlc_entity_t *entity),
|
||||
void *max_retx_reached_data,
|
||||
int t_reordering,
|
||||
int t_status_prohibit,
|
||||
int t_poll_retransmit,
|
||||
int poll_pdu,
|
||||
int poll_byte,
|
||||
int max_retx_threshold);
|
||||
|
||||
rlc_entity_t *new_rlc_entity_um(
|
||||
int rx_maxsize,
|
||||
int tx_maxsize,
|
||||
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
|
||||
char *buf, int size),
|
||||
void *deliver_sdu_data,
|
||||
int t_reordering,
|
||||
int sn_field_length);
|
||||
|
||||
#endif /* _RLC_ENTITY_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_ENTITY_AM_H_
|
||||
#define _RLC_ENTITY_AM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rlc_entity.h"
|
||||
#include "rlc_pdu.h"
|
||||
#include "rlc_sdu.h"
|
||||
|
||||
/*
|
||||
* Here comes some documentation to understand the reassembly
|
||||
* logic in the code and the fields in the structure rlc_am_reassemble_t.
|
||||
*
|
||||
* Inside RLC, we deal with SDUs, PDUs and PDU segments.
|
||||
* SDUs are packets coming from upper layer.
|
||||
* A PDU is made of a header and a payload.
|
||||
* In the payload there are SDUs.
|
||||
* First SDU and last SDU in a PDU may be incomplete.
|
||||
* PDU segments exist in case of retransmissions when the MAC
|
||||
* layer asks for less data than previously, in which case
|
||||
* only part of the previous PDU is sent.
|
||||
*
|
||||
* This is PDU data (just bytes):
|
||||
* ---------------------------------------------------------
|
||||
* | PDU data |
|
||||
* ---------------------------------------------------------
|
||||
* It contains SDUs, like:
|
||||
* ---------------------------------------------------------
|
||||
* | SDU 1 | SDU 2 | [...] | SDU n |
|
||||
* ---------------------------------------------------------
|
||||
* SDU 1 may be only the end of an SDU from which previous bytes were
|
||||
* transmitted in previous PDUs.
|
||||
* SDU n may be only the start of an SDU, that is more bytes from
|
||||
* this SDU may be sent in successive PDUs.
|
||||
*
|
||||
* At front of the PDU data, we have a header:
|
||||
* --------------- ---------------------------------------------------------
|
||||
* | PDU header | | SDU 1 | SDU 2 | [...] | SDU n |
|
||||
* --------------- ---------------------------------------------------------
|
||||
* PDU header describes PDU data (most notably lengths).
|
||||
*
|
||||
* A PDU segment is a part of a PDU. For example, from this PDU data:
|
||||
* ---------------------------------------------------------
|
||||
* | SDU 1 | SDU 2 | [...] | SDU n |
|
||||
* ---------------------------------------------------------
|
||||
* We can extract the following PDU segment (data part only):
|
||||
* ----------------------
|
||||
* | PDU segment data |
|
||||
* ----------------------
|
||||
* This PDU segment would contain the end of SDU 2 above and some SDUs up to,
|
||||
* let's say SDU x (x is 5 below).
|
||||
*
|
||||
* In front of a transmitted PDU segment, we have a header,
|
||||
* containing the important variable 'so' (segment offset) that gives
|
||||
* the index of the first byte of the segment in the original PDU.
|
||||
* -------------- ----------------------
|
||||
* | seg. header| | PDU segment data |
|
||||
* -------------- ----------------------
|
||||
*
|
||||
* Let's now explain the data structure rlc_am_reassemble_t.
|
||||
*
|
||||
* In the structure rlc_am_reassemble_t, the fields fi, e, sn and so
|
||||
* are coming from the PDU segment header and the semantics is the
|
||||
* one of the RLC specs.
|
||||
*
|
||||
* The currently processed PDU segment is stored in 'start'.
|
||||
* We have 'start->s->data_offset' and 'start->s->size'.
|
||||
* start->s->data_offset is the index of the start of the data in the
|
||||
* PDU segment. That is if the header is of length 3 bytes
|
||||
* then start->s->data_offset is 3.
|
||||
* start->s->size is the total length of the PDU segment,
|
||||
* including header.
|
||||
* The size of actual data bytes in the PDU segment is thus
|
||||
* start->s->size - start->s->data_offset.
|
||||
*
|
||||
* The field sdu_len is the length of the current SDU being
|
||||
* processed.
|
||||
*
|
||||
* The field sdu_offset is the starting point of the
|
||||
* current SDU being processed (starting from beginning
|
||||
* of PDU segment, including header).
|
||||
*
|
||||
* The field data_pos is the current read pointer. 0 points to
|
||||
* the beginning of the PDU segment (including header).
|
||||
*
|
||||
* The field pdu_byte points to the current byte in the original
|
||||
* PDU (not the PDU segment). It starts at 0 when we start
|
||||
* processing a new PDU (when a new 'sn' is seen) and always
|
||||
* increases after each byte processed. This is tha variable
|
||||
* that is used to know if the next PDU segment will be used
|
||||
* or not and if yes, starting from which data byte (see
|
||||
* function rlc_am_reassemble_next_segment).
|
||||
*
|
||||
* 'so' is important and points to the byte in the original PDU
|
||||
* that is the first byte of the PDU segment.
|
||||
*
|
||||
* For example, let's take this PDU segment data from above:
|
||||
* ----------------------
|
||||
* | PDU segment data |
|
||||
* ----------------------
|
||||
* Let's say it is decomposed as:
|
||||
* ----------------------
|
||||
* |222|33|4444|55555555|
|
||||
* ----------------------
|
||||
* It contains SDUs 2, 3, 4, and 5.
|
||||
* SDU 2 is 3 bytes, SDU 3 is 2 bytes, SDU 4 is 4 bytes, SDU 5 is 8 bytes.
|
||||
*
|
||||
* Let's suppose that the original PDU starts with:
|
||||
* ----------------
|
||||
* |1111111|222222|
|
||||
* ----------------
|
||||
*
|
||||
* (In this example, in the PDU segment, SDU 2 is not full,
|
||||
* we only have its end.)
|
||||
*
|
||||
* Then 'so' is 13 (SDU 1 is 7 bytes, head of SDU 2 is 6 bytes).
|
||||
*
|
||||
* Let's continue with our PDU segment data.
|
||||
* Let's say we are current processing SDU 4.
|
||||
* Let's say the read pointer (variable 'data_pos') is there:
|
||||
* ----------------------
|
||||
* |222|33|4444|55555555|
|
||||
* ----------------------
|
||||
* ^
|
||||
* read pointer (data_pos)
|
||||
*
|
||||
* Then:
|
||||
* - sdu_len is 4
|
||||
* - sdu_offset is 5 + [PDU segment header length]
|
||||
* (it points to the beginning of SDU 4, starting
|
||||
* from the head of the PDU segment, that is
|
||||
* 3 bytes for SDU 2, 2 bytes for SDU 3, and the
|
||||
* PDU segment header length)
|
||||
* - start->s->data_offset is [PDU segment header length]
|
||||
* - pdu_byte is 20
|
||||
* (13 bytes from beginning of original PDU,
|
||||
* 3 bytes for SDU 2, 2 bytes for SDU 3, then 2 bytes for SDU 4)
|
||||
* - data_pos = read pointer = 7 + [PDU segment header length]
|
||||
*
|
||||
* To finish this description, in the code, a PDU is simply
|
||||
* seen as a PDU segment with 'so' = 0 (and is_last == 1 (lsf in the specs),
|
||||
* but this variable is not used by the reassembly logic).
|
||||
*
|
||||
* And for [PDU segment header length] we use start->s->data_offset.
|
||||
*
|
||||
* To recap, here is an illustration of the various variables
|
||||
* and what starting point they use. In the figures, the start
|
||||
* of the variable name is aligned to the byte it refers to.
|
||||
* + is used to show the starting point.
|
||||
*
|
||||
* Let's put the PDU segment back into the original PDU.
|
||||
* And let's show the values for when the read pointer
|
||||
* is on the second byte of SDU 4 (as above).
|
||||
*
|
||||
* +++++++++++++++ so
|
||||
* +++++++++++++++++++++++ pdu_byte
|
||||
* ---------------------------------------------------------
|
||||
* | SDU 1| SDU 2..222|33|4444|55555555| [...] | SDU n |
|
||||
* ---------------------------------------------------------
|
||||
*
|
||||
* And now the PDU segment with header.
|
||||
*
|
||||
*
|
||||
* ++++ sdu_len
|
||||
* ++++++++++++++++++++++ sdu_offset
|
||||
* +++++++++++++++++++++++ data_pos
|
||||
* +++++++++++++++ start->s->data_offset
|
||||
* +++++++++++++++++++++++++++++++++++++ start->s->size
|
||||
* -------------- ----------------------
|
||||
* | seg. header| |222|33|4444|55555555|
|
||||
* -------------- ----------------------
|
||||
*
|
||||
* We see three case for the starting point:
|
||||
* - start of original PDU (without any header)
|
||||
* - start of header of current PDU segment
|
||||
* - start of current SDU (for sdu_len)
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
rlc_rx_pdu_segment_t *start; /* start of list */
|
||||
rlc_rx_pdu_segment_t *end; /* end of list (last element) */
|
||||
int pos; /* byte to get from current buffer */
|
||||
char sdu[SDU_MAX]; /* sdu is reassembled here */
|
||||
int sdu_pos; /* next byte to put in sdu */
|
||||
|
||||
/* decoder of current PDU */
|
||||
rlc_pdu_decoder_t dec;
|
||||
int fi;
|
||||
int e;
|
||||
int sn;
|
||||
int so;
|
||||
int sdu_len;
|
||||
int sdu_offset;
|
||||
int data_pos;
|
||||
int pdu_byte;
|
||||
} rlc_am_reassemble_t;
|
||||
|
||||
typedef struct {
|
||||
rlc_entity_t common;
|
||||
|
||||
/* configuration */
|
||||
int t_reordering;
|
||||
int t_status_prohibit;
|
||||
int t_poll_retransmit;
|
||||
int poll_pdu; /* -1 means infinity */
|
||||
int poll_byte; /* -1 means infinity */
|
||||
int max_retx_threshold;
|
||||
|
||||
/* runtime rx */
|
||||
int vr_r;
|
||||
int vr_x;
|
||||
int vr_ms;
|
||||
int vr_h;
|
||||
|
||||
int status_triggered;
|
||||
|
||||
/* runtime tx */
|
||||
int vt_a;
|
||||
int vt_s;
|
||||
int poll_sn;
|
||||
int pdu_without_poll;
|
||||
int byte_without_poll;
|
||||
int force_poll;
|
||||
|
||||
/* set to the latest know time by the user of the module. Unit: ms */
|
||||
uint64_t t_current;
|
||||
|
||||
/* timers (stores the TTI of activation, 0 means not active) */
|
||||
uint64_t t_reordering_start;
|
||||
uint64_t t_status_prohibit_start;
|
||||
uint64_t t_poll_retransmit_start;
|
||||
|
||||
/* rx management */
|
||||
rlc_rx_pdu_segment_t *rx_list;
|
||||
int rx_size;
|
||||
int rx_maxsize;
|
||||
|
||||
/* reassembly management */
|
||||
rlc_am_reassemble_t reassemble;
|
||||
|
||||
/* tx management */
|
||||
rlc_sdu_t *tx_list;
|
||||
rlc_sdu_t *tx_end;
|
||||
int tx_size;
|
||||
int tx_maxsize;
|
||||
|
||||
rlc_tx_pdu_segment_t *wait_list;
|
||||
rlc_tx_pdu_segment_t *retransmit_list;
|
||||
|
||||
rlc_tx_pdu_segment_t *ack_list;
|
||||
} rlc_entity_am_t;
|
||||
|
||||
void rlc_entity_am_recv_sdu(rlc_entity_t *entity, char *buffer, int size,
|
||||
int sdu_id);
|
||||
void rlc_entity_am_recv_pdu(rlc_entity_t *entity, char *buffer, int size);
|
||||
rlc_entity_buffer_status_t rlc_entity_am_buffer_status(
|
||||
rlc_entity_t *entity, int maxsize);
|
||||
int rlc_entity_am_generate_pdu(rlc_entity_t *entity, char *buffer, int size);
|
||||
void rlc_entity_am_set_time(rlc_entity_t *entity, uint64_t now);
|
||||
void rlc_entity_am_discard_sdu(rlc_entity_t *entity, int sdu_id);
|
||||
void rlc_entity_am_reestablishment(rlc_entity_t *entity);
|
||||
void rlc_entity_am_delete(rlc_entity_t *entity);
|
||||
|
||||
#endif /* _RLC_ENTITY_AM_H_ */
|
||||
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "rlc_entity_um.h"
|
||||
#include "rlc_pdu.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "LOG/log.h"
|
||||
|
||||
/*************************************************************************/
|
||||
/* PDU RX functions */
|
||||
/*************************************************************************/
|
||||
|
||||
static int modulus_rx(rlc_entity_um_t *entity, int a)
|
||||
{
|
||||
/* as per 36.322 7.1, modulus base is vr(uh)-window_size and modulus is
|
||||
* 2^sn_field_length (which is 'sn_modulus' in rlc_entity_um_t)
|
||||
*/
|
||||
int r = a - (entity->vr_uh - entity->window_size);
|
||||
if (r < 0) r += entity->sn_modulus;
|
||||
return r % entity->sn_modulus;
|
||||
}
|
||||
|
||||
static int sn_compare_rx(void *_entity, int a, int b)
|
||||
{
|
||||
rlc_entity_um_t *entity = _entity;
|
||||
return modulus_rx(entity, a) - modulus_rx(entity, b);
|
||||
}
|
||||
|
||||
static int sn_in_recv_window(void *_entity, int sn)
|
||||
{
|
||||
rlc_entity_um_t *entity = _entity;
|
||||
int mod_sn = modulus_rx(entity, sn);
|
||||
/* we simplify (VR(UH) - UM_Window_Size) <= SN < VR(UH), base is
|
||||
* (VR(UH) - UM_Window_Size) and VR(UH) = base + window_size
|
||||
*/
|
||||
return mod_sn < entity->window_size;
|
||||
}
|
||||
|
||||
/* return 1 if a PDU with SN == 'sn' is in the rx list, 0 otherwise */
|
||||
static int rlc_um_pdu_received(rlc_entity_um_t *entity, int sn)
|
||||
{
|
||||
rlc_rx_pdu_segment_t *cur = entity->rx_list;
|
||||
while (cur != NULL) {
|
||||
if (cur->sn == sn)
|
||||
return 1;
|
||||
cur = cur->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int less_than_vr_ur(rlc_entity_um_t *entity, int sn)
|
||||
{
|
||||
return sn_compare_rx(entity, sn, entity->vr_ur) < 0;
|
||||
}
|
||||
|
||||
static int outside_of_reordering_window(rlc_entity_um_t *entity, int sn)
|
||||
{
|
||||
return !sn_in_recv_window(entity, sn);
|
||||
}
|
||||
|
||||
static int less_than_vr_uh(rlc_entity_um_t *entity, int sn)
|
||||
{
|
||||
return sn_compare_rx(entity, sn, entity->vr_uh) < 0;
|
||||
}
|
||||
|
||||
static void rlc_um_reassemble_pdu(rlc_entity_um_t *entity,
|
||||
rlc_rx_pdu_segment_t *pdu)
|
||||
{
|
||||
rlc_um_reassemble_t *r = &entity->reassemble;
|
||||
|
||||
int fi;
|
||||
int e;
|
||||
int sn;
|
||||
int data_pos;
|
||||
int sdu_len;
|
||||
int sdu_offset;
|
||||
|
||||
sdu_offset = pdu->data_offset;
|
||||
|
||||
rlc_pdu_decoder_init(&r->dec, pdu->data, pdu->size);
|
||||
|
||||
if (entity->sn_field_length == 10)
|
||||
rlc_pdu_decoder_get_bits(&r->dec, 3);
|
||||
|
||||
fi = rlc_pdu_decoder_get_bits(&r->dec, 2);
|
||||
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
|
||||
sn = rlc_pdu_decoder_get_bits(&r->dec, entity->sn_field_length);
|
||||
|
||||
if (e) {
|
||||
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
|
||||
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
|
||||
} else
|
||||
sdu_len = pdu->size - sdu_offset;
|
||||
|
||||
/* discard current SDU being reassembled if bad SN or bad FI */
|
||||
if (sn != (r->sn + 1) % entity->sn_modulus ||
|
||||
!(fi & 0x02)) {
|
||||
if (r->sdu_pos)
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard partially reassembled SDU\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
r->sdu_pos = 0;
|
||||
}
|
||||
|
||||
/* if the head of the SDU is missing, still process the PDU
|
||||
* but remember to discard the reassembled SDU later on (the
|
||||
* head has not been received).
|
||||
* The head is missing if sdu_pos == 0 and fi says the PDU does not
|
||||
* start an SDU.
|
||||
*/
|
||||
if (r->sdu_pos == 0 && (fi & 0x02))
|
||||
r->sdu_head_missing = 1;
|
||||
|
||||
r->sn = sn;
|
||||
data_pos = pdu->data_offset;
|
||||
|
||||
while (1) {
|
||||
if (r->sdu_pos >= SDU_MAX) {
|
||||
/* TODO: proper error handling (discard PDUs with current sn from
|
||||
* reassembly queue? something else?)
|
||||
*/
|
||||
LOG_E(RLC, "%s:%d:%s: bad RLC PDU\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
r->sdu[r->sdu_pos] = pdu->data[data_pos];
|
||||
r->sdu_pos++;
|
||||
data_pos++;
|
||||
if (data_pos == sdu_offset + sdu_len) {
|
||||
/* all bytes of SDU are consumed, check if SDU is fully there.
|
||||
* It is if the data pointer is not at the end of the PDU segment
|
||||
* or if 'fi' & 1 == 0
|
||||
*/
|
||||
if (data_pos != pdu->size || (fi & 1) == 0) {
|
||||
/* time to discard the SDU if we didn't receive the head */
|
||||
if (r->sdu_head_missing) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard SDU, head not received\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
r->sdu_head_missing = 0;
|
||||
} else {
|
||||
/* SDU is full - deliver to higher layer */
|
||||
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
|
||||
(rlc_entity_t *)entity,
|
||||
r->sdu, r->sdu_pos);
|
||||
}
|
||||
r->sdu_pos = 0;
|
||||
}
|
||||
/* done with PDU? */
|
||||
if (data_pos == pdu->size)
|
||||
break;
|
||||
/* not at the end of PDU, process next SDU */
|
||||
sdu_offset += sdu_len;
|
||||
if (e) {
|
||||
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
|
||||
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
|
||||
} else
|
||||
sdu_len = pdu->size - sdu_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rlc_um_reassemble(rlc_entity_um_t *entity,
|
||||
int (*check_sn)(rlc_entity_um_t *entity, int sn))
|
||||
{
|
||||
rlc_rx_pdu_segment_t *cur;
|
||||
|
||||
/* process all PDUs from head of rx list until all is processed or
|
||||
* the SN is not valid anymore with respect to 'check_sn'
|
||||
*/
|
||||
while (entity->rx_list != NULL && check_sn(entity, entity->rx_list->sn)) {
|
||||
cur = entity->rx_list;
|
||||
rlc_um_reassemble_pdu(entity, cur);
|
||||
entity->rx_size -= cur->size;
|
||||
entity->rx_list = cur->next;
|
||||
rlc_rx_free_pdu_segment(cur);
|
||||
}
|
||||
}
|
||||
|
||||
static void rlc_um_reception_actions(rlc_entity_um_t *entity,
|
||||
rlc_rx_pdu_segment_t *pdu_segment)
|
||||
{
|
||||
if (!sn_in_recv_window(entity, pdu_segment->sn)) {
|
||||
entity->vr_uh = (pdu_segment->sn + 1) % entity->sn_modulus;
|
||||
rlc_um_reassemble(entity, outside_of_reordering_window);
|
||||
if (!sn_in_recv_window(entity, entity->vr_ur))
|
||||
entity->vr_ur = (entity->vr_uh - entity->window_size
|
||||
+ entity->sn_modulus) % entity->sn_modulus;
|
||||
}
|
||||
|
||||
if (rlc_um_pdu_received(entity, entity->vr_ur)) {
|
||||
do {
|
||||
entity->vr_ur = (entity->vr_ur + 1) % entity->sn_modulus;
|
||||
} while (rlc_um_pdu_received(entity, entity->vr_ur));
|
||||
rlc_um_reassemble(entity, less_than_vr_ur);
|
||||
}
|
||||
|
||||
if (entity->t_reordering_start) {
|
||||
if (sn_compare_rx(entity, entity->vr_ux, entity->vr_ur) <= 0 ||
|
||||
(!sn_in_recv_window(entity, entity->vr_ux) &&
|
||||
entity->vr_ux != entity->vr_uh))
|
||||
entity->t_reordering_start = 0;
|
||||
}
|
||||
|
||||
if (entity->t_reordering_start == 0) {
|
||||
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
|
||||
entity->t_reordering_start = entity->t_current;
|
||||
entity->vr_ux = entity->vr_uh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_entity_um_recv_pdu(rlc_entity_t *_entity, char *buffer, int size)
|
||||
{
|
||||
#define R(d) do { if (rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
rlc_pdu_decoder_t decoder;
|
||||
rlc_pdu_decoder_t data_decoder;
|
||||
|
||||
int e;
|
||||
int sn;
|
||||
|
||||
int data_e;
|
||||
int data_li;
|
||||
|
||||
int packet_count;
|
||||
int data_size;
|
||||
int data_start;
|
||||
int indicated_data_size;
|
||||
|
||||
rlc_rx_pdu_segment_t *pdu_segment;
|
||||
|
||||
rlc_pdu_decoder_init(&decoder, buffer, size);
|
||||
|
||||
if (entity->sn_field_length == 10) {
|
||||
rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder); /* R1 */
|
||||
}
|
||||
|
||||
rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder); /* FI */
|
||||
e = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
|
||||
sn = rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length); R(decoder);
|
||||
|
||||
/* dicard PDU if rx buffer is full */
|
||||
if (entity->rx_size + size > entity->rx_maxsize) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* discard according to 36.322 5.1.2.2.2 */
|
||||
if ((sn_compare_rx(entity, entity->vr_ur, sn) < 0 &&
|
||||
sn_compare_rx(entity, sn, entity->vr_uh) < 0 &&
|
||||
rlc_um_pdu_received(entity, sn)) ||
|
||||
(sn_compare_rx(entity, entity->vr_uh - entity->window_size, sn) <= 0 &&
|
||||
sn_compare_rx(entity, sn, entity->vr_ur) < 0)) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard PDU (sn %d vr(ur) %d vr(uh) %d)\n",
|
||||
__FILE__, __LINE__, __FUNCTION__,
|
||||
sn, entity->vr_ur, entity->vr_uh);
|
||||
return;
|
||||
}
|
||||
|
||||
packet_count = 1;
|
||||
|
||||
/* go to start of data */
|
||||
indicated_data_size = 0;
|
||||
data_decoder = decoder;
|
||||
data_e = e;
|
||||
while (data_e) {
|
||||
data_e = rlc_pdu_decoder_get_bits(&data_decoder, 1); R(data_decoder);
|
||||
data_li = rlc_pdu_decoder_get_bits(&data_decoder, 11); R(data_decoder);
|
||||
if (data_li == 0) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, li == 0\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
indicated_data_size += data_li;
|
||||
packet_count++;
|
||||
}
|
||||
rlc_pdu_decoder_align(&data_decoder);
|
||||
|
||||
data_start = data_decoder.byte;
|
||||
data_size = size - data_start;
|
||||
|
||||
if (data_size <= 0) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, wrong data size (sum of LI %d data size %d)\n",
|
||||
__FILE__, __LINE__, __FUNCTION__,
|
||||
indicated_data_size, data_size);
|
||||
return;
|
||||
}
|
||||
if (indicated_data_size >= data_size) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, bad LIs (sum of LI %d data size %d)\n",
|
||||
__FILE__, __LINE__, __FUNCTION__,
|
||||
indicated_data_size, data_size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* put in pdu reception list */
|
||||
entity->rx_size += size;
|
||||
pdu_segment = rlc_rx_new_pdu_segment(sn, 0, size, 1, buffer, data_start);
|
||||
entity->rx_list = rlc_rx_pdu_segment_list_add(sn_compare_rx, entity,
|
||||
entity->rx_list, pdu_segment);
|
||||
|
||||
/* do reception actions (36.322 5.1.2.2.3) */
|
||||
rlc_um_reception_actions(entity, pdu_segment);
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
LOG_D(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
|
||||
#undef R
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* TX functions */
|
||||
/*************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
int sdu_count;
|
||||
int data_size;
|
||||
int header_size;
|
||||
int last_sdu_is_full;
|
||||
int first_sdu_length;
|
||||
} tx_pdu_size_t;
|
||||
|
||||
static int header_size(int sn_field_length, int sdu_count)
|
||||
{
|
||||
int bits = 8 + 8 * (sn_field_length == 10) + 12 * (sdu_count - 1);
|
||||
/* padding if we have to */
|
||||
return (bits + 7) / 8;
|
||||
}
|
||||
|
||||
static tx_pdu_size_t tx_pdu_size(rlc_entity_um_t *entity, int maxsize)
|
||||
{
|
||||
tx_pdu_size_t ret;
|
||||
int sdu_count;
|
||||
int sdu_size;
|
||||
int pdu_data_size;
|
||||
rlc_sdu_t *sdu;
|
||||
|
||||
ret.sdu_count = 0;
|
||||
ret.data_size = 0;
|
||||
ret.header_size = 0;
|
||||
ret.last_sdu_is_full = 1;
|
||||
ret.first_sdu_length = 0;
|
||||
|
||||
/* TX PDU - let's make the biggest PDU we can with the SDUs we have */
|
||||
sdu_count = 0;
|
||||
pdu_data_size = 0;
|
||||
sdu = entity->tx_list;
|
||||
while (sdu != NULL) {
|
||||
int new_header_size = header_size(entity->sn_field_length, sdu_count+1);
|
||||
/* if we cannot put new header + at least 1 byte of data then over */
|
||||
if (new_header_size + pdu_data_size >= maxsize)
|
||||
break;
|
||||
sdu_count++;
|
||||
/* only include the bytes of this SDU not included in PDUs already */
|
||||
sdu_size = sdu->size - sdu->next_byte;
|
||||
/* don't feed more than 'maxsize' bytes */
|
||||
if (new_header_size + pdu_data_size + sdu_size > maxsize) {
|
||||
sdu_size = maxsize - new_header_size - pdu_data_size;
|
||||
ret.last_sdu_is_full = 0;
|
||||
}
|
||||
if (sdu_count == 1)
|
||||
ret.first_sdu_length = sdu_size;
|
||||
pdu_data_size += sdu_size;
|
||||
/* if we put more than 2^11-1 bytes then the LI field cannot be used,
|
||||
* so this is the last SDU we can put
|
||||
*/
|
||||
if (sdu_size > 2047)
|
||||
break;
|
||||
sdu = sdu->next;
|
||||
}
|
||||
|
||||
if (sdu_count) {
|
||||
ret.sdu_count = sdu_count;
|
||||
ret.data_size = pdu_data_size;
|
||||
ret.header_size = header_size(entity->sn_field_length, sdu_count);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
rlc_entity_buffer_status_t rlc_entity_um_buffer_status(
|
||||
rlc_entity_t *_entity, int maxsize)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
rlc_entity_buffer_status_t ret;
|
||||
tx_pdu_size_t tx_size;
|
||||
|
||||
ret.status_size = 0;
|
||||
|
||||
/* todo: if an SDU has size >2047 in the tx list then processing
|
||||
* stops and computed size will not be accurate. Change the computation
|
||||
* to be more accurate (if needed).
|
||||
*/
|
||||
tx_size = tx_pdu_size(entity, maxsize);
|
||||
ret.tx_size = tx_size.data_size + tx_size.header_size;
|
||||
|
||||
ret.retx_size = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rlc_entity_um_generate_pdu(rlc_entity_t *_entity, char *buffer, int size)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
tx_pdu_size_t pdu_size;
|
||||
rlc_sdu_t *sdu;
|
||||
int i;
|
||||
int cursize;
|
||||
int first_sdu_full;
|
||||
int last_sdu_full;
|
||||
rlc_pdu_encoder_t encoder;
|
||||
int fi;
|
||||
int e;
|
||||
int li;
|
||||
char *out;
|
||||
int outpos;
|
||||
int first_sdu_start_byte;
|
||||
int sdu_start_byte;
|
||||
|
||||
pdu_size = tx_pdu_size(entity, size);
|
||||
if (pdu_size.sdu_count == 0)
|
||||
return 0;
|
||||
|
||||
sdu = entity->tx_list;
|
||||
|
||||
first_sdu_start_byte = sdu->next_byte;
|
||||
|
||||
/* reserve SDU bytes */
|
||||
cursize = 0;
|
||||
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
|
||||
int sdu_size = sdu->size - sdu->next_byte;
|
||||
if (cursize + sdu_size > pdu_size.data_size)
|
||||
sdu_size = pdu_size.data_size - cursize;
|
||||
sdu->next_byte += sdu_size;
|
||||
cursize += sdu_size;
|
||||
}
|
||||
|
||||
first_sdu_full = first_sdu_start_byte == 0;
|
||||
last_sdu_full = pdu_size.last_sdu_is_full;
|
||||
|
||||
/* generate header */
|
||||
rlc_pdu_encoder_init(&encoder, buffer, size);
|
||||
|
||||
if (entity->sn_field_length == 10)
|
||||
rlc_pdu_encoder_put_bits(&encoder, 0, 3); /* R1 */
|
||||
|
||||
fi = 0;
|
||||
if (!first_sdu_full)
|
||||
fi |= 0x02;
|
||||
if (!last_sdu_full)
|
||||
fi |= 0x01;
|
||||
rlc_pdu_encoder_put_bits(&encoder, fi, 2); /* FI */
|
||||
|
||||
/* see the AM code to understand the logic for Es and LIs */
|
||||
if (pdu_size.sdu_count >= 2)
|
||||
e = 1;
|
||||
else
|
||||
e = 0;
|
||||
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
|
||||
|
||||
if (entity->sn_field_length == 10)
|
||||
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 10); /* SN */
|
||||
else
|
||||
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 5); /* SN */
|
||||
|
||||
/* put LIs */
|
||||
sdu = entity->tx_list;
|
||||
/* first SDU */
|
||||
li = pdu_size.first_sdu_length;
|
||||
/* put E+LI only if at least 2 SDUs */
|
||||
if (pdu_size.sdu_count >= 2) {
|
||||
/* E is 1 if at least 3 SDUs */
|
||||
if (pdu_size.sdu_count >= 3)
|
||||
e = 1;
|
||||
else
|
||||
e = 0;
|
||||
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
|
||||
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
|
||||
}
|
||||
/* next SDUs, but not the last (no LI for the last) */
|
||||
sdu = sdu->next;
|
||||
for (i = 2; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
|
||||
if (i != pdu_size.sdu_count - 1)
|
||||
e = 1;
|
||||
else
|
||||
e = 0;
|
||||
li = sdu->size;
|
||||
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
|
||||
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
|
||||
}
|
||||
|
||||
rlc_pdu_encoder_align(&encoder);
|
||||
|
||||
/* generate data */
|
||||
out = buffer + pdu_size.header_size;
|
||||
sdu = entity->tx_list;
|
||||
sdu_start_byte = first_sdu_start_byte;
|
||||
outpos = 0;
|
||||
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
|
||||
li = sdu->size - sdu_start_byte;
|
||||
if (outpos + li >= pdu_size.data_size)
|
||||
li = pdu_size.data_size - outpos;
|
||||
memcpy(out+outpos, sdu->data + sdu_start_byte, li);
|
||||
outpos += li;
|
||||
sdu_start_byte = 0;
|
||||
}
|
||||
|
||||
/* cleanup sdu list */
|
||||
while (entity->tx_list != NULL &&
|
||||
entity->tx_list->size == entity->tx_list->next_byte) {
|
||||
rlc_sdu_t *c = entity->tx_list;
|
||||
/* release SDU bytes */
|
||||
entity->tx_size -= c->size;
|
||||
entity->tx_list = c->next;
|
||||
rlc_free_sdu(c);
|
||||
}
|
||||
if (entity->tx_list == NULL)
|
||||
entity->tx_end = NULL;
|
||||
|
||||
/* update VT(US) */
|
||||
entity->vt_us = (entity->vt_us + 1) % entity->sn_modulus;
|
||||
|
||||
return pdu_size.header_size + pdu_size.data_size;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* SDU RX functions */
|
||||
/*************************************************************************/
|
||||
|
||||
void rlc_entity_um_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
|
||||
int sdu_id)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
rlc_sdu_t *sdu;
|
||||
|
||||
if (size > SDU_MAX) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
|
||||
__FILE__, __LINE__, __FUNCTION__, size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (entity->tx_size + size > entity->tx_maxsize) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
entity->tx_size += size;
|
||||
|
||||
sdu = rlc_new_sdu(buffer, size, sdu_id);
|
||||
rlc_sdu_list_add(&entity->tx_list, &entity->tx_end, sdu);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* time/timers */
|
||||
/*************************************************************************/
|
||||
|
||||
static void check_t_reordering(rlc_entity_um_t *entity)
|
||||
{
|
||||
int sn;
|
||||
|
||||
/* is t_reordering running and if yes has it expired? */
|
||||
if (entity->t_reordering_start == 0 ||
|
||||
entity->t_current <= entity->t_reordering_start + entity->t_reordering)
|
||||
return;
|
||||
|
||||
/* stop timer */
|
||||
entity->t_reordering_start = 0;
|
||||
|
||||
LOG_D(RLC, "%s:%d:%s: t_reordering expired\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
|
||||
/* update VR(UR) to first SN >= VR(UX) of PDU not received
|
||||
*/
|
||||
sn = entity->vr_ux;
|
||||
while (rlc_um_pdu_received(entity, sn))
|
||||
sn = (sn + 1) % entity->sn_modulus;
|
||||
entity->vr_ur = sn;
|
||||
|
||||
rlc_um_reassemble(entity, less_than_vr_ur);
|
||||
|
||||
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
|
||||
entity->t_reordering_start = entity->t_current;
|
||||
entity->vr_ux = entity->vr_uh;
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_entity_um_set_time(rlc_entity_t *_entity, uint64_t now)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
|
||||
entity->t_current = now;
|
||||
|
||||
check_t_reordering(entity);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* discard/re-establishment/delete */
|
||||
/*************************************************************************/
|
||||
|
||||
void rlc_entity_um_discard_sdu(rlc_entity_t *_entity, int sdu_id)
|
||||
{
|
||||
/* implements 36.322 5.3 */
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
rlc_sdu_t head;
|
||||
rlc_sdu_t *cur;
|
||||
rlc_sdu_t *prev;
|
||||
|
||||
head.next = entity->tx_list;
|
||||
cur = entity->tx_list;
|
||||
prev = &head;
|
||||
|
||||
while (cur != NULL && cur->upper_layer_id != sdu_id) {
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
/* if sdu_id not found or some bytes have already been 'PDU-ized'
|
||||
* then do nothing
|
||||
*/
|
||||
if (cur == NULL || cur->next_byte != 0)
|
||||
return;
|
||||
|
||||
/* remove SDU from tx_list */
|
||||
prev->next = cur->next;
|
||||
entity->tx_list = head.next;
|
||||
if (entity->tx_end == cur) {
|
||||
if (prev != &head)
|
||||
entity->tx_end = prev;
|
||||
else
|
||||
entity->tx_end = NULL;
|
||||
}
|
||||
|
||||
rlc_free_sdu(cur);
|
||||
}
|
||||
|
||||
static void clear_entity(rlc_entity_um_t *entity)
|
||||
{
|
||||
rlc_rx_pdu_segment_t *cur_rx;
|
||||
rlc_sdu_t *cur_tx;
|
||||
|
||||
entity->vr_ur = 0;
|
||||
entity->vr_ux = 0;
|
||||
entity->vr_uh = 0;
|
||||
|
||||
entity->vt_us = 0;
|
||||
|
||||
entity->t_current = 0;
|
||||
|
||||
entity->t_reordering_start = 0;
|
||||
|
||||
cur_rx = entity->rx_list;
|
||||
while (cur_rx != NULL) {
|
||||
rlc_rx_pdu_segment_t *p = cur_rx;
|
||||
cur_rx = cur_rx->next;
|
||||
rlc_rx_free_pdu_segment(p);
|
||||
}
|
||||
entity->rx_list = NULL;
|
||||
entity->rx_size = 0;
|
||||
|
||||
memset(&entity->reassemble, 0, sizeof(rlc_um_reassemble_t));
|
||||
|
||||
cur_tx = entity->tx_list;
|
||||
while (cur_tx != NULL) {
|
||||
rlc_sdu_t *p = cur_tx;
|
||||
cur_tx = cur_tx->next;
|
||||
rlc_free_sdu(p);
|
||||
}
|
||||
entity->tx_list = NULL;
|
||||
entity->tx_end = NULL;
|
||||
entity->tx_size = 0;
|
||||
}
|
||||
|
||||
void rlc_entity_um_reestablishment(rlc_entity_t *_entity)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
|
||||
rlc_um_reassemble(entity, less_than_vr_uh);
|
||||
|
||||
clear_entity(entity);
|
||||
}
|
||||
|
||||
void rlc_entity_um_delete(rlc_entity_t *_entity)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
clear_entity(entity);
|
||||
free(entity);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_ENTITY_UM_H_
|
||||
#define _RLC_ENTITY_UM_H_
|
||||
|
||||
#include "rlc_entity.h"
|
||||
#include "rlc_pdu.h"
|
||||
#include "rlc_sdu.h"
|
||||
|
||||
typedef struct {
|
||||
char sdu[SDU_MAX]; /* sdu is reassembled here */
|
||||
int sdu_pos; /* next byte to put in sdu */
|
||||
|
||||
/* decoder of current PDU */
|
||||
rlc_pdu_decoder_t dec;
|
||||
int sn;
|
||||
|
||||
int sdu_head_missing;
|
||||
} rlc_um_reassemble_t;
|
||||
|
||||
typedef struct {
|
||||
rlc_entity_t common;
|
||||
|
||||
/* configuration */
|
||||
int t_reordering;
|
||||
int sn_field_length;
|
||||
|
||||
int sn_modulus; /* 1024 for sn_field_length == 10, 32 for 5 */
|
||||
int window_size; /* 512 for sn_field_length == 10, 16 for 5 */
|
||||
|
||||
/* runtime rx */
|
||||
int vr_ur;
|
||||
int vr_ux;
|
||||
int vr_uh;
|
||||
|
||||
/* runtime tx */
|
||||
int vt_us;
|
||||
|
||||
/* set to the latest know time by the user of the module. Unit: ms */
|
||||
uint64_t t_current;
|
||||
|
||||
/* timers (stores the TTI of activation, 0 means not active) */
|
||||
uint64_t t_reordering_start;
|
||||
|
||||
/* rx management */
|
||||
rlc_rx_pdu_segment_t *rx_list;
|
||||
int rx_size;
|
||||
int rx_maxsize;
|
||||
|
||||
/* reassembly management */
|
||||
rlc_um_reassemble_t reassemble;
|
||||
|
||||
/* tx management */
|
||||
rlc_sdu_t *tx_list;
|
||||
rlc_sdu_t *tx_end;
|
||||
int tx_size;
|
||||
int tx_maxsize;
|
||||
} rlc_entity_um_t;
|
||||
|
||||
void rlc_entity_um_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
|
||||
int sdu_id);
|
||||
void rlc_entity_um_recv_pdu(rlc_entity_t *entity, char *buffer, int size);
|
||||
rlc_entity_buffer_status_t rlc_entity_um_buffer_status(
|
||||
rlc_entity_t *entity, int maxsize);
|
||||
int rlc_entity_um_generate_pdu(rlc_entity_t *_entity, char *buffer, int size);
|
||||
void rlc_entity_um_set_time(rlc_entity_t *entity, uint64_t now);
|
||||
void rlc_entity_um_discard_sdu(rlc_entity_t *entity, int sdu_id);
|
||||
void rlc_entity_um_reestablishment(rlc_entity_t *entity);
|
||||
void rlc_entity_um_delete(rlc_entity_t *entity);
|
||||
|
||||
#endif /* _RLC_ENTITY_UM_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "rlc_pdu.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "LOG/log.h"
|
||||
|
||||
/**************************************************************************/
|
||||
/* RX PDU segment and segment list */
|
||||
/**************************************************************************/
|
||||
|
||||
rlc_rx_pdu_segment_t *rlc_rx_new_pdu_segment(int sn, int so, int size,
|
||||
int is_last, char *data, int data_offset)
|
||||
{
|
||||
rlc_rx_pdu_segment_t *ret = malloc(sizeof(rlc_rx_pdu_segment_t));
|
||||
if (ret == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
ret->sn = sn;
|
||||
ret->so = so;
|
||||
ret->size = size;
|
||||
ret->is_last = is_last;
|
||||
ret->next = NULL;
|
||||
|
||||
ret->data = malloc(size);
|
||||
if (ret->data == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(ret->data, data, size);
|
||||
|
||||
ret->data_offset = data_offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rlc_rx_free_pdu_segment(rlc_rx_pdu_segment_t *pdu_segment)
|
||||
{
|
||||
free(pdu_segment->data);
|
||||
free(pdu_segment);
|
||||
}
|
||||
|
||||
rlc_rx_pdu_segment_t *rlc_rx_pdu_segment_list_add(
|
||||
int (*sn_compare)(void *, int, int), void *sn_compare_data,
|
||||
rlc_rx_pdu_segment_t *list, rlc_rx_pdu_segment_t *pdu_segment)
|
||||
{
|
||||
rlc_rx_pdu_segment_t head;
|
||||
rlc_rx_pdu_segment_t *cur;
|
||||
rlc_rx_pdu_segment_t *prev;
|
||||
|
||||
head.next = list;
|
||||
cur = list;
|
||||
prev = &head;
|
||||
|
||||
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
|
||||
while (cur != NULL) {
|
||||
/* check if 'pdu_segment' is before 'cur' in the list */
|
||||
if (sn_compare(sn_compare_data, cur->sn, pdu_segment->sn) > 0 ||
|
||||
(cur->sn == pdu_segment->sn && cur->so > pdu_segment->so)) {
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
prev->next = pdu_segment;
|
||||
pdu_segment->next = cur;
|
||||
return head.next;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/* TX PDU management */
|
||||
/**************************************************************************/
|
||||
|
||||
rlc_tx_pdu_segment_t *rlc_tx_new_pdu(void)
|
||||
{
|
||||
rlc_tx_pdu_segment_t *ret = calloc(1, sizeof(rlc_tx_pdu_segment_t));
|
||||
if (ret == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
ret->retx_count = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rlc_tx_free_pdu(rlc_tx_pdu_segment_t *pdu)
|
||||
{
|
||||
free(pdu);
|
||||
}
|
||||
|
||||
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_append(rlc_tx_pdu_segment_t *list,
|
||||
rlc_tx_pdu_segment_t *pdu)
|
||||
{
|
||||
rlc_tx_pdu_segment_t head;
|
||||
rlc_tx_pdu_segment_t *cur;
|
||||
|
||||
head.next = list;
|
||||
|
||||
cur = &head;
|
||||
while (cur->next != NULL) {
|
||||
cur = cur->next;
|
||||
}
|
||||
cur->next = pdu;
|
||||
|
||||
return head.next;
|
||||
}
|
||||
|
||||
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_add(
|
||||
int (*sn_compare)(void *, int, int), void *sn_compare_data,
|
||||
rlc_tx_pdu_segment_t *list, rlc_tx_pdu_segment_t *pdu_segment)
|
||||
{
|
||||
rlc_tx_pdu_segment_t head;
|
||||
rlc_tx_pdu_segment_t *cur;
|
||||
rlc_tx_pdu_segment_t *prev;
|
||||
|
||||
head.next = list;
|
||||
cur = list;
|
||||
prev = &head;
|
||||
|
||||
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
|
||||
while (cur != NULL) {
|
||||
/* check if 'pdu_segment' is before 'cur' in the list */
|
||||
if (sn_compare(sn_compare_data, cur->sn, pdu_segment->sn) > 0 ||
|
||||
(cur->sn == pdu_segment->sn && cur->so > pdu_segment->so)) {
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
prev->next = pdu_segment;
|
||||
pdu_segment->next = cur;
|
||||
return head.next;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/* PDU decoder */
|
||||
/**************************************************************************/
|
||||
|
||||
void rlc_pdu_decoder_init(rlc_pdu_decoder_t *decoder, char *buffer, int size)
|
||||
{
|
||||
decoder->error = 0;
|
||||
decoder->byte = 0;
|
||||
decoder->bit = 0;
|
||||
decoder->buffer = buffer;
|
||||
decoder->size = size;
|
||||
}
|
||||
|
||||
static int get_bit(rlc_pdu_decoder_t *decoder)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (decoder->byte >= decoder->size) {
|
||||
decoder->error = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = (decoder->buffer[decoder->byte] >> (7 - decoder->bit)) & 1;
|
||||
|
||||
decoder->bit++;
|
||||
if (decoder->bit == 8) {
|
||||
decoder->bit = 0;
|
||||
decoder->byte++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rlc_pdu_decoder_get_bits(rlc_pdu_decoder_t *decoder, int count)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (count > 31) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret <<= 1;
|
||||
ret |= get_bit(decoder);
|
||||
if (decoder->error) return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rlc_pdu_decoder_align(rlc_pdu_decoder_t *decoder)
|
||||
{
|
||||
if (decoder->bit) {
|
||||
decoder->bit = 0;
|
||||
decoder->byte++;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/* PDU encoder */
|
||||
/**************************************************************************/
|
||||
|
||||
void rlc_pdu_encoder_init(rlc_pdu_encoder_t *encoder, char *buffer, int size)
|
||||
{
|
||||
encoder->byte = 0;
|
||||
encoder->bit = 0;
|
||||
encoder->buffer = buffer;
|
||||
encoder->size = size;
|
||||
}
|
||||
|
||||
static void put_bit(rlc_pdu_encoder_t *encoder, int bit)
|
||||
{
|
||||
if (encoder->byte == encoder->size) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal, buffer full\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
encoder->buffer[encoder->byte] <<= 1;
|
||||
if (bit)
|
||||
encoder->buffer[encoder->byte] |= 1;
|
||||
|
||||
encoder->bit++;
|
||||
if (encoder->bit == 8) {
|
||||
encoder->bit = 0;
|
||||
encoder->byte++;
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_pdu_encoder_put_bits(rlc_pdu_encoder_t *encoder, int value, int count)
|
||||
{
|
||||
int i;
|
||||
int x;
|
||||
|
||||
if (count > 31) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
x = 1 << (count - 1);
|
||||
for (i = 0; i < count; i++, x >>= 1)
|
||||
put_bit(encoder, value & x);
|
||||
}
|
||||
|
||||
void rlc_pdu_encoder_align(rlc_pdu_encoder_t *encoder)
|
||||
{
|
||||
while (encoder->bit)
|
||||
put_bit(encoder, 0);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_PDU_H_
|
||||
#define _RLC_PDU_H_
|
||||
|
||||
/**************************************************************************/
|
||||
/* RX PDU segment and segment list */
|
||||
/**************************************************************************/
|
||||
|
||||
typedef struct rlc_rx_pdu_segment_t {
|
||||
int sn;
|
||||
int so;
|
||||
int size;
|
||||
int is_last;
|
||||
char *data;
|
||||
int data_offset;
|
||||
struct rlc_rx_pdu_segment_t *next;
|
||||
} rlc_rx_pdu_segment_t;
|
||||
|
||||
rlc_rx_pdu_segment_t *rlc_rx_new_pdu_segment(int sn, int so, int size,
|
||||
int is_last, char *data, int data_offset);
|
||||
|
||||
void rlc_rx_free_pdu_segment(rlc_rx_pdu_segment_t *pdu_segment);
|
||||
|
||||
rlc_rx_pdu_segment_t *rlc_rx_pdu_segment_list_add(
|
||||
int (*sn_compare)(void *, int, int), void *sn_compare_data,
|
||||
rlc_rx_pdu_segment_t *list, rlc_rx_pdu_segment_t *pdu_segment);
|
||||
|
||||
/**************************************************************************/
|
||||
/* TX PDU management */
|
||||
/**************************************************************************/
|
||||
|
||||
typedef struct rlc_tx_pdu_segment_t {
|
||||
int sn;
|
||||
void *start_sdu; /* real type is rlc_sdu_t * */
|
||||
int sdu_start_byte; /* starting byte in 'start_sdu' */
|
||||
int so; /* starting byte of the segment in full PDU */
|
||||
int data_size; /* number of data bytes (exclude header) */
|
||||
int is_segment;
|
||||
int is_last;
|
||||
int retx_count;
|
||||
struct rlc_tx_pdu_segment_t *next;
|
||||
} rlc_tx_pdu_segment_t;
|
||||
|
||||
rlc_tx_pdu_segment_t *rlc_tx_new_pdu(void);
|
||||
void rlc_tx_free_pdu(rlc_tx_pdu_segment_t *pdu);
|
||||
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_append(rlc_tx_pdu_segment_t *list,
|
||||
rlc_tx_pdu_segment_t *pdu);
|
||||
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_add(
|
||||
int (*sn_compare)(void *, int, int), void *sn_compare_data,
|
||||
rlc_tx_pdu_segment_t *list, rlc_tx_pdu_segment_t *pdu_segment);
|
||||
|
||||
/**************************************************************************/
|
||||
/* PDU decoder */
|
||||
/**************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
int error;
|
||||
int byte; /* next byte to decode */
|
||||
int bit; /* next bit in next byte to decode */
|
||||
char *buffer;
|
||||
int size;
|
||||
} rlc_pdu_decoder_t;
|
||||
|
||||
void rlc_pdu_decoder_init(rlc_pdu_decoder_t *decoder, char *buffer, int size);
|
||||
|
||||
#define rlc_pdu_decoder_in_error(d) ((d)->error == 1)
|
||||
|
||||
int rlc_pdu_decoder_get_bits(rlc_pdu_decoder_t *decoder, int count);
|
||||
|
||||
void rlc_pdu_decoder_align(rlc_pdu_decoder_t *decoder);
|
||||
|
||||
/**************************************************************************/
|
||||
/* PDU encoder */
|
||||
/**************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
int byte; /* next byte to encode */
|
||||
int bit; /* next bit in next byte to encode */
|
||||
char *buffer;
|
||||
int size;
|
||||
} rlc_pdu_encoder_t;
|
||||
|
||||
void rlc_pdu_encoder_init(rlc_pdu_encoder_t *encoder, char *buffer, int size);
|
||||
|
||||
void rlc_pdu_encoder_put_bits(rlc_pdu_encoder_t *encoder, int value, int count);
|
||||
|
||||
void rlc_pdu_encoder_align(rlc_pdu_encoder_t *encoder);
|
||||
|
||||
#endif /* _RLC_PDU_H_ */
|
||||
+38
-32
@@ -19,44 +19,50 @@
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file PHY/NR_TRANSPORT/nr_dci_tools_common.c
|
||||
* \brief
|
||||
* \author
|
||||
* \date 2018
|
||||
* \version 0.1
|
||||
* \company Eurecom
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
#include "rlc_sdu.h"
|
||||
|
||||
#include "nr_dci.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//#define DEBUG_FILL_DCI
|
||||
#include "LOG/log.h"
|
||||
|
||||
#include "nr_dlsch.h"
|
||||
rlc_sdu_t *rlc_new_sdu(char *buffer, int size, int upper_layer_id)
|
||||
{
|
||||
rlc_sdu_t *ret = calloc(1, sizeof(rlc_sdu_t));
|
||||
if (ret == NULL)
|
||||
goto oom;
|
||||
|
||||
ret->upper_layer_id = upper_layer_id;
|
||||
|
||||
void get_coreset_rballoc(uint8_t *FreqDomainResource,int *n_rb,int *rb_offset) {
|
||||
ret->data = malloc(size);
|
||||
if (ret->data == NULL)
|
||||
goto oom;
|
||||
|
||||
uint8_t count=0, start=0, start_set=0;
|
||||
memcpy(ret->data, buffer, size);
|
||||
|
||||
uint64_t bitmap = (((uint64_t)FreqDomainResource[0])<<37)|
|
||||
(((uint64_t)FreqDomainResource[1])<<29)|
|
||||
(((uint64_t)FreqDomainResource[2])<<21)|
|
||||
(((uint64_t)FreqDomainResource[3])<<13)|
|
||||
(((uint64_t)FreqDomainResource[4])<<5)|
|
||||
(((uint64_t)FreqDomainResource[5])>>3);
|
||||
|
||||
for (int i=0; i<45; i++)
|
||||
if ((bitmap>>(44-i))&1) {
|
||||
count++;
|
||||
if (!start_set) {
|
||||
start = i;
|
||||
start_set = 1;
|
||||
}
|
||||
}
|
||||
*rb_offset = 6*start;
|
||||
*n_rb = 6*count;
|
||||
ret->size = size;
|
||||
|
||||
return ret;
|
||||
|
||||
oom:
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void rlc_free_sdu(rlc_sdu_t *sdu)
|
||||
{
|
||||
free(sdu->data);
|
||||
free(sdu);
|
||||
}
|
||||
|
||||
void rlc_sdu_list_add(rlc_sdu_t **list, rlc_sdu_t **end, rlc_sdu_t *sdu)
|
||||
{
|
||||
if (*list == NULL) {
|
||||
*list = sdu;
|
||||
*end = sdu;
|
||||
return;
|
||||
}
|
||||
|
||||
(*end)->next = sdu;
|
||||
*end = sdu;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_SDU_H_
|
||||
#define _RLC_SDU_H_
|
||||
|
||||
typedef struct rlc_sdu_t {
|
||||
int upper_layer_id;
|
||||
char *data;
|
||||
int size;
|
||||
/* next_byte indicates the starting byte to use to construct a new PDU */
|
||||
int next_byte;
|
||||
int acked_bytes;
|
||||
struct rlc_sdu_t *next;
|
||||
} rlc_sdu_t;
|
||||
|
||||
rlc_sdu_t *rlc_new_sdu(char *buffer, int size, int upper_layer_id);
|
||||
void rlc_free_sdu(rlc_sdu_t *sdu);
|
||||
void rlc_sdu_list_add(rlc_sdu_t **list, rlc_sdu_t **end, rlc_sdu_t *sdu);
|
||||
|
||||
#endif /* _RLC_SDU_H_ */
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "rlc_ue_manager.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "LOG/log.h"
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t lock;
|
||||
rlc_ue_t **ue_list;
|
||||
int ue_count;
|
||||
int enb_flag;
|
||||
} rlc_ue_manager_internal_t;
|
||||
|
||||
rlc_ue_manager_t *new_rlc_ue_manager(int enb_flag)
|
||||
{
|
||||
rlc_ue_manager_internal_t *ret;
|
||||
|
||||
ret = calloc(1, sizeof(rlc_ue_manager_internal_t));
|
||||
if (ret == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&ret->lock, NULL)) abort();
|
||||
ret->enb_flag = enb_flag;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rlc_manager_get_enb_flag(rlc_ue_manager_t *_m)
|
||||
{
|
||||
rlc_ue_manager_internal_t *m = _m;
|
||||
return m->enb_flag;
|
||||
}
|
||||
|
||||
void rlc_manager_lock(rlc_ue_manager_t *_m)
|
||||
{
|
||||
rlc_ue_manager_internal_t *m = _m;
|
||||
if (pthread_mutex_lock(&m->lock)) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_manager_unlock(rlc_ue_manager_t *_m)
|
||||
{
|
||||
rlc_ue_manager_internal_t *m = _m;
|
||||
if (pthread_mutex_unlock(&m->lock)) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* must be called with lock acquired */
|
||||
rlc_ue_t *rlc_manager_get_ue(rlc_ue_manager_t *_m, int rnti)
|
||||
{
|
||||
/* TODO: optimze */
|
||||
rlc_ue_manager_internal_t *m = _m;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < m->ue_count; i++)
|
||||
if (m->ue_list[i]->rnti == rnti)
|
||||
return m->ue_list[i];
|
||||
|
||||
LOG_D(RLC, "%s:%d:%s: new UE %d\n", __FILE__, __LINE__, __FUNCTION__, rnti);
|
||||
|
||||
m->ue_count++;
|
||||
m->ue_list = realloc(m->ue_list, sizeof(rlc_ue_t *) * m->ue_count);
|
||||
if (m->ue_list == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
m->ue_list[m->ue_count-1] = calloc(1, sizeof(rlc_ue_t));
|
||||
if (m->ue_list[m->ue_count-1] == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
m->ue_list[m->ue_count-1]->rnti = rnti;
|
||||
|
||||
return m->ue_list[m->ue_count-1];
|
||||
}
|
||||
|
||||
/* must be called with lock acquired */
|
||||
void rlc_manager_remove_ue(rlc_ue_manager_t *_m, int rnti)
|
||||
{
|
||||
rlc_ue_manager_internal_t *m = _m;
|
||||
rlc_ue_t *ue;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < m->ue_count; i++)
|
||||
if (m->ue_list[i]->rnti == rnti)
|
||||
break;
|
||||
|
||||
if (i == m->ue_count) {
|
||||
LOG_D(RLC, "%s:%d:%s: warning: ue %d not found\n",
|
||||
__FILE__, __LINE__, __FUNCTION__,
|
||||
rnti);
|
||||
return;
|
||||
}
|
||||
|
||||
ue = m->ue_list[i];
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
if (ue->srb[j] != NULL)
|
||||
ue->srb[j]->delete(ue->srb[j]);
|
||||
|
||||
for (j = 0; j < 5; j++)
|
||||
if (ue->drb[j] != NULL)
|
||||
ue->drb[j]->delete(ue->drb[j]);
|
||||
|
||||
free(ue);
|
||||
|
||||
m->ue_count--;
|
||||
if (m->ue_count == 0) {
|
||||
free(m->ue_list);
|
||||
m->ue_list = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(&m->ue_list[i], &m->ue_list[i+1],
|
||||
(m->ue_count - i) * sizeof(rlc_ue_t *));
|
||||
m->ue_list = realloc(m->ue_list, m->ue_count * sizeof(rlc_ue_t *));
|
||||
if (m->ue_list == NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* must be called with lock acquired */
|
||||
void rlc_ue_add_srb_rlc_entity(rlc_ue_t *ue, int srb_id, rlc_entity_t *entity)
|
||||
{
|
||||
if (srb_id < 1 || srb_id > 2) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
srb_id--;
|
||||
|
||||
if (ue->srb[srb_id] != NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal, srb already present\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ue->srb[srb_id] = entity;
|
||||
}
|
||||
|
||||
/* must be called with lock acquired */
|
||||
void rlc_ue_add_drb_rlc_entity(rlc_ue_t *ue, int drb_id, rlc_entity_t *entity)
|
||||
{
|
||||
if (drb_id < 1 || drb_id > 5) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
drb_id--;
|
||||
|
||||
if (ue->drb[drb_id] != NULL) {
|
||||
LOG_E(RLC, "%s:%d:%s: fatal, drb already present\n",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ue->drb[drb_id] = entity;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef _RLC_UE_MANAGER_H_
|
||||
#define _RLC_UE_MANAGER_H_
|
||||
|
||||
#include "rlc_entity.h"
|
||||
|
||||
typedef void rlc_ue_manager_t;
|
||||
|
||||
typedef struct rlc_ue_t {
|
||||
int rnti;
|
||||
int module_id; /* necesarry for the L2 simulator - not clean, to revise */
|
||||
rlc_entity_t *srb[2];
|
||||
rlc_entity_t *drb[5];
|
||||
} rlc_ue_t;
|
||||
|
||||
/***********************************************************************/
|
||||
/* manager functions */
|
||||
/***********************************************************************/
|
||||
|
||||
rlc_ue_manager_t *new_rlc_ue_manager(int enb_flag);
|
||||
|
||||
int rlc_manager_get_enb_flag(rlc_ue_manager_t *m);
|
||||
|
||||
void rlc_manager_lock(rlc_ue_manager_t *m);
|
||||
void rlc_manager_unlock(rlc_ue_manager_t *m);
|
||||
|
||||
rlc_ue_t *rlc_manager_get_ue(rlc_ue_manager_t *m, int rnti);
|
||||
void rlc_manager_remove_ue(rlc_ue_manager_t *m, int rnti);
|
||||
|
||||
/***********************************************************************/
|
||||
/* ue functions */
|
||||
/***********************************************************************/
|
||||
|
||||
void rlc_ue_add_srb_rlc_entity(rlc_ue_t *ue, int srb_id, rlc_entity_t *entity);
|
||||
void rlc_ue_add_drb_rlc_entity(rlc_ue_t *ue, int drb_id, rlc_entity_t *entity);
|
||||
|
||||
#endif /* _RLC_UE_MANAGER_H_ */
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOG_E(x, ...) printf(__VA_ARGS__)
|
||||
#define LOG_D(x, ...) printf(__VA_ARGS__)
|
||||
#define LOG_W(x, ...) printf(__VA_ARGS__)
|
||||
|
||||
#endif /* _LOG_H_ */
|
||||
@@ -0,0 +1,32 @@
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -g --coverage -I.
|
||||
|
||||
LIB=rlc_entity.o rlc_entity_am.o rlc_entity_um.o rlc_pdu.o rlc_sdu.o
|
||||
|
||||
tests:
|
||||
@./run_tests.sh
|
||||
|
||||
all: clean_run $(TEST).run
|
||||
|
||||
%.run: $(TEST).bin
|
||||
#valgrind ./$(TEST).bin > $(TEST).run_pre 2> $(TEST).valgrind
|
||||
./$(TEST).bin > $(TEST).run_pre
|
||||
grep ^TEST $(TEST).run_pre > $(TEST).run
|
||||
gunzip -c $(TEST).txt.gz > $(TEST).txt
|
||||
diff -q $(TEST).txt $(TEST).run
|
||||
|
||||
$(TEST).bin: $(TEST).o $(LIB)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
%.o: ../%.c
|
||||
$(CC) $(CFLAGS) -I.. -c -o $@ $<
|
||||
|
||||
$(TEST).o: test.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $< -DTEST='"$(TEST).h"'
|
||||
|
||||
clean_run:
|
||||
rm -f $(TEST).run $(TEST).bin $(TEST).o
|
||||
|
||||
clean:
|
||||
rm -f *.o *.bin *.run *.run_pre *.gcov *.gcda *.gcno test*.txt a.out \
|
||||
*.valgrind
|
||||
@@ -0,0 +1,38 @@
|
||||
To run tests, simply type: make
|
||||
|
||||
Each test is made of:
|
||||
testXX.h definition of the test
|
||||
testXX.txt.gz compressed expected output of the test
|
||||
|
||||
At runtime, we generate:
|
||||
testXX.run actual output of the test
|
||||
|
||||
test.c is the test driving program.
|
||||
|
||||
Only the output lines of the test program starting with "TEST" are
|
||||
stored into testXX.txt and testXX.run.
|
||||
|
||||
A test is considered a success if testXX.txt and testXX.run are equal.
|
||||
|
||||
Only failed tests are reported.
|
||||
|
||||
How to define a new test?
|
||||
|
||||
1 - get the ID
|
||||
|
||||
Look in the file run_tests.sh, the variable test_count gives you
|
||||
the number of existing tests. The ID of your test has to be
|
||||
test_count + 1.
|
||||
|
||||
2 - create files
|
||||
|
||||
Create the file test<ID>.h containing the test, then generate test<ID>.run
|
||||
by running 'make all TEST=test<ID>' and copy test<ID>.run to test<ID>.txt.
|
||||
Then compress this file (gzip -9 test<ID>.txt). Be sure that the output
|
||||
is correct, of course.
|
||||
|
||||
For the file names, replace <ID> by the actual number of the test.
|
||||
For example, if your test ID is 47, then name the files test47.h and
|
||||
test47.txt. And run 'make all TEST=test47' to generate test47.run.
|
||||
|
||||
The available instructions for a test are described at the top of test.c.
|
||||
@@ -0,0 +1,29 @@
|
||||
/* gcc -Wall make_pdu.c -I.. ../rlc_pdu.c */
|
||||
|
||||
#include "rlc_pdu.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char out[100];
|
||||
rlc_pdu_encoder_t e;
|
||||
int i;
|
||||
|
||||
rlc_pdu_encoder_init(&e, out, 100);
|
||||
|
||||
rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
|
||||
rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
|
||||
rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
|
||||
rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
|
||||
rlc_pdu_encoder_put_bits(&e, 10, 10); // nack_sn
|
||||
rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
|
||||
rlc_pdu_encoder_put_bits(&e, 0, 1); // e2
|
||||
|
||||
rlc_pdu_encoder_align(&e);
|
||||
|
||||
for (i = 0; i < e.byte; i++) printf(" %2.2x", (unsigned char)e.buffer[i]);
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_count=45
|
||||
|
||||
for i in `seq $test_count`
|
||||
do
|
||||
make all TEST=test$i >/dev/null 2>/dev/null
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
echo TEST $i FAILURE
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,433 @@
|
||||
#include "../rlc_entity.h"
|
||||
#include "../rlc_entity_am.h"
|
||||
#include "../rlc_entity_um.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* ENB_AM <rx_maxsize> <tx_maxsize> <t_reordering> <t_status_prohibit>
|
||||
* <t_poll_retransmit> <poll_pdu> <poll_byte> <max_retx_threshold>
|
||||
* create the eNB RLC AM entity with given parameters
|
||||
*
|
||||
* UE_AM <rx_maxsize> <tx_maxsize> <t_reordering> <t_status_prohibit>
|
||||
* <t_poll_retransmit> <poll_pdu> <poll_byte> <max_retx_threshold>
|
||||
* create the UE RLC AM entity with given parameters
|
||||
*
|
||||
* ENB_UM <rx_maxsize> <tx_maxsize> <t_reordering> <sn_field_length>
|
||||
* create the eNB RLC UM entity with given parameters
|
||||
*
|
||||
* UE_UM <rx_maxsize> <tx_maxsize> <t_reordering> <sn_field_length>
|
||||
* create the UE RLC UM entity with given parameters
|
||||
*
|
||||
* TIME <time>
|
||||
* following actions to be performed at time <time>
|
||||
* <time> starts at 1
|
||||
* You must end your test definition with a line 'TIME, -1'.
|
||||
*
|
||||
* ENB_SDU <id> <size>
|
||||
* send an SDU to eNB with id <i> and size <size>
|
||||
* the SDU is [00 01 ... ff 01 ...]
|
||||
* (ie. start byte is 00 then we increment for each byte, loop if needed)
|
||||
*
|
||||
* UE_SDU <id> <size>
|
||||
* same as ENB_SDU but the SDU is sent to the UE
|
||||
*
|
||||
* ENB_PDU <size> <'size' bytes>
|
||||
* send a custom PDU from eNB to UE (eNB does not see this PDU at all)
|
||||
*
|
||||
* UE_PDU <size> <'size' bytes>
|
||||
* send a custom PDU from UE to eNB (UE does not see this PDU at all)
|
||||
*
|
||||
* ENB_PDU_SIZE <size>
|
||||
* set 'enb_pdu_size'
|
||||
*
|
||||
* UE_PDU_SIZE <size>
|
||||
* set 'ue_pdu_size'
|
||||
*
|
||||
* ENB_RECV_FAILS <fails>
|
||||
* set the 'enb_recv_fails' flag to <fails>
|
||||
* (1: recv will fail, 0: recv will succeed)
|
||||
*
|
||||
* UE_RECV_FAILS <fails>
|
||||
* same as ENB_RECV_FAILS but for 'ue_recv_fails'
|
||||
*
|
||||
* MUST_FAIL
|
||||
* to be used as first command after the first TIME to indicate
|
||||
* that the test must fail (ie. exit with non zero, crash not allowed)
|
||||
*
|
||||
* ENB_BUFFER_STATUS
|
||||
* call buffer_status for eNB and print result
|
||||
*
|
||||
* UE_BUFFER_STATUS
|
||||
* call buffer_status for UE and print result
|
||||
*
|
||||
* ENB_DISCARD_SDU <sdu ID>
|
||||
* discards given SDU
|
||||
*
|
||||
* UE_DISCARD_SDU <sdu ID>
|
||||
* discards given SDU
|
||||
*
|
||||
* RE_ESTABLISH
|
||||
* re-establish both eNB and UE
|
||||
*/
|
||||
|
||||
enum action {
|
||||
ENB_AM, UE_AM,
|
||||
ENB_UM, UE_UM,
|
||||
TIME, ENB_SDU, UE_SDU, ENB_PDU, UE_PDU,
|
||||
ENB_PDU_SIZE, UE_PDU_SIZE,
|
||||
ENB_RECV_FAILS, UE_RECV_FAILS,
|
||||
MUST_FAIL,
|
||||
ENB_BUFFER_STATUS, UE_BUFFER_STATUS,
|
||||
ENB_DISCARD_SDU, UE_DISCARD_SDU,
|
||||
RE_ESTABLISH
|
||||
};
|
||||
|
||||
int test[] = {
|
||||
/* TEST is defined at compilation time */
|
||||
#include TEST
|
||||
};
|
||||
|
||||
void deliver_sdu_enb_am(void *deliver_sdu_data, struct rlc_entity_t *_entity,
|
||||
char *buf, int size)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: ENB: %"PRIu64": deliver SDU size %d [",
|
||||
entity->t_current, size);
|
||||
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
void deliver_sdu_enb_um(void *deliver_sdu_data, struct rlc_entity_t *_entity,
|
||||
char *buf, int size)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
printf("TEST: ENB: %"PRIu64": deliver SDU size %d [",
|
||||
entity->t_current, size);
|
||||
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
void successful_delivery_enb(void *successful_delivery_data,
|
||||
rlc_entity_t *_entity, int sdu_id)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: ENB: %"PRIu64": SDU %d was successfully delivered.\n",
|
||||
entity->t_current, sdu_id);
|
||||
}
|
||||
|
||||
void max_retx_reached_enb(void *max_retx_reached_data, rlc_entity_t *_entity)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: ENB: %"PRIu64": max RETX reached! radio link failure!\n",
|
||||
entity->t_current);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void deliver_sdu_ue_am(void *deliver_sdu_data, struct rlc_entity_t *_entity,
|
||||
char *buf, int size)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
|
||||
entity->t_current, size);
|
||||
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
void deliver_sdu_ue_um(void *deliver_sdu_data, struct rlc_entity_t *_entity,
|
||||
char *buf, int size)
|
||||
{
|
||||
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
|
||||
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
|
||||
entity->t_current, size);
|
||||
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
void successful_delivery_ue(void *successful_delivery_data,
|
||||
rlc_entity_t *_entity, int sdu_id)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: UE: %"PRIu64": SDU %d was successfully delivered.\n",
|
||||
entity->t_current, sdu_id);
|
||||
}
|
||||
|
||||
void max_retx_reached_ue(void *max_retx_reached_data, rlc_entity_t *_entity)
|
||||
{
|
||||
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
|
||||
printf("TEST: UE: %"PRIu64", max RETX reached! radio link failure!\n",
|
||||
entity->t_current);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
rlc_entity_t *enb = NULL;
|
||||
rlc_entity_t *ue = NULL;
|
||||
int i;
|
||||
int k;
|
||||
char *sdu;
|
||||
char *pdu;
|
||||
rlc_entity_buffer_status_t buffer_status;
|
||||
int enb_do_buffer_status = 0;
|
||||
int ue_do_buffer_status = 0;
|
||||
int size;
|
||||
int pos;
|
||||
int next_byte_enb = 0;
|
||||
int next_byte_ue = 0;
|
||||
int enb_recv_fails = 0;
|
||||
int ue_recv_fails = 0;
|
||||
int enb_pdu_size = 1000;
|
||||
int ue_pdu_size = 1000;
|
||||
|
||||
sdu = malloc(16001);
|
||||
pdu = malloc(3000);
|
||||
if (sdu == NULL || pdu == NULL) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16001; i++)
|
||||
sdu[i] = i & 255;
|
||||
|
||||
pos = 0;
|
||||
if (test[pos] != TIME) {
|
||||
printf("%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 1; i < 1000; i++) {
|
||||
if (i == test[pos+1]) {
|
||||
pos += 2;
|
||||
while (test[pos] != TIME)
|
||||
switch (test[pos]) {
|
||||
default: printf("fatal: unknown action\n"); exit(1);
|
||||
case ENB_AM:
|
||||
enb = new_rlc_entity_am(test[pos+1], test[pos+2],
|
||||
deliver_sdu_enb_am, NULL,
|
||||
successful_delivery_enb, NULL,
|
||||
max_retx_reached_enb, NULL,
|
||||
test[pos+3], test[pos+4], test[pos+5],
|
||||
test[pos+6], test[pos+7], test[pos+8]);
|
||||
pos += 9;
|
||||
break;
|
||||
case UE_AM:
|
||||
ue = new_rlc_entity_am(test[pos+1], test[pos+2],
|
||||
deliver_sdu_ue_am, NULL,
|
||||
successful_delivery_ue, NULL,
|
||||
max_retx_reached_ue, NULL,
|
||||
test[pos+3], test[pos+4], test[pos+5],
|
||||
test[pos+6], test[pos+7], test[pos+8]);
|
||||
pos += 9;
|
||||
break;
|
||||
case ENB_UM:
|
||||
enb = new_rlc_entity_um(test[pos+1], test[pos+2],
|
||||
deliver_sdu_enb_um, NULL,
|
||||
test[pos+3], test[pos+4]);
|
||||
pos += 5;
|
||||
break;
|
||||
case UE_UM:
|
||||
ue = new_rlc_entity_um(test[pos+1], test[pos+2],
|
||||
deliver_sdu_ue_um, NULL,
|
||||
test[pos+3], test[pos+4]);
|
||||
pos += 5;
|
||||
break;
|
||||
case ENB_SDU:
|
||||
for (k = 0; k < test[pos+2]; k++, next_byte_enb++)
|
||||
sdu[k] = next_byte_enb;
|
||||
printf("TEST: ENB: %d: recv_sdu (id %d): size %d: [",
|
||||
i, test[pos+1], test[pos+2]);
|
||||
for (k = 0; k < test[pos+2]; k++)
|
||||
printf(" %2.2x", (unsigned char)sdu[k]);
|
||||
printf("]\n");
|
||||
enb->recv_sdu(enb, sdu, test[pos+2], test[pos+1]);
|
||||
pos += 3;
|
||||
break;
|
||||
case UE_SDU:
|
||||
for (k = 0; k < test[pos+2]; k++, next_byte_ue++)
|
||||
sdu[k] = next_byte_ue;
|
||||
printf("TEST: UE: %d: recv_sdu (id %d): size %d: [",
|
||||
i, test[pos+1], test[pos+2]);
|
||||
for (k = 0; k < test[pos+2]; k++)
|
||||
printf(" %2.2x", (unsigned char)sdu[k]);
|
||||
printf("]\n");
|
||||
ue->recv_sdu(ue, sdu, test[pos+2], test[pos+1]);
|
||||
pos += 3;
|
||||
break;
|
||||
case ENB_PDU:
|
||||
for (k = 0; k < test[pos+1]; k++)
|
||||
pdu[k] = test[pos+2+k];
|
||||
printf("TEST: ENB: %d: custom PDU: size %d: [", i, test[pos+1]);
|
||||
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
|
||||
printf("]\n");
|
||||
if (!ue_recv_fails)
|
||||
ue->recv_pdu(ue, pdu, test[pos+1]);
|
||||
pos += 2 + test[pos+1];
|
||||
break;
|
||||
case UE_PDU:
|
||||
for (k = 0; k < test[pos+1]; k++)
|
||||
pdu[k] = test[pos+2+k];
|
||||
printf("TEST: UE: %d: custom PDU: size %d: [", i, test[pos+1]);
|
||||
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
|
||||
printf("]\n");
|
||||
if (!enb_recv_fails)
|
||||
enb->recv_pdu(enb, pdu, test[pos+1]);
|
||||
pos += 2 + test[pos+1];
|
||||
break;
|
||||
case ENB_PDU_SIZE:
|
||||
enb_pdu_size = test[pos+1];
|
||||
pos += 2;
|
||||
break;
|
||||
case UE_PDU_SIZE:
|
||||
ue_pdu_size = test[pos+1];
|
||||
pos += 2;
|
||||
break;
|
||||
case ENB_RECV_FAILS:
|
||||
enb_recv_fails = test[pos+1];
|
||||
pos += 2;
|
||||
break;
|
||||
case UE_RECV_FAILS:
|
||||
ue_recv_fails = test[pos+1];
|
||||
pos += 2;
|
||||
break;
|
||||
case MUST_FAIL:
|
||||
/* do nothing, only used by caller */
|
||||
pos++;
|
||||
break;
|
||||
case ENB_BUFFER_STATUS:
|
||||
enb_do_buffer_status = 1;
|
||||
pos++;
|
||||
break;
|
||||
case UE_BUFFER_STATUS:
|
||||
ue_do_buffer_status = 1;
|
||||
pos++;
|
||||
break;
|
||||
case ENB_DISCARD_SDU:
|
||||
printf("TEST: ENB: %d: discard SDU %d\n", i, test[pos+1]);
|
||||
enb->discard_sdu(enb, test[pos+1]);
|
||||
pos += 2;
|
||||
break;
|
||||
case UE_DISCARD_SDU:
|
||||
printf("TEST: UE: %d: discard SDU %d\n", i, test[pos+1]);
|
||||
ue->discard_sdu(ue, test[pos+1]);
|
||||
pos += 2;
|
||||
break;
|
||||
case RE_ESTABLISH:
|
||||
printf("TEST: %d: re-establish eNB and UE\n", i);
|
||||
enb->reestablishment(enb);
|
||||
ue->reestablishment(ue);
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enb->set_time(enb, i);
|
||||
ue->set_time(ue, i);
|
||||
|
||||
if (enb_do_buffer_status) {
|
||||
enb_do_buffer_status = 0;
|
||||
buffer_status = enb->buffer_status(enb, enb_pdu_size);
|
||||
printf("TEST: ENB: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
|
||||
i,
|
||||
buffer_status.status_size,
|
||||
buffer_status.tx_size,
|
||||
buffer_status.retx_size);
|
||||
}
|
||||
|
||||
size = enb->generate_pdu(enb, pdu, enb_pdu_size);
|
||||
if (size) {
|
||||
printf("TEST: ENB: %d: generate_pdu: size %d: [", i, size);
|
||||
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
|
||||
printf("]\n");
|
||||
if (!ue_recv_fails)
|
||||
ue->recv_pdu(ue, pdu, size);
|
||||
}
|
||||
|
||||
if (ue_do_buffer_status) {
|
||||
ue_do_buffer_status = 0;
|
||||
buffer_status = ue->buffer_status(ue, ue_pdu_size);
|
||||
printf("TEST: UE: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
|
||||
i,
|
||||
buffer_status.status_size,
|
||||
buffer_status.tx_size,
|
||||
buffer_status.retx_size);
|
||||
}
|
||||
|
||||
size = ue->generate_pdu(ue, pdu, ue_pdu_size);
|
||||
if (size) {
|
||||
printf("TEST: UE: %d: generate_pdu: size %d: [", i, size);
|
||||
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
|
||||
printf("]\n");
|
||||
if (!enb_recv_fails)
|
||||
enb->recv_pdu(enb, pdu, size);
|
||||
}
|
||||
}
|
||||
|
||||
enb->delete(enb);
|
||||
ue->delete(ue);
|
||||
|
||||
free(sdu);
|
||||
free(pdu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("options:\n");
|
||||
printf(" -no-fork\n");
|
||||
printf(" don't fork (to ease debugging with gdb)\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
int must_fail = 0;
|
||||
int son;
|
||||
int status;
|
||||
int i;
|
||||
int no_fork = 0;
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-no-fork")) { no_fork = 1; continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
if (test[2] == MUST_FAIL)
|
||||
must_fail = 1;
|
||||
|
||||
if (no_fork) return test_main();
|
||||
|
||||
son = fork();
|
||||
if (son == -1) {
|
||||
perror("fork");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (son == 0)
|
||||
return test_main();
|
||||
|
||||
if (wait(&status) == -1) {
|
||||
perror("wait");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* child must quit properly */
|
||||
if (!WIFEXITED(status))
|
||||
return 1;
|
||||
|
||||
/* child must fail if expected to */
|
||||
if (must_fail && WEXITSTATUS(status) == 0)
|
||||
return 1;
|
||||
|
||||
/* child must not fail if not expected to */
|
||||
if (!must_fail && WEXITSTATUS(status))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* basic am test:
|
||||
* at time 1, eNB receives an SDU of 10 bytes
|
||||
* at time 10, UE receives an SDU of 5 bytes
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 10,
|
||||
UE_SDU, 0, 5,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* rlc am test resegmentation of PDU segment with several SDUs
|
||||
* eNB sends 3 SDUs [1..10] [11.20] [21..30], not received
|
||||
* eNB retx with smaller PDUs, not received
|
||||
* eNB retx with still smaller PDUs, not received
|
||||
* then reception on, all passes
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_SDU, 0, 10,
|
||||
ENB_SDU, 1, 10,
|
||||
ENB_SDU, 2, 10,
|
||||
TIME, 2,
|
||||
ENB_PDU_SIZE, 25,
|
||||
TIME, 48,
|
||||
ENB_PDU_SIZE, 15,
|
||||
TIME, 100,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* rlc am test function rlc_am_reassemble_next_segment
|
||||
* in r->pdu_byte >= r->so + (r->sdu_offset - r->start->data_offset)
|
||||
* + r->sdu_len
|
||||
* when case 'if (r->e)' is false
|
||||
* eNB sends 3 SDUs [1..10] [11.20] [21..30], not received
|
||||
* eNB retx with smaller PDUs, not received
|
||||
* eNB retx with still smaller PDUs, not received
|
||||
* then UE reception on
|
||||
* then custom PDUs, first a small part of head of original PDU
|
||||
* then a bigger part, covering the first part
|
||||
* so that the beginning of this part triggers the 'while'
|
||||
* then eNB reception on, all passes
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_SDU, 0, 10,
|
||||
ENB_SDU, 1, 10,
|
||||
ENB_SDU, 2, 10,
|
||||
TIME, 2,
|
||||
ENB_PDU_SIZE, 25,
|
||||
TIME, 48,
|
||||
ENB_PDU_SIZE, 15,
|
||||
TIME, 95,
|
||||
ENB_BUFFER_STATUS,
|
||||
TIME, 99,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_PDU, 14, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
ENB_PDU, 25, 0xec, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
|
||||
TIME, 100,
|
||||
ENB_RECV_FAILS, 0,
|
||||
TIME, 134,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* rlc am test function rlc_am_reassemble_next_segment
|
||||
* in r->pdu_byte >= r->so + (r->sdu_offset - r->start->data_offset)
|
||||
* + r->sdu_len
|
||||
* when case 'if (r->e)' is true
|
||||
* eNB sends 4 SDUs [1..5] [6..10] [11.20] [21..30], not received
|
||||
* eNB retx with smaller PDUs, not received
|
||||
* eNB retx with still smaller PDUs, not received
|
||||
* then UE reception on
|
||||
* then custom PDUs, first a small part of head of original PDU
|
||||
* then a bigger part, covering the first part
|
||||
* so that the beginning of this part triggers the 'while'
|
||||
* then eNB reception on, all passes
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_SDU, 0, 5,
|
||||
ENB_SDU, 1, 5,
|
||||
ENB_SDU, 2, 10,
|
||||
ENB_SDU, 3, 10,
|
||||
TIME, 2,
|
||||
ENB_PDU_SIZE, 25,
|
||||
TIME, 48,
|
||||
ENB_PDU_SIZE, 15,
|
||||
TIME, 99,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_PDU, 15, 0xec, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
ENB_PDU, 25, 0xec, 0x00, 0x00, 0x00, 0x80, 0x50, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
TIME, 100,
|
||||
ENB_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* rlc am test function process_received_ack with something in
|
||||
* the retransmit_list to put in the ack_list
|
||||
* eNB sends 4 PDUs, not received
|
||||
* eNB retransmits 4th PDU, received, ACKed with NACKs for PDU 1, 2, 3
|
||||
* UE receives custom PDU for 1, 2, 3, 4 (they are not sent by eNB)
|
||||
* (4 resent to have the P bit set)
|
||||
* UE sends ACK for all, eNB puts from retransmit_list to ack_list
|
||||
*
|
||||
* Maybe not very realistic (custom PDUs).
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_PDU_SIZE, 12,
|
||||
ENB_SDU, 0, 10,
|
||||
ENB_SDU, 1, 10,
|
||||
ENB_SDU, 2, 10,
|
||||
ENB_SDU, 3, 10,
|
||||
TIME, 10,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_RECV_FAILS, 0,
|
||||
TIME, 87,
|
||||
ENB_PDU, 12, 0x80, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
ENB_PDU, 12, 0x80, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
|
||||
ENB_PDU, 12, 0x80, 0x02, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
|
||||
ENB_PDU, 12, 0xa0, 0x03, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* rlc am test max_retx_reached
|
||||
* eNB sends PDU, never received
|
||||
*/
|
||||
TIME, 1,
|
||||
MUST_FAIL,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_SDU, 0, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* rlc am test so_overlap
|
||||
* eNB sends PDU, not received
|
||||
* then PDU is segmented in 3 parts, part 1 & 3 not received,
|
||||
* then we generate a fake control PDU from UE to eNB that
|
||||
* contains NACK with so_start/so_end being inside part 2.
|
||||
*
|
||||
* code to generate fake control PDU:
|
||||
* rlc_pdu_encoder_init(&e, out, 100);
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
|
||||
* rlc_pdu_encoder_put_bits(&e, 2, 10); // ack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 10); // nack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e2
|
||||
* rlc_pdu_encoder_put_bits(&e, 14, 15); // so_start
|
||||
* rlc_pdu_encoder_put_bits(&e, 16, 15); // so_end
|
||||
* rlc_pdu_encoder_align(&e);
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 8,
|
||||
ENB_RECV_FAILS, 1,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_SDU, 1, 30,
|
||||
TIME, 20,
|
||||
ENB_PDU_SIZE, 14,
|
||||
TIME, 48,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 49,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 50,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 60,
|
||||
ENB_RECV_FAILS, 0,
|
||||
UE_PDU, 8, 0x00, 0x0a, 0x00, 0xa0, 0x03, 0x80, 0x08, 0x00,
|
||||
TIME, 70,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* rlc am test process_received_nack
|
||||
* Same events as for test15 except the fake control PDU
|
||||
* does not ACK anything (ack_sn = 0) so that PDU in the
|
||||
* wait_list are not transfered into the ack_list and
|
||||
* we cover the case:
|
||||
* } else {
|
||||
* prev = cur;
|
||||
* cur = cur->next;
|
||||
* }
|
||||
* for the wait_list case.
|
||||
*
|
||||
* code to generate fake control PDU:
|
||||
* rlc_pdu_encoder_init(&e, out, 100);
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 10); // nack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e2
|
||||
* rlc_pdu_encoder_put_bits(&e, 14, 15); // so_start
|
||||
* rlc_pdu_encoder_put_bits(&e, 16, 15); // so_end
|
||||
* rlc_pdu_encoder_align(&e);
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 8,
|
||||
ENB_RECV_FAILS, 1,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_SDU, 1, 30,
|
||||
TIME, 20,
|
||||
ENB_PDU_SIZE, 14,
|
||||
TIME, 48,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 49,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 50,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 60,
|
||||
ENB_RECV_FAILS, 0,
|
||||
UE_PDU, 8, 0x00, 0x02, 0x00, 0xa0, 0x03, 0x80, 0x08, 0x00,
|
||||
TIME, 70,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* rlc am test function process_received_nack
|
||||
* case 'check that VT(A) <= sn < VT(S)'
|
||||
* eNB sends PDU, not received, resends segmented
|
||||
* we generate a fake control PDU containing nack_sn == 10,
|
||||
* to fail the 'check ...' and cover the return.
|
||||
*
|
||||
* code to generate fake control PDU:
|
||||
* rlc_pdu_encoder_init(&e, out, 100);
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 10, 10); // nack_sn
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e2
|
||||
* rlc_pdu_encoder_align(&e);
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 30,
|
||||
ENB_RECV_FAILS, 1,
|
||||
TIME, 20,
|
||||
ENB_PDU_SIZE, 14,
|
||||
TIME, 60,
|
||||
ENB_RECV_FAILS, 0,
|
||||
UE_PDU, 4, 0x00, 0x02, 0x05, 0x00,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* test rlc am simulate rx pdu buffer full
|
||||
* eNB sends too big PDU to UE, rejected because buffer full
|
||||
*/
|
||||
TIME, 1,
|
||||
MUST_FAIL,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 10, 10, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* test rlc am bad PDU
|
||||
* eNB sends custom PDUs to UE, all of them are wrong for a reason or another
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
/* data PDU, LI == 0
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 11); // LI
|
||||
*/
|
||||
ENB_PDU, 4, 0x84, 0x00, 0x00, 0x00,
|
||||
/* data PDU, no data
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
|
||||
*/
|
||||
ENB_PDU, 2, 0x80, 0x00,
|
||||
/* data PDU, LI == 2 > data size == 1
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
|
||||
* rlc_pdu_encoder_put_bits(&e, 2, 11); // LI
|
||||
* rlc_pdu_encoder_align(&e);
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 8); // 1 byte of data
|
||||
*/
|
||||
ENB_PDU, 5, 0x84, 0x00, 0x00, 0x20, 0x00,
|
||||
/* control PDU, CPT != 0
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 2, 3); // CPT
|
||||
*/
|
||||
ENB_PDU, 1, 0x20,
|
||||
/* data PDU, but only 1 byte
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
|
||||
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
|
||||
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
|
||||
*/
|
||||
ENB_PDU, 1, 0x84,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* basic am test:
|
||||
* at time 1, eNB receives an SDU of 16000 bytes
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 16000,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* rlc am test full tx window
|
||||
* for that eNB sends a lot of small PDUs
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 513,
|
||||
ENB_PDU_SIZE, 3,
|
||||
ENB_RECV_FAILS, 1,
|
||||
ENB_BUFFER_STATUS,
|
||||
TIME, 511,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 512,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 513,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 557,
|
||||
ENB_BUFFER_STATUS,
|
||||
TIME, 558,
|
||||
ENB_BUFFER_STATUS,
|
||||
TIME, 559,
|
||||
ENB_BUFFER_STATUS,
|
||||
TIME, 600,
|
||||
ENB_BUFFER_STATUS,
|
||||
ENB_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* rlc am test big SDU (size > 2047)
|
||||
* first generate SDU with exactly 2047 bytes
|
||||
* later on generate SDU with exactly 2048 bytes
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 20,
|
||||
ENB_SDU, 1, 2047,
|
||||
ENB_SDU, 2, 20,
|
||||
ENB_PDU_SIZE, 2200,
|
||||
TIME, 10,
|
||||
ENB_SDU, 3, 20,
|
||||
ENB_SDU, 4, 2048,
|
||||
ENB_SDU, 5, 20,
|
||||
TIME, -1
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* am test: ask for retx with TX buffer too small
|
||||
* then ask for status with buffer too small
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 100,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 47,
|
||||
ENB_PDU_SIZE, 4,
|
||||
ENB_BUFFER_STATUS,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 48,
|
||||
ENB_PDU_SIZE, 1000,
|
||||
UE_PDU_SIZE, 1,
|
||||
UE_BUFFER_STATUS,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 49,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, 50,
|
||||
UE_PDU_SIZE, 1000,
|
||||
UE_BUFFER_STATUS,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* am test: basic test with poll_byte == 1
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, 1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, 1, 4,
|
||||
ENB_SDU, 0, 30,
|
||||
ENB_PDU_SIZE, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* am test: basic test with poll_pdu == 2
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, 2, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, 2, -1, 4,
|
||||
ENB_SDU, 0, 50,
|
||||
ENB_PDU_SIZE, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* am test: reject SDU because not enough room in rx buffer
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 10, 10, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 10, 10, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 50,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* am test: test function check_t_poll_retransmit
|
||||
* case 'PDU with SN = VT(S)-1 not found?'
|
||||
* eNB sends some PDUs, UE receives none
|
||||
* then UE receives the first retransmitted PDU and nothing more
|
||||
* until poll retransmit occurs again in the eNB to trigger the case
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 2,
|
||||
ENB_SDU, 1, 10,
|
||||
TIME, 3,
|
||||
ENB_SDU, 2, 10,
|
||||
TIME, 4,
|
||||
ENB_SDU, 3, 10,
|
||||
TIME, 50,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, 51,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 100,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* am test: test function check_t_poll_retransmit
|
||||
* case 'do we meet conditions of 36.322 5.2.2.3?'
|
||||
* eNB sends one PDU, UE does not receive
|
||||
* just before calling check_t_poll_retransmit, eNB receives a new SDU
|
||||
* for the function 'check_poll_after_pdu_assembly' to fail
|
||||
* then UE receives all what eNB sends
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 47,
|
||||
ENB_SDU, 1, 10,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* am test: test function check_t_reordering,
|
||||
* case 'update VR(MS) to first SN >= VR(X) for which not
|
||||
* all PDU segments have been received'
|
||||
* eNB sends 3 PDUs, first not received, two others received
|
||||
* later on, everything is received
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_SDU, 1, 10,
|
||||
TIME, 3,
|
||||
ENB_SDU, 2, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* am test: test function check_t_reordering,
|
||||
* case 'VR(H) > VR(MS)'
|
||||
* eNB sends 4 PDUs, only 1st and 3rd are received
|
||||
* later on, everything is received
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 10,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_SDU, 1, 10,
|
||||
TIME, 3,
|
||||
UE_RECV_FAILS, 1,
|
||||
ENB_SDU, 2, 10,
|
||||
TIME, 4,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_SDU, 3, 10,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* basic am test:
|
||||
* at time 1, eNB receives an SDU of 16001 bytes
|
||||
*/
|
||||
|
||||
TIME, 1,
|
||||
MUST_FAIL,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 16001,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* am test: test function generate_status
|
||||
* enter the while loop 'go to highest full sn+1 for ACK'
|
||||
* eNB sends several PDUs, only the last is received
|
||||
* UE sends status PDU of a chosen size that let the code enter the while
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
|
||||
ENB_SDU, 0, 70,
|
||||
ENB_PDU_SIZE, 12,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 7,
|
||||
UE_RECV_FAILS, 0,
|
||||
UE_PDU_SIZE, 12,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* um test: several SDUs in a PDU (field length 5 bits)
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_UM, 100000, 100000, 35, 5,
|
||||
UE_UM, 100000, 100000, 35, 5,
|
||||
ENB_SDU, 0, 10,
|
||||
ENB_SDU, 1, 20,
|
||||
ENB_SDU, 2, 30,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* um test: several SDUs in a PDU (field length 10 bits)
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_UM, 100000, 100000, 35, 10,
|
||||
UE_UM, 100000, 100000, 35, 10,
|
||||
ENB_SDU, 0, 10,
|
||||
ENB_SDU, 1, 20,
|
||||
ENB_SDU, 2, 30,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* um test: test function rlc_um_reassemble_pdu, discard SDU
|
||||
* case '!(fi & 0x02'
|
||||
* eNB sends 33 PDUs covering 1 SDU, only PDU 0 received (with SN=0 and FI=1)
|
||||
* then eNB sends 1 PDU covering 1 SDU (so SN=1 and FI=0 for this one)
|
||||
* received by UE
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_UM, 100000, 100000, 35, 5,
|
||||
UE_UM, 100000, 100000, 35, 5,
|
||||
ENB_SDU, 0, 33,
|
||||
ENB_PDU_SIZE, 2,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 34,
|
||||
UE_RECV_FAILS, 0,
|
||||
ENB_SDU, 1, 1,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* um test: trigger some cases in rlc_um_reception_actions
|
||||
* eNB sends several PDUs, only the beginning PDUs and ending PDUs are
|
||||
* received. Middle PDUs are not.
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_UM, 100000, 100000, 35, 5,
|
||||
UE_UM, 100000, 100000, 35, 5,
|
||||
ENB_SDU, 0, 40,
|
||||
ENB_PDU_SIZE, 2,
|
||||
TIME, 2,
|
||||
UE_RECV_FAILS, 1,
|
||||
TIME, 8,
|
||||
UE_RECV_FAILS, 0,
|
||||
TIME, -1
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* um: discard PDU because rx buffer full
|
||||
* eNB sends a PDU too big
|
||||
*/
|
||||
TIME, 1,
|
||||
ENB_UM, 100000, 100000, 35, 5,
|
||||
UE_UM, 10, 10, 35, 5,
|
||||
ENB_SDU, 0, 40,
|
||||
TIME, -1
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user