From ebc4bb8409dd3c646c430b146ad2348da1cf8e5f Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 26 Nov 2025 11:12:03 +0100 Subject: [PATCH 01/23] CI: Remove redundant Jenkinsfiles All the removed files can be replaced by Jenkinsfile introduced in !3738 --- ci-scripts/Jenkinsfile-cluster | 228 -------------------- ci-scripts/Jenkinsfile-nrue | 223 ------------------- ci-scripts/Jenkinsfile-physim-deploy | 224 ------------------- ci-scripts/Jenkinsfile-tmp-full-ran | 250 ---------------------- ci-scripts/Jenkinsfile-tmp-multi-enb | 225 -------------------- ci-scripts/Jenkinsfile-tmp-multi-enb-nsa | 216 ------------------- ci-scripts/Jenkinsfile-tmp-ran | 260 ----------------------- 7 files changed, 1626 deletions(-) delete mode 100644 ci-scripts/Jenkinsfile-cluster delete mode 100644 ci-scripts/Jenkinsfile-nrue delete mode 100644 ci-scripts/Jenkinsfile-physim-deploy delete mode 100644 ci-scripts/Jenkinsfile-tmp-full-ran delete mode 100644 ci-scripts/Jenkinsfile-tmp-multi-enb delete mode 100644 ci-scripts/Jenkinsfile-tmp-multi-enb-nsa delete mode 100644 ci-scripts/Jenkinsfile-tmp-ran diff --git a/ci-scripts/Jenkinsfile-cluster b/ci-scripts/Jenkinsfile-cluster deleted file mode 100644 index 174ccab62f..0000000000 --- a/ci-scripts/Jenkinsfile-cluster +++ /dev/null @@ -1,228 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Location of the executor node -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -// Name of the resource -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -def directoryExistsGlob(fileNameWithGlob) { - /* if multiple directories match, will join their names and call fileExists() on that */ - def dir = sh returnStdout: true, script: 'find . -name \'' + fileNameWithGlob + '\' -type d' - dir = dir.replaceAll("\n", "").trim() - echo "matching '" + fileNameWithGlob + "' found: '" + dir + "'" - if (dir == "") - return false - return fileExists(dir) -} - -//------------------------------------------------------------------------------- -// Pipeline start -pipeline { - agent { - label pythonExecutor - } - options { - timestamps() - ansiColor('xterm') - lock(extra: lockResources) - } - - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ('Verify Parameters') { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - - JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"' - JOB_TIMESTAMP = JOB_TIMESTAMP.trim() - - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - echo "no eNB_SourceCodePath given" - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } else { - echo "no eNB_mergeRequest given - not merging develop" - } - - if (params.OC_Credentials == null) { - echo "no OC_Credentials given" - allParametersPresent = false - } - if (params.OC_ProjectName == null) { - echo "no OC_ProjectName given" - allParametersPresent = false - } - if (params.pythonTestXmlFile == null) { - echo "no pythonTestXmlFile given" - allParametersPresent = false - } - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - echo "Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - - stage ("Deploy and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] - ]) { - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - } - stage ("Log Collection") { - steps { - script { - dir ('ci-scripts') { - if (directoryExistsGlob("build_log_*")) { - sh "zip -r -qq build_log_${env.BUILD_ID}.zip build_log_*/*" - sh "rm -rf build_log_*/" - archiveArtifacts artifacts: "build_log_${env.BUILD_ID}.zip" - } - if (directoryExistsGlob("test_log_*")) { - sh "zip -r -qq test_log_${env.BUILD_ID}.zip test_log_*/*" - sh "rm -rf test_log_*/" - archiveArtifacts artifacts: "test_log_${env.BUILD_ID}.zip" - } - if (fileExists("test_results.html")) { - sh "mv test_results.html test_results-${env.JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts artifacts: "test_results-${env.JOB_NAME}.html" - } - } - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-nrue b/ci-scripts/Jenkinsfile-nrue deleted file mode 100644 index 3f7ef9b9ed..0000000000 --- a/ci-scripts/Jenkinsfile-nrue +++ /dev/null @@ -1,223 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Location of the executor node -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -// Name of the resource -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -// Flags -def scmEvent = false -def upstreamEvent = false - -//------------------------------------------------------------------------------- -// Pipeline start -pipeline { - agent { - label pythonExecutor - } - options { - timestamps() - ansiColor('xterm') - lock(extra: lockResources) - } - - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ('Verify Parameters') { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - - JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"' - JOB_TIMESTAMP = JOB_TIMESTAMP.trim() - - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (params.OC_Credentials == null) { - allParametersPresent = false - } - if (params.OC_ProjectName == null) { - allParametersPresent = false - } - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - echo "Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - - stage ("Deploy and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] - ]) { - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - } - stage ("Log Collection") { - steps { - script { - dir ('ci-scripts') { - // Zipping all archived log files - sh "mkdir test_logs" - sh "mv *.log* test_logs || true" - // Zip all log files matching cmake_targets/{*.log*,log/*} into test_logs_XXXX.zip - if (fileExists('../cmake_targets/log')) { - sh "mv ../cmake_targets/log/* test_logs || true" - } - sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" - sh "rm -rf *test_log*/" - if (fileExists("test_logs_${env.BUILD_ID}.zip")) { - archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" - } - if (fileExists("test_results.html")) { - sh "mv test_results.html test_results-${env.JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts artifacts: "test_results-${env.JOB_NAME}.html" - } - } - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-physim-deploy b/ci-scripts/Jenkinsfile-physim-deploy deleted file mode 100644 index e3d243d07b..0000000000 --- a/ci-scripts/Jenkinsfile-physim-deploy +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Location of the executor node -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -// Name of the resource -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -// Flags -def scmEvent = false -def upstreamEvent = false - -//------------------------------------------------------------------------------- -// Pipeline start -pipeline { - agent { - label pythonExecutor - } - options { - timestamps() - ansiColor('xterm') - lock(extra: lockResources) - } - - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ('Verify Parameters') { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - - JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"' - JOB_TIMESTAMP = JOB_TIMESTAMP.trim() - - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (params.OC_Credentials == null) { - allParametersPresent = false - } - if (params.OC_ProjectName == null) { - allParametersPresent = false - } - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - echo "Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - - stage ("Deploy and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - withCredentials([ - [$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.OC_Credentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password'] - ]) { - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile} --OCUserName=${OC_Username} --OCPassword=${OC_Password} --OCProjectName=${OC_ProjectName}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - } - stage ("Log Collection") { - steps { - script { - dir ('ci-scripts') { - // Zipping all archived log files - sh "mkdir test_logs" - sh "mv *.log* test_logs || true" - // Zip all log files matching cmake_targets/{*.log*,log/*} into test_logs_XXXX.zip - if (fileExists('../cmake_targets/log')) { - sh "mv ../cmake_targets/log/* test_logs || true" - } - sh "zip -r -qq test_logs_${env.BUILD_ID}.zip *test_log*/" - sh "rm -rf *test_log*/" - if (fileExists("test_logs_${env.BUILD_ID}.zip")) { - archiveArtifacts artifacts: "test_logs_${env.BUILD_ID}.zip" - } - if (fileExists("test_results.html")) { - sh "mv test_results.html test_results-${env.JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts artifacts: "test_results-${env.JOB_NAME}.html" - } - } - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-tmp-full-ran b/ci-scripts/Jenkinsfile-tmp-full-ran deleted file mode 100644 index 07b10ab980..0000000000 --- a/ci-scripts/Jenkinsfile-tmp-full-ran +++ /dev/null @@ -1,250 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Template Jenkins Declarative Pipeline script to run Test w/ RF HW - -// Location of the python executor node shall be in the same subnet as the others servers -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Terminate Status -def termENB = 0 -def termStatusArray = new Boolean[termENB + 1] -termStatusArray[termENB] = false - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -def doEpcLogCollection = true - - - -pipeline { - agent { - label pythonExecutor - } - options { - ansiColor('xterm') - lock(extra: lockResources) - } - - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ("Verify Parameters") { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - currentBuild.result = 'ABORTED' - error('Stopping early because some parameters are missing') - } - } - } - } - stage ("Build and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - stage ("Terminate") { - parallel { - stage('Terminate eNB') { - steps { - echo '\u2705 \u001B[32mTerminate eNB\u001B[0m' - sh "python3 ci-scripts/main.py --mode=TerminateeNB" - } - post { - success { - script { - termStatusArray[termENB] = true - } - } - } - } - } - } - stage('Log Collection') { - parallel { - stage('Log Collection (eNB - Run)') { - steps { - echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}" - - script { - if(fileExists("enb.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "enb.log.${env.BUILD_ID}.zip" - } - if(fileExists("ci-scripts/test_results.html")) { - sh "mv ci-scripts/test_results.html test_results-${JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts "test_results-${JOB_NAME}.html" - } - } - } - } - } - } - } - - post { - always { - script { - if (params.pipelineZipsConsoleLog != null) { - if (params.pipelineZipsConsoleLog) { - echo "Archiving Jenkins console log" - sh "wget --no-check-certificate --no-proxy ${env.JENKINS_URL}/job/${env.JOB_NAME}/${env.BUILD_ID}/consoleText -O consoleText.log || true" - sh "zip -m consoleText.log.${env.BUILD_ID}.zip consoleText.log || true" - if(fileExists("consoleText.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "consoleText.log.${env.BUILD_ID}.zip" - } - } - } - } - } - // Making sure that we really shutdown every thing before leaving - failure { - script { - if (!termStatusArray[termENB]) { - sh "python3 ci-scripts/main.py --mode=TerminateeNB" - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-tmp-multi-enb b/ci-scripts/Jenkinsfile-tmp-multi-enb deleted file mode 100644 index c01c8daf7e..0000000000 --- a/ci-scripts/Jenkinsfile-tmp-multi-enb +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Template Jenkins Declarative Pipeline script to run Test w/ RF HW - -// Location of the python executor node shall be in the same subnet as the others servers -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -pipeline { - agent { - label pythonExecutor - } - options { - ansiColor('xterm') - lock(extra: lockResources) - } - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ("Verify Parameters") { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - echo "Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - stage ("Build and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - stage ("Terminate") { - parallel { - stage('Terminate eNB') { - steps { - echo '\u2705 \u001B[32mTerminate eNB\u001B[0m' - sh "python3 ci-scripts/main.py --mode=TerminateeNB" - } - } - } - } - stage('Log Collection') { - parallel { - stage('Log Collection (eNB - Run)') { - steps { - echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}" - - script { - if(fileExists("enb.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "enb.log.${env.BUILD_ID}.zip" - } - if(fileExists("ci-scripts/test_results.html")) { - sh "mv ci-scripts/test_results.html test_results-${JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts "test_results-${JOB_NAME}.html" - } - } - } - } - } - } - } - - post { - always { - script { - if (params.pipelineZipsConsoleLog != null) { - if (params.pipelineZipsConsoleLog) { - echo "Archiving Jenkins console log" - sh "wget --no-check-certificate --no-proxy ${env.JENKINS_URL}/job/${env.JOB_NAME}/${env.BUILD_ID}/consoleText -O consoleText.log || true" - sh "zip -m consoleText.log.${env.BUILD_ID}.zip consoleText.log || true" - if(fileExists("consoleText.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "consoleText.log.${env.BUILD_ID}.zip" - } - } - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa b/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa deleted file mode 100644 index 67aa6b4511..0000000000 --- a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa +++ /dev/null @@ -1,216 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Template Jenkins Declarative Pipeline script to run Test w/ RF HW - -// Location of the python executor node shall be in the same subnet as the others servers -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess -def eNB_TargetBranch - -//Status fed to the database -def StatusForDb = "" - - -pipeline { - agent {label pythonExecutor} - options { - ansiColor('xterm') - lock(extra: lockResources) - } - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ("Verify Parameters") { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest!= null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - echo "Some parameters are missing" - sh "./ci-scripts/fail.sh" - } - } - } - } - stage ("Build and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - stage('Log Collection') { - parallel { - stage('Log Collection (eNB - Run)') { - steps { - echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}" - script { - if(fileExists("enb.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "enb.log.${env.BUILD_ID}.zip" - } - } - } - } - } - } - } - - post { - always { - script { - if(fileExists("ci-scripts/test_results.html")) { - sh "mv ci-scripts/test_results.html test_results-${JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts "test_results-${JOB_NAME}.html" - } - if (params.pipelineZipsConsoleLog != null) { - if (params.pipelineZipsConsoleLog) { - echo "Archiving Jenkins console log" - sh "wget --no-check-certificate --no-proxy ${env.JENKINS_URL}/job/${env.JOB_NAME}/${env.BUILD_ID}/consoleText -O consoleText.log || true" - sh "zip -m consoleText.log.${env.BUILD_ID}.zip consoleText.log || true" - if(fileExists("consoleText.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "consoleText.log.${env.BUILD_ID}.zip" - } - } - } - } - } - } -} diff --git a/ci-scripts/Jenkinsfile-tmp-ran b/ci-scripts/Jenkinsfile-tmp-ran deleted file mode 100644 index 526899a5a2..0000000000 --- a/ci-scripts/Jenkinsfile-tmp-ran +++ /dev/null @@ -1,260 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Template Jenkins Declarative Pipeline script to run Test w/ RF HW - -// Location of the python executor node shall be in the same subnet as the others servers -def pythonExecutor = params.pythonExecutor - -// Location of the test XML file to be run -def testXMLFile = params.pythonTestXmlFile -def mainPythonAllXmlFiles = "" -def buildStageStatus = true - -// Name of the test stage -def testStageName = params.pipelineTestStageName - -def lockResources = [] -if (params.LockResources != null && params.LockResources.trim().length() > 0) - params.LockResources.trim().split(",").each{lockResources += [resource: it.trim()]} - -// Terminate Status -def termUE = 0 -def termENB = 1 -def termSPGW = 2 -def termMME = 3 -def termHSS = 4 -def termStatusArray = new Boolean[termHSS + 1] -termStatusArray[termUE] = false -termStatusArray[termENB] = false -termStatusArray[termSPGW] = false -termStatusArray[termMME] = false -termStatusArray[termHSS] = false - -// Global Parameters. Normally they should be populated when the master job -// triggers the slave job with parameters -def eNB_Repository -def eNB_Branch -def eNB_CommitID -def eNB_AllowMergeRequestProcess = false -def eNB_TargetBranch - -def flexricOption = "" - -pipeline { - agent { - label pythonExecutor - } - options { - ansiColor('xterm') - lock(extra: lockResources) - } - stages { - stage("Build Init") { - steps { - // update the build name and description - buildName "${params.eNB_MR}" - buildDescription "Branch : ${params.eNB_Branch}" - } - } - stage ("Verify Parameters") { - steps { - script { - echo '\u2705 \u001B[32mVerify Parameters\u001B[0m' - def allParametersPresent = true - - // It is already to late to check it - if (params.pythonExecutor != null) { - echo "eNB CI executor node : ${pythonExecutor}" - } - // If not present picking a default Stage Name - if (params.pipelineTestStageName == null) { - // picking default - testStageName = 'Template Test Stage' - } - - if (params.LockResources == null) { - echo "no LockResources given" - allParametersPresent = false - } - if (params.eNB_SourceCodePath == null) { - allParametersPresent = false - } - // the following 4 parameters should be pushed by the master trigger - // if not present, take the job GIT variables (used for developing) - if (params.eNB_Repository == null) { - eNB_Repository = env.GIT_URL - } else { - eNB_Repository = params.eNB_Repository - } - echo "eNB_Repository : ${eNB_Repository}" - if (params.eNB_Branch == null) { - eNB_Branch = env.GIT_BRANCH - } else { - eNB_Branch = params.eNB_Branch - } - echo "eNB_Branch : ${eNB_Branch}" - if (params.eNB_CommitID == null) { - eNB_CommitID = env.GIT_COMMIT - } else { - eNB_CommitID = params.eNB_CommitID - } - echo "eNB_CommitID : ${eNB_CommitID}" - if (params.eNB_mergeRequest != null) { - eNB_AllowMergeRequestProcess = params.eNB_mergeRequest - if (eNB_AllowMergeRequestProcess) { - if (params.eNB_TargetBranch != null) { - eNB_TargetBranch = params.eNB_TargetBranch - } else { - eNB_TargetBranch = 'develop' - } - echo "eNB_TargetBranch : ${eNB_TargetBranch}" - } - } - - if (params.Flexric_Tag != null) { - echo "This pipeline is configured to run with a FlexRIC deployment." - echo "Appending FlexRicTag option to the list of options" - flexricOption = "--FlexRicTag=${params.Flexric_Tag}" - echo "Using new Flexric option: ${flexricOption}" - } - - if (allParametersPresent) { - echo "All parameters are present" - if (eNB_AllowMergeRequestProcess) { - sh "git fetch" - sh "./ci-scripts/doGitLabMerge.sh --src-branch ${eNB_Branch} --src-commit ${eNB_CommitID} --target-branch ${eNB_TargetBranch} --target-commit latest" - } else if (eNB_CommitID != 'develop') { - sh "git fetch" - sh "git checkout -f ${eNB_CommitID}" - } - } else { - error "Some parameters are missing" - } - } - } - } - stage ("Build and Test") { - steps { - script { - dir ('ci-scripts') { - echo "\u2705 \u001B[32m${testStageName}\u001B[0m" - // If not present picking a default XML file - if (params.pythonTestXmlFile == null) { - // picking default - testXMLFile = 'xml_files/enb_usrpB210_band7_50PRB.xml' - echo "Test XML file(default): ${testXMLFile}" - mainPythonAllXmlFiles += "--XMLTestFile=" + testXMLFile + " " - } else { - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - mainPythonAllXmlFiles += "--XMLTestFile=" + xmlFile + " " - echo "Test XML file : ${xmlFile}" - } else { - echo "Test XML file ${xmlFile}: no such file" - } - } - } - sh "python3 main.py --mode=InitiateHtml --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} ${mainPythonAllXmlFiles}" - String[] myXmlTestSuite = testXMLFile.split("\\r?\\n") - for (xmlFile in myXmlTestSuite) { - if (fileExists(xmlFile)) { - try { - timeout (time: 60, unit: 'MINUTES') { - sh "python3 main.py --mode=TesteNB --ranRepository=${eNB_Repository} --ranBranch=${eNB_Branch} --ranCommitID=${eNB_CommitID} --ranAllowMerge=${eNB_AllowMergeRequestProcess} --ranTargetBranch=${eNB_TargetBranch} ${flexricOption} --eNBSourceCodePath=${params.eNB_SourceCodePath} --XMLTestFile=${xmlFile}" - } - } catch (Exception e) { - currentBuild.result = 'FAILURE' - buildStageStatus = false - } - } - } - sh "python3 main.py --mode=FinalizeHtml --finalStatus=${buildStageStatus}" - } - } - } - } - stage ("Terminate") { - parallel { - stage('Terminate eNB') { - steps { - echo '\u2705 \u001B[32mTerminate eNB\u001B[0m' - sh "python3 ci-scripts/main.py --mode=TerminateeNB" - } - post { - success { - script { - termStatusArray[termENB] = true - } - } - } - } - } - } - stage('Log Collection') { - parallel { - stage('Log Collection (eNB - Run)') { - steps { - echo '\u2705 \u001B[32mLog Collection (eNB - Run)\u001B[0m' - sh "python3 ci-scripts/main.py --mode=LogCollecteNB --eNBSourceCodePath=${params.eNB_SourceCodePath} --BuildId=${env.BUILD_ID}" - script { - if(fileExists("enb.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "enb.log.${env.BUILD_ID}.zip" - } - if(fileExists("ci-scripts/test_results.html")) { - sh "mv ci-scripts/test_results.html test_results-${JOB_NAME}.html" - sh "sed -i -e 's#TEMPLATE_JOB_NAME#${JOB_NAME}#' -e 's@build #TEMPLATE_BUILD_ID@build #${BUILD_ID}@' -e 's#Build-ID: TEMPLATE_BUILD_ID#Build-ID: ${BUILD_ID}#' -e 's#TEMPLATE_STAGE_NAME#${testStageName}#' test_results-${JOB_NAME}.html" - archiveArtifacts "test_results-${JOB_NAME}.html" - } - } - } - } - } - } - } - - post { - always { - script { - if (params.pipelineZipsConsoleLog != null) { - if (params.pipelineZipsConsoleLog) { - echo "Archiving Jenkins console log" - sh "wget --no-check-certificate --no-proxy ${env.JENKINS_URL}/job/${env.JOB_NAME}/${env.BUILD_ID}/consoleText -O consoleText.log || true" - sh "zip -m consoleText.log.${env.BUILD_ID}.zip consoleText.log || true" - if(fileExists("consoleText.log.${env.BUILD_ID}.zip")) { - archiveArtifacts "consoleText.log.${env.BUILD_ID}.zip" - } - } - } - } - } - // Making sure that we really shutdown every thing before leaving - failure { - script { - if (!termStatusArray[termENB]) { - sh "python3 ci-scripts/main.py --mode=TerminateeNB" - } - } - } - } -} From 4db49a87673aba393dc2f1ba72b4d0b4bc5071eb Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 19 Nov 2025 15:27:17 +0100 Subject: [PATCH 02/23] RRC: Implement proper NH chain synchronization for RRC Reestablishment Refactor key derivation logic to properly handle Next Hop (NH) chain synchronization according to 3GPP TS 33.501 specifications for both RRC Reestablishment and Master Key Update procedures. Changes in rrc_gNB.c: - Clarify key derivation in rrc_gNB_generate_RRCReestablishment by explicitly selecting base key (NH or KgNB) based on nh_ncc value - Add comment explaining vertical vs horizontal derivation logic Changes in rrc_UE.c: - Add nr_sync_nh_chain(): Common helper to synchronize NH parameter chain per TS 33.501 Annex A.10, supporting both Master Key Update and RRC Reestablishment procedures - Add nr_derive_kgnb_horizontal(): Derive KNG-RAN* from current KgNB when NCC values match (horizontal derivation per TS 33.501 A.11/A.12) - Add nr_derive_kgnb_vertical(): Derive KNG-RAN* from synchronized NH after NH chain sync (vertical derivation per TS 33.501 A.11/A.12) - Add nr_update_kgnb_from_ncc(): Unified function implementing TS 33.501 6.9.2.3.4 logic for updating KgNB based on received nextHopChainingCount - Refactor as_security_key_update(): Use new nr_update_kgnb_from_ncc() helper function for cleaner, more maintainable code - Fix nr_rrc_ue_process_rrcReestablishment(): Properly handle received nextHopChainingCount and update KgNB using unified helper function - Add debug logging for key derivation process Key NAS deriver utils: - update derive_kgnb documentation and protect input keys with const attribute --- openair2/RRC/NR/rrc_gNB.c | 5 +- openair2/RRC/NR_UE/rrc_UE.c | 201 ++++++++++++++++++++++++-------- openair3/SECU/key_nas_deriver.c | 19 ++- openair3/SECU/key_nas_deriver.h | 2 +- 4 files changed, 177 insertions(+), 50 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 4d28d66b69..6831d97869 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1092,7 +1092,10 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; uint32_t ssb_arfcn = get_ssb_arfcn(du); LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", cell_info->nr_pci, ssb_arfcn); - nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, ue_p->nh_ncc > 0 ? ue_p->nh : ue_p->kgnb, ue_p->kgnb); + + /* Derive KgNB* using NH if nh_ncc > 0 (vertical derivation), else use KgNB (horizontal derivation) */ + const uint8_t *base_key = ue_p->nh_ncc > 0 ? ue_p->nh : ue_p->kgnb; + nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, base_key, ue_p->kgnb); int size = do_RRCReestablishment(ue_context_pP->ue_context.nh_ncc, buffer, NR_RRC_BUF_SIZE, xid); LOG_A(NR_RRC, "Send RRCReestablishment [%d bytes] to RNTI %04x\n", size, ue_p->rnti); diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 27e1dc0bb1..8b1219d392 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -525,13 +525,141 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf nr_rrc_send_msg_to_mac(rrc, &rrc_msg); } -static void nr_rrc_nh_update(NR_UE_RRC_INST_t *rrc, uint8_t *kamf, uint8_t *sync_input) +/** @brief Synchronize NH chain based on nextHopChainingCount + * + * This function synchronizes the NH parameter chain as specified in 3GPP TS 33.501 Annex A.10. + * Use cases: Master Key Update, RRC Reestablishment: The UE receives an NCC value that is + * different from the NCC associated with the currently active KgNB. The NH chain is synchronized + * by computing the function defined in Annex A.10 iteratively (and increasing the NCC value until + * it matches the NCC value received from the source ng-gNB). + * + * @param kamf K_AMF key from NAS + * @param kgnb Current KgNB key (input/output) + * @param nh Current NH parameter (input/output) + * @param nhcc Pointer to current nextHopChainingCount (input/output) + * @param target_ncc Target nextHopChainingCount value to synchronize to */ +static void nr_sync_nh_chain(const uint8_t kamf[SECURITY_KEY_LEN], + uint8_t kgnb[SECURITY_KEY_LEN], + uint8_t nh[SECURITY_KEY_LEN], + uint64_t *nhcc, + const int8_t target_ncc) { - uint8_t nh[SECURITY_KEY_LEN] = {0}; - nr_derive_nh(kamf, sync_input, nh); - log_hex_buffer("Sync input = stored NH", rrc->nh, SECURITY_KEY_LEN); - memcpy(rrc->nh, nh, SECURITY_KEY_LEN); - rrc->nhcc++; // Increase stored nextHopChainingCount + // If the UE received an NCC value that was different from the NCC associated with the + // currently active KgNB, the UE shall first synchronize the locally kept NH + // parameter by computing the function defined in Annex A.10 iteratively (and increasing + // the NCC value until it matches the NCC value received from the source ng-gNB). + if (target_ncc <= *nhcc) { + LOG_W(NR_RRC, "Received NCC=%d is less than or equal to current nhcc=%ld (nothing to sync)\n", target_ncc, *nhcc); + return; + } + if (target_ncc > *nhcc) { + LOG_I(NR_RRC, "Synchronizing NH chain: current nhcc=%ld, target ncc=%d\n", *nhcc, target_ncc); + if (*nhcc == 0) { + // First derivation: derive KgNB from KAMF, then derive NH from KgNB (per TS 33.501 A.10) + // Note: For the first NH derivation, we use UL NAS COUNT = 0 to match the AMF's derivation + // during handover. The AMF derives the first NH using the UL NAS COUNT from the last + // successful NAS SMC, which is 0 for the initial derivation. + derive_kgnb(kamf, 0, kgnb); + nr_derive_nh(kamf, kgnb, nh); + *nhcc = 1; + } + // Following derivations: iterate from current nhcc to target_ncc + for (uint8_t i = *nhcc; i < target_ncc; i++) { + LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i); + nr_derive_nh(kamf, nh, nh); + } + *nhcc = target_ncc; + } +} + +/** @brief Derive KNG-RAN* (KgNB*) using horizontal derivation + * + * This function derives KNG-RAN* from the currently active KgNB (horizontal derivation) + * as specified in 3GPP TS 33.501 Annex A.11 and A.12. + * Used when NCC values match (no NH synchronization needed). + * + * @param pci Physical Cell ID + * @param nr_arfcn_dl NR ARFCN-DL + * @param kgnb Current KgNB key (input/output) + */ +static void nr_derive_kgnb_horizontal(const uint16_t pci, const uint64_t nr_arfcn_dl, uint8_t kgnb[SECURITY_KEY_LEN]) +{ + // When the NCC values match, the UE shall derive the KNG-RAN* from the currently active + // KgNB and the target PCI and its frequency ARFCN-DL using the function defined in Annex A.11 and A.12. + LOG_D(NR_RRC, "Deriving KNG-RAN* using horizontal derivation (from current KgNB)\n"); + nr_derive_key_ng_ran_star(pci, nr_arfcn_dl, kgnb, kgnb); +} + +/** @brief Derive KNG-RAN* (KgNB*) using vertical derivation + * + * This function derives KNG-RAN* from the synchronized NH parameter (vertical derivation) + * as specified in 3GPP TS 33.501 Annex A.11 and A.12. + * Used after NH chain synchronization when NCC values differ. + * + * @param pci Physical Cell ID + * @param nr_arfcn_dl NR ARFCN-DL + * @param kgnb Current KgNB key (input/output) + * @param nh Synchronized NH parameter (input) + */ +static void nr_derive_kgnb_vertical(const uint16_t pci, + const uint64_t nr_arfcn_dl, + uint8_t kgnb[SECURITY_KEY_LEN], + const uint8_t nh[SECURITY_KEY_LEN]) +{ + // When the NCC values match (after synchronization), the UE shall compute the K NG-RAN * + // from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL + // using the function defined in Annex A.11 and A.12. + LOG_D(NR_RRC, "Deriving KNG-RAN* using vertical derivation (from synchronized NH)\n"); + nr_derive_key_ng_ran_star(pci, nr_arfcn_dl, nh, kgnb); +} + +/** @brief Update KgNB based on received nextHopChainingCount + * + * This function implements the common logic for updating KgNB based on received NCC value, + * as specified in 3GPP TS 33.501 6.9.2.3.4. + * + * Per 33.501 6.9.2.3.4 and Fig 6.9.2.1.1-1: + * - If NCC received == current NCC: use horizontal derivation (from currently active KgNB) + * This applies regardless of whether NCC is 0 or >0. For a fixed NCC level, multiple + * horizontal derivations can be done within that level. + * - If NCC received > current NCC: synchronize NH chain (Annex A.10), then use vertical + * derivation (from NH) to enter the new NCC level. + * + * Horizontal derivation = derive KNG-RAN* from currently active KgNB + (PCI, ARFCN-DL) + * Vertical derivation = derive KNG-RAN* from NH + (PCI, ARFCN-DL) + * + * @param rrc RRC instance pointer + * @param kamf K_AMF key from NAS + * @param received_ncc Received nextHopChainingCount value */ +static void nr_update_kgnb_from_ncc(NR_UE_RRC_INST_t *rrc, const uint8_t kamf[SECURITY_KEY_LEN], int8_t received_ncc) +{ + const uint64_t original_nhcc = rrc->nhcc; + + if (received_ncc == original_nhcc) { + // NCC values match: use horizontal derivation from currently active KgNB + // Per TS 33.501 6.9.2.3.4: "derive the KNG-RAN* from the currently active KgNB" + // This applies regardless of NCC being 0 or >0. For a fixed NCC level, we stay + // within that level using horizontal derivations. + LOG_D(NR_RRC, "NCC values match (%d), using horizontal derivation\n", received_ncc); + nr_derive_kgnb_horizontal(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb); + } else if (received_ncc < original_nhcc) { + // Note: According to spec, NCC should only increase. If received_ncc < original_nhcc, + // this is an error condition, but we handle it gracefully. + LOG_W(NR_RRC, + "Received NCC=%d is less than current nhcc=%ld (unexpected per spec, NH chain should only increase)\n", + received_ncc, + original_nhcc); + } else { + // received_ncc > original_nhcc: synchronize NH chain first (per 33.501 A.10) + nr_sync_nh_chain(kamf, rrc->kgnb, rrc->nh, &rrc->nhcc, received_ncc); + // Store the received nextHopChainingCount value (per 38.331 5.3.7.5) + LOG_D(NR_RRC, "Synchronizing NH chain to target NCC %d\n", received_ncc); + rrc->nhcc = received_ncc; + // After synchronization, derive KNG-RAN* from synchronized NH (vertical derivation) + // per 33.501 6.9.2.3.4: "When the NCC values match, the UE shall compute the K NG-RAN * + // from the synchronized NH parameter" + nr_derive_kgnb_vertical(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->nh); + } } /** @brief AS security key update procedure (5.3.5.7 3GPP TS 38.331) */ @@ -543,40 +671,17 @@ void as_security_key_update(NR_UE_RRC_INST_t *rrc, NR_MasterKeyUpdate_t *mku) if (mku->keySetChangeIndicator) { LOG_E(NR_RRC, "derive or update the K gNB key based on the K AMF key, as specified in TS 33.501: not implemented yet\n"); } else { - /* derive or update the K gNB key based on the current K gNB key or the NH, using the nextHopChainingCount - value indicated in the received masterKeyUpdate, as specified in 6.9.2.3.3 3GPP TS 33.501 */ - if (mku->nextHopChainingCount != rrc->nhcc) { - // - If the UE received an NCC value that was different from the NCC associated with the currently active - // K gNB/K eNB, the UE shall first synchronize the locally kept NH parameter by computing the function defined in - // Annex A.10 iteratively (and increasing the NCC value until it matches the NCC value received from the source - // ng-eNB/gNB via the HO command message. - LOG_A(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount); - /** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized - * with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig - * processing that triggers bearer modifications. Security configueration of bearers must - * complete using the newly derived keys. As a workaround NAS is directly accessed here. */ - nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id); - uint8_t *kamf = nas->security.kamf; - log_hex_buffer("Stored kamf", kamf, SECURITY_KEY_LEN); - if (rrc->nhcc == 0) { // First derivation - derive_kgnb(kamf, 0, rrc->kgnb); - log_hex_buffer("Sync input = derived kgnb", rrc->kgnb, SECURITY_KEY_LEN); - nr_rrc_nh_update(rrc, kamf, rrc->kgnb); - } - for (int i = rrc->nhcc; i < mku->nextHopChainingCount; i++) { // Following derivations - LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i); - nr_rrc_nh_update(rrc, kamf, rrc->nh); - } - nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->nh, rrc->kgnb); - // When the NCC values match, the UE shall compute the K NG-RAN * - // from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL/EARFCN-DL using the - // function defined in Annex A.11 and A.12. - // The UE shall use the KNG-RAN * as the K gNB when communicating with the target gNB and as the KeNB when - // communicating with the target ng-eNB. - } else { - nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->kgnb); - } - log_hex_buffer("Derived kgnb", rrc->kgnb, SECURITY_KEY_LEN); + LOG_I(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount); + /** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized + * with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig + * processing that triggers bearer modifications. Security configueration of bearers must + * complete using the newly derived keys. As a workaround NAS is directly accessed here. */ + nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id); + const uint8_t *kamf = nas->security.kamf; + // KgNB update (TS 33.501 §6.9.2.3.3): + // If received NCC != local NCC, iteratively derive NH (Annex A.10) until NCC matches. + // Then derive the new KgNB from the synchronized NH. + nr_update_kgnb_from_ncc(rrc, kamf, mku->nextHopChainingCount); } } @@ -2196,6 +2301,9 @@ static void nr_rrc_ue_generate_rrcReestablishmentComplete(const NR_UE_RRC_INST_t nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL); } +/** @brief Process RRCReestablishment message + * This function processes the RRCReestablishment message received from the gNB, + * implementing procedures as described in 38.331 section 5.3.7.5 */ static void nr_rrc_ue_process_rrcReestablishment(NR_UE_RRC_INST_t *rrc, const int gNB_index, const NR_RRCReestablishment_t *rrcReestablishment, @@ -2204,18 +2312,19 @@ static void nr_rrc_ue_process_rrcReestablishment(NR_UE_RRC_INST_t *rrc, int msg_size, const nr_pdcp_integrity_data_t *msg_integrity) { - // implementign procedues as described in 38.331 section 5.3.7.5 // stop timer T301 NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants; nr_timer_stop(&timers->T301); - // store the nextHopChainingCount value NR_RRCReestablishment_IEs_t *ies = rrcReestablishment->criticalExtensions.choice.rrcReestablishment; AssertFatal(ies, "Not expecting RRCReestablishment_IEs to be NULL\n"); - // TODO need to understand how to use nextHopChainingCount - // int nh = rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nextHopChainingCount; - // update the K gNB key based on the current K gNB key or the NH, using the stored nextHopChainingCount value - nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->kgnb); + // Update KgNB based on the current K gNB key or the NH, + // using the received nextHopChainingCount (per 33.501 6.9.2.3.4) + // received from RRCReestablishment and update the stored value in rrc->nhcc + int8_t received_ncc = ies->nextHopChainingCount; + nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id); + const uint8_t *kamf = nas->security.kamf; + nr_update_kgnb_from_ncc(rrc, kamf, received_ncc); // derive the K_RRCenc key associated with the previously configured cipheringAlgorithm // derive the K_RRCint key associated with the previously configured integrityProtAlgorithm diff --git a/openair3/SECU/key_nas_deriver.c b/openair3/SECU/key_nas_deriver.c index 3fb35adee2..a2948ab0b2 100644 --- a/openair3/SECU/key_nas_deriver.c +++ b/openair3/SECU/key_nas_deriver.c @@ -150,8 +150,23 @@ void nr_derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, const uint8_t * NOTE: knas is dynamically allocated by the KDF function */ -/** @brief KgNB derivation (A.9 3GPP TS 33.501) */ -void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb) +/** @brief KgNB derivation function (Annex A.9 3GPP TS 33.501) + * + * Derives KgNB from K_AMF and the uplink NAS COUNT. + * This function is applied when cryptographically protected 5G radio bearers are + * established and when a key change on-the-fly is performed. + * + * KDF input S structure (per Annex A.9): + * - FC = 0x6E + * - P0 = Uplink NAS COUNT (count parameter) + * - L0 = length of uplink NAS COUNT (0x00 0x04) + * - P1 = Access type distinguisher (0x01 for 3GPP access) + * - L1 = length of Access type distinguisher (0x00 0x01) + * + * @param[in] kamf 256-bit K_AMF key (input key KEY to KDF) + * @param[in] count Uplink NAS COUNT value (P0 in KDF input S) + * @param[out] kgnb Derived 256-bit KgNB key */ +void derive_kgnb(const uint8_t kamf[32], const uint32_t count, uint8_t *kgnb) { /* Compute the KDF input parameter * S = FC(0x6E) || UL NAS Count || 0x00 0x04 || 0x01 || 0x00 0x01 diff --git a/openair3/SECU/key_nas_deriver.h b/openair3/SECU/key_nas_deriver.h index eff72c68bb..100d554aa2 100644 --- a/openair3/SECU/key_nas_deriver.h +++ b/openair3/SECU/key_nas_deriver.h @@ -16,7 +16,7 @@ typedef enum { UP_INT_ALG = 0x06 } algorithm_type_dist_t; -void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb); +void derive_kgnb(const uint8_t kamf[32], const uint32_t count, uint8_t *kgnb); void derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t *keNB); From 5c601b7d624d97a0f927a2076f7fc48ec1f8e126 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 19 Dec 2025 16:59:18 +0100 Subject: [PATCH 03/23] Fix RRCReestablishment key derivation to use horizontal derivation Per TS 33.501 6.9.2.3.4 and Fig 6.9.2.1.1-1, when sending RRCReestablishment with nextHopChainingCount, the gNB is staying at the same NCC level (not advancing). Therefore, horizontal derivation from the currently active KgNB must be used, not vertical derivation from NH. Vertical derivation (from NH) is only used when advancing to a new NCC level, which happens during handover or masterKeyUpdate procedures. The previous implementation incorrectly used vertical derivation from NH whenever nh_ncc > 0, which caused key mismatch between gNB and UE during reestablishment after handover, leading to integrity check failures. This fix ensures both gNB and UE use horizontal derivation when nextHopChainingCount matches the current NCC level, aligning with the specification's "virtual NH at NCC=0" model where: - Horizontal derivation = derive KNG-RAN* from currently active KgNB - Vertical derivation = derive KNG-RAN* from NH (only when advancing NCC) --- openair2/RRC/NR/rrc_gNB.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6831d97869..3b8476f9a4 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1093,8 +1093,11 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context uint32_t ssb_arfcn = get_ssb_arfcn(du); LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", cell_info->nr_pci, ssb_arfcn); - /* Derive KgNB* using NH if nh_ncc > 0 (vertical derivation), else use KgNB (horizontal derivation) */ - const uint8_t *base_key = ue_p->nh_ncc > 0 ? ue_p->nh : ue_p->kgnb; + /* Per TS 33.501 6.9.2.3.4: When sending RRCReestablishment with nextHopChainingCount, + * we're staying at the same NCC level (not advancing), so use horizontal derivation + * from the currently active KgNB. Vertical derivation (from NH) is only used when + * advancing to a new NCC level, which happens during handover or masterKeyUpdate. */ + const uint8_t *base_key = ue_p->kgnb; nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, base_key, ue_p->kgnb); int size = do_RRCReestablishment(ue_context_pP->ue_context.nh_ncc, buffer, NR_RRC_BUF_SIZE, xid); From 75367249c580e510940747fa70c45c9a6713812b Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 19 Dec 2025 15:31:01 +0100 Subject: [PATCH 04/23] cleanup of functions in nr_ue_scheduler (no code change, just moving things around) --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 1528 +++++++++---------- 1 file changed, 753 insertions(+), 775 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index c21a0ce3bd..2818c20c37 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -57,30 +57,6 @@ AssertFatal(ret == 0, "Failure in mutex management ret=%d\n", a); \ } -static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t slotP); -static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac); - -static void nr_ue_fill_phr(NR_UE_MAC_INST_t *mac, - NR_SINGLE_ENTRY_PHR_MAC_CE *phr, - float P_CMAX, - float tx_power, - frame_t frame, - slot_t slot); -static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, - int cc_id, - frame_t frame, - slot_t slot, - uint8_t gNB_index, - uint8_t *ulsch_buffer, - const uint32_t buflen, - int tx_power, - int P_MAX, - bool *BSRsent); - -static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, int slotP); -static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot); -static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot); - static void clear_ul_config_request(NR_UE_MAC_INST_t *mac) { int slots = mac->frame_structure.numb_slots_frame; @@ -1131,6 +1107,205 @@ static bool nr_ue_periodic_srs_scheduling(NR_UE_MAC_INST_t *mac, frame_t frame, return srs_scheduled; } +static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac) +{ + NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; + fapi_nr_ta_command_pdu *ta = &dl_config->dl_config_list[dl_config->number_pdus].ta_command_pdu; + ta->ta_frame = ul_time_alignment->frame; + ta->ta_slot = ul_time_alignment->slot; + ta->is_rar = ul_time_alignment->ta_apply == rar_ta; + ta->ta_command = ul_time_alignment->ta_command; + dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_CONFIG_TA_COMMAND; + dl_config->number_pdus += 1; + ul_time_alignment->ta_apply = no_ta; +} + +static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *csi_measconfig, + NR_BWP_Id_t dl_bwp_id, + NR_NZP_CSI_RS_ResourceId_t csi_id) +{ + bool found = false; + for (int csi_list = 0; csi_list < csi_measconfig->csi_ResourceConfigToAddModList->list.count; csi_list++) { + NR_CSI_ResourceConfig_t *csires = csi_measconfig->csi_ResourceConfigToAddModList->list.array[csi_list]; + if(csires->bwp_Id != dl_bwp_id) + continue; + struct NR_CSI_ResourceConfig__csi_RS_ResourceSetList *resset = &csires->csi_RS_ResourceSetList; + if(resset->present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) + continue; + if(!resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList) + continue; + for(int i = 0; i < resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.count; i++) { + NR_NZP_CSI_RS_ResourceSetId_t *res_id = resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[i]; + AssertFatal(res_id, "NR_NZP_CSI_RS_ResourceSetId shouldn't be NULL\n"); + struct NR_CSI_MeasConfig__nzp_CSI_RS_ResourceSetToAddModList *res_list = csi_measconfig->nzp_CSI_RS_ResourceSetToAddModList; + AssertFatal(res_list, "nzp_CSI_RS_ResourceSetToAddModList shouldn't be NULL\n"); + for (int j = 0; j < res_list->list.count; j++) { + NR_NZP_CSI_RS_ResourceSet_t *csi_res = res_list->list.array[j]; + if (*res_id != csi_res->nzp_CSI_ResourceSetId) + continue; + for (int k = 0; k < csi_res->nzp_CSI_RS_Resources.list.count; k++) { + AssertFatal(csi_res->nzp_CSI_RS_Resources.list.array[k], "NZP_CSI_RS_ResourceId shoulan't be NULL\n"); + if (csi_id == *csi_res->nzp_CSI_RS_Resources.list.array[k]) { + found = true; + break; + } + } + if (found && csi_res->trs_Info) + // CRI-RS for Tracking (not implemented yet) + // in this case we there is no associated CSI report + // therefore to signal this we return a value higher than + // maxNrofCSI-ResourceConfigurations + return NR_maxNrofCSI_ResourceConfigurations + 1; + else if (found) + return csires->csi_ResourceConfigId; + } + } + } + return -1; // not found any CSI-resource in current DL BWP associated with this CSI-RS ID +} + +static uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI_ResourceConfigId_t csi_res_id) +{ + uint8_t meas_bitmap = 0; + if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) + return meas_bitmap; // CSI-RS for tracking + for(int i = 0; i < csi_measconfig->csi_ReportConfigToAddModList->list.count; i++) { + NR_CSI_ReportConfig_t *report_config = csi_measconfig->csi_ReportConfigToAddModList->list.array[i]; + if(report_config->resourcesForChannelMeasurement != csi_res_id) + continue; + // bit 0 RSRP bit 1 RI bit 2 LI bit 3 PMI bit 4 CQI bit 5 i1 + switch (report_config->reportQuantity.present) { + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI: + meas_bitmap += (1 << 1) + (1 << 3) + (1 << 4); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1: + meas_bitmap += (1 << 1) + (1 << 5); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1_CQI: + meas_bitmap += (1 << 1) + (1 << 4) + (1 << 5); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI: + meas_bitmap += (1 << 1) + (1 << 4); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP: + meas_bitmap += 1; + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI: + meas_bitmap += (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4); + break; + default: + AssertFatal(false, "Unexpected measurement report type %d\n", report_config->reportQuantity.present); + } + } + return meas_bitmap; +} + +static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot) +{ + if (!mac->sc_info.csi_MeasConfig) + return; + + NR_CSI_MeasConfig_t *csi_measconfig = mac->sc_info.csi_MeasConfig; + + if (csi_measconfig->nzp_CSI_RS_ResourceToAddModList == NULL) + return; + + fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot); + NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; + NR_BWP_Id_t dl_bwp_id = current_DL_BWP->bwp_id; + + int mu = current_DL_BWP->scs; + uint16_t bwp_size = current_DL_BWP->BWPSize; + uint16_t bwp_start = current_DL_BWP->BWPStart; + + for (int id = 0; id < csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.count; id++) { + NR_NZP_CSI_RS_Resource_t *nzpcsi = csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.array[id]; + int period, offset; + csi_period_offset(NULL, nzpcsi->periodicityAndOffset, &period, &offset); + if((frame * mac->frame_structure.numb_slots_frame + slot-offset) % period != 0) + continue; + NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId); + // do not schedule reseption of this CSI-RS if not associated with current BWP + if(csi_res_id < 0) + continue; + LOG_D(MAC,"Scheduling reception of CSI-RS in frame %d slot %d\n", frame, slot); + fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu = &dl_config->dl_config_list[dl_config->number_pdus].csirs_config_pdu.csirs_config_rel15; + csirs_config_pdu->measurement_bitmap = set_csirs_measurement_bitmap(csi_measconfig, csi_res_id); + csirs_config_pdu->subcarrier_spacing = mu; + csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0; + + if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) + csirs_config_pdu->csi_type = 0; // TRS + else + csirs_config_pdu->csi_type = 1; // NZP-CSI-RS + + csirs_config_pdu->scramb_id = nzpcsi->scramblingID; + csirs_config_pdu->power_control_offset = nzpcsi->powerControlOffset + 8; + if (nzpcsi->powerControlOffsetSS) + csirs_config_pdu->power_control_offset_ss = *nzpcsi->powerControlOffsetSS; + else + csirs_config_pdu->power_control_offset_ss = 1; // 0 dB + + configure_csi_resource_mapping(csirs_config_pdu, &nzpcsi->resourceMapping, bwp_size, bwp_start); + + dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_CSI_RS; + dl_config->number_pdus += 1; + } +} + +static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot) +{ + if (!mac->sc_info.csi_MeasConfig) + return; + + NR_CSI_MeasConfig_t *csi_measconfig = mac->sc_info.csi_MeasConfig; + + if (csi_measconfig->csi_IM_ResourceToAddModList == NULL) + return; + + fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot); + NR_CSI_IM_Resource_t *imcsi; + int period, offset; + + NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; + int mu = current_DL_BWP->scs; + uint16_t bwp_size = current_DL_BWP->BWPSize; + uint16_t bwp_start = current_DL_BWP->BWPStart; + + for (int id = 0; id < csi_measconfig->csi_IM_ResourceToAddModList->list.count; id++){ + imcsi = csi_measconfig->csi_IM_ResourceToAddModList->list.array[id]; + csi_period_offset(NULL,imcsi->periodicityAndOffset,&period,&offset); + if((frame * mac->frame_structure.numb_slots_frame + slot - offset ) %period != 0) + continue; + fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu = &dl_config->dl_config_list[dl_config->number_pdus].csiim_config_pdu.csiim_config_rel15; + csiim_config_pdu->bwp_size = bwp_size; + csiim_config_pdu->bwp_start = bwp_start; + csiim_config_pdu->subcarrier_spacing = mu; + csiim_config_pdu->start_rb = imcsi->freqBand->startingRB; + csiim_config_pdu->nr_of_rbs = imcsi->freqBand->nrofRBs; + // As specified in 5.2.2.4 of 38.214 + switch (imcsi->csi_IM_ResourceElementPattern->present) { + case NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern0: + for (int i = 0; i < 4; i++) { + csiim_config_pdu->k_csiim[i] = + (imcsi->csi_IM_ResourceElementPattern->choice.pattern0->subcarrierLocation_p0 << 1) + (i >> 1); + csiim_config_pdu->l_csiim[i] = imcsi->csi_IM_ResourceElementPattern->choice.pattern0->symbolLocation_p0 + (i % 2); + } + break; + case NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1: + for (int i = 0; i < 4; i++) { + csiim_config_pdu->k_csiim[i] = (imcsi->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 << 2) + i; + csiim_config_pdu->l_csiim[i] = imcsi->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1; + } + break; + default: + AssertFatal(1 == 0, "Invalid CSI-IM pattern\n"); + } + dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_CSI_IM; + dl_config->number_pdus += 1; + } +} + // Performs : // 1. TODO: Call RRC for link status return to PHY // 2. TODO: Perform SR/BSR procedures for scheduling feedback @@ -1341,260 +1516,179 @@ static void nr_update_bsr(NR_UE_MAC_INST_t *mac, uint32_t *LCG_bytes) } } -void nr_ue_ul_scheduler(NR_UE_MAC_INST_t *mac, nr_uplink_indication_t *ul_info) +static bool is_prach_frame(frame_t frame, prach_occasion_info_t *prach_occasion_info, int association_periods) { - int cc_id = ul_info->cc_id; - frame_t frame_tx = ul_info->frame; - slot_t slot_tx = ul_info->slot; - uint32_t gNB_index = ul_info->gNB_index; + int config_period = prach_occasion_info->frame_info[0]; + int frame_period = config_period * association_periods; + int frame_in_period = frame % frame_period; + if (association_periods > 1) { + int current_assoc_period = frame_in_period / config_period; + if (current_assoc_period != prach_occasion_info->association_period_idx) + return false; + frame_in_period %= config_period; + } + if (frame_in_period == prach_occasion_info->frame_info[1]) // SFN % x == y (see Tables in Table 6.3.3.2 of 38.211) + return true; + else + return false; +} +// This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x +// PRACH formats 9, 10, 11 are corresponding to dual PRACH format configurations A1/B1, A2/B2, A3/B3. +// - todo: +// - Partial configuration is actually already stored in (fapi_nr_prach_config_t) &mac->phy_config.config_req->prach_config +static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t slotP) +{ RA_config_t *ra = &mac->ra; - if (mac->state == UE_PERFORMING_RA && ra->ra_state == nrRA_UE_IDLE) { - init_RA(mac, frame_tx); - // perform the Random Access Resource selection procedure (see clause 5.1.2 and .2a) - ra_resource_selection(mac); - } + // Get any valid PRACH occasion in the current slot for the selected SSB index + prach_occasion_info_t *prach_occasion_info = &ra->sched_ro_info; - if (mac->state == UE_PERFORMING_RA && ra->ra_state == nrRA_GENERATE_PREAMBLE) - nr_ue_prach_scheduler(mac, frame_tx, slot_tx); - - bool BSRsent = false; - if (mac->state == UE_CONNECTED) { - nr_ue_periodic_srs_scheduling(mac, frame_tx, slot_tx); - nr_update_rlc_buffers_status(mac, frame_tx, slot_tx, gNB_index); - } - - // Schedule ULSCH only if the current frame and slot match those in ul_config_req - // AND if a UL grant (UL DCI or Msg3) has been received (as indicated by num_pdus) - uint8_t ulsch_input_buffer_array[NFAPI_MAX_NUM_UL_PDU][MAX_ULSCH_PAYLOAD_BYTES]; - int number_of_pdus = 0; - - fapi_nr_ul_config_request_pdu_t *ulcfg_pdu = lockGet_ul_iterator(mac, frame_tx, slot_tx); - if (!ulcfg_pdu) + if (!is_prach_frame(frameP, prach_occasion_info, ra->association_periods)) return; - LOG_D(NR_MAC, "number of UL PDUs: %d with UL transmission in sfn [%d.%d]\n", *ulcfg_pdu->privateNBpdus, frame_tx, slot_tx); - while (ulcfg_pdu->pdu_type != FAPI_NR_END) { - uint8_t *ulsch_input_buffer = ulsch_input_buffer_array[number_of_pdus]; - if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) { - nfapi_nr_ue_pusch_pdu_t *pdu = &ulcfg_pdu->pusch_config_pdu; - uint32_t TBS_bytes = pdu->pusch_data.tb_size; - LOG_D(NR_MAC, - "harq_id %d, new_data_indicator %d, TBS_bytes %d (ra_state %d)\n", - pdu->pusch_data.harq_process_id, - pdu->pusch_data.new_data_indicator, - TBS_bytes, - ra->ra_state); - pdu->tx_request_body.fapiTxPdu = NULL; - if ((ra->ra_state == nrRA_WAIT_RAR || ra->ra_state == nrRA_WAIT_MSGB) && !ra->cfra) { - nr_get_Msg3_MsgA_PUSCH_payload(mac, ulsch_input_buffer, TBS_bytes); - for (int k = 0; k < TBS_bytes; k++) { - LOG_D(NR_MAC, "(%i): 0x%x\n", k, ulsch_input_buffer[k]); + if (is_ul_slot(slotP, &mac->frame_structure)) { + if (slotP == prach_occasion_info->slot) { + fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frameP, slotP, FAPI_NR_UL_CONFIG_TYPE_PRACH); + if (!pdu) { + LOG_E(NR_MAC, "Error in PRACH allocation\n"); + return; + } + + int format = prach_occasion_info->format; + fapi_nr_prach_config_t *prach_config = &mac->phy_config.config_req.prach_config; + pdu->prach_config_pdu = (fapi_nr_ul_config_prach_pdu){ + .phys_cell_id = mac->physCellId, + .num_prach_ocas = 1, + .prach_slot = prach_occasion_info->slot, + .prach_start_symbol = prach_occasion_info->start_symbol, + .num_ra = prach_occasion_info->fdm, + .num_cs = get_NCS(ra->zeroCorrelationZoneConfig, format, ra->restricted_set_config), + .root_seq_id = prach_config->num_prach_fd_occasions_list[prach_occasion_info->fdm].prach_root_sequence_index, + .restricted_set = prach_config->restricted_set_config, + .freq_msg1 = prach_config->num_prach_fd_occasions_list[prach_occasion_info->fdm].k1}; + + LOG_I(NR_MAC, + "PRACH scheduler: Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", + frameP, + pdu->prach_config_pdu.prach_slot, + pdu->prach_config_pdu.prach_start_symbol, + pdu->prach_config_pdu.num_ra); + + switch (format) { // single PRACH format + case 0: + pdu->prach_config_pdu.prach_format = 0; + break; + case 1: + pdu->prach_config_pdu.prach_format = 1; + break; + case 2: + pdu->prach_config_pdu.prach_format = 2; + break; + case 3: + pdu->prach_config_pdu.prach_format = 3; + break; + case 0xa1: + pdu->prach_config_pdu.prach_format = 4; + break; + case 0xa2: + pdu->prach_config_pdu.prach_format = 5; + break; + case 0xa3: + pdu->prach_config_pdu.prach_format = 6; + break; + case 0xb1: + pdu->prach_config_pdu.prach_format = 7; + break; + case 0xb4: + pdu->prach_config_pdu.prach_format = 8; + break; + case 0xc0: + pdu->prach_config_pdu.prach_format = 9; + break; + case 0xc2: + pdu->prach_config_pdu.prach_format = 10; + break; + default: + AssertFatal(false, "Invalid PRACH format"); + } + + pdu->prach_config_pdu.ra_PreambleIndex = ra->ra_PreambleIndex; + pdu->prach_config_pdu.prach_tx_power = get_prach_tx_power(mac); + release_ul_config(pdu, false); + nr_scheduled_response_t scheduled_response = {.ul_config = mac->ul_config_request + slotP, + .mac = mac, + .module_id = mac->ue_id, + .CC_id = 0 /*TBR fix*/}; + if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) + mac->if_module->scheduled_response(&scheduled_response); + + T(T_UE_PHY_INITIATE_RA_PROCEDURE, T_INT(frameP), T_INT(pdu->prach_config_pdu.prach_slot), + T_INT(pdu->prach_config_pdu.ra_PreambleIndex), T_INT(pdu->prach_config_pdu.prach_tx_power)); + + const int n_slots_frame = mac->frame_structure.numb_slots_frame; + if (ra->ra_type == RA_4_STEP) { + ra->ra_state = nrRA_WAIT_RAR; + // we start to monitor DCI for RAR in the first valid occasion after transmitting RACH + // the response window timer should be started at that time + // but for processing reasons it is better to start it here and to add the slot difference + // that also takes into account the rx to tx slot offset + int next_slot = (slotP + 1) % n_slots_frame; + int next_frame = (frameP + (next_slot < slotP)) % MAX_FRAME_NUMBER; + int add_slots = 1; + NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[mac->current_DL_BWP->bwp_id]; + const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id); + while (!is_dl_slot(next_slot, &mac->frame_structure) + || !is_ss_monitor_occasion(next_frame, next_slot, n_slots_frame, ra_SS)) { + int temp_slot = (next_slot + 1) % n_slots_frame; + next_frame = (next_frame + (temp_slot < next_slot)) % MAX_FRAME_NUMBER; + next_slot = temp_slot; + add_slots++; } - pdu->tx_request_body.fapiTxPdu = ulsch_input_buffer; - pdu->tx_request_body.pdu_length = TBS_bytes; - number_of_pdus++; - T(T_NRUE_MAC_UL_PDU_WITH_DATA, T_INT(mac->crnti), T_INT(frame_tx), T_INT(slot_tx), - T_INT(ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id), T_BUFFER(ulsch_input_buffer, TBS_bytes)); + add_slots += GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mac->current_DL_BWP->scs); + nr_timer_setup(&ra->response_window_timer, ra->response_window_setup_time + add_slots, 1); + nr_timer_start(&ra->response_window_timer); + } else if (ra->ra_type == RA_2_STEP) { + NR_MsgA_PUSCH_Resource_r16_t *msgA_PUSCH_Resource = + mac->current_UL_BWP->msgA_ConfigCommon_r16->msgA_PUSCH_Config_r16->msgA_PUSCH_ResourceGroupA_r16; + slot_t msgA_pusch_slot = (slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) % n_slots_frame; + frame_t msgA_pusch_frame = + (frameP + ((slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) / n_slots_frame)) % 1024; + + fapi_nr_ul_config_request_pdu_t *pdu = + lockGet_ul_config(mac, msgA_pusch_frame, msgA_pusch_slot, FAPI_NR_UL_CONFIG_TYPE_PUSCH); + if (!pdu) + return; + + // Config Msg3/MsgA-PUSCH PDU + NR_tda_info_t tda_info = {0}; + AssertFatal(msgA_PUSCH_Resource->startSymbolAndLengthMsgA_PO_r16, + "Only SLIV based on startSymbolAndLengthMsgA_PO_r16 implemented\n"); + + int ret = nr_config_pusch_pdu(mac, + &tda_info, + &pdu->pusch_config_pdu, + NULL, + NULL, + NULL, + mac->ra.ra_rnti, + NR_SearchSpace__searchSpaceType_PR_common, + NR_DCI_NONE); + + if (ret != 0) + remove_ul_config_last_item(pdu); + release_ul_config(pdu, false); + + LOG_D(NR_MAC, "ra->ra_state %s\n", nrra_ue_text[ra->ra_state]); + ra->ra_state = nrRA_WAIT_MSGB; + ra->t_crnti = 0; + mac->crnti = 0; + ra->ra_rnti = 0; } else { - if (ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator - && (mac->state == UE_CONNECTED || (ra->ra_state == nrRA_WAIT_RAR && ra->cfra))) { - if (!nr_timer_is_active(&mac->time_alignment_timer) && mac->state == UE_CONNECTED && !get_softmodem_params()->phy_test) { - // UL data arrival during RRC_CONNECTED when UL synchronisation status is "non-synchronised" - trigger_MAC_UE_RA(mac, NULL); - return; - } - // Getting IP traffic to be transmitted - int tx_power = pdu->tx_power; - int P_CMAX = nr_get_Pcmax(mac->p_Max, - mac->nr_band, - mac->frame_structure.frame_type, - mac->frequency_range, - mac->current_UL_BWP->channel_bandwidth, - pdu->qam_mod_order, - false, - mac->current_UL_BWP->scs, - mac->current_UL_BWP->BWPSize, - pdu->transform_precoding, - pdu->rb_size, - pdu->rb_start); - - nr_ue_get_sdu(mac, cc_id, frame_tx, slot_tx, gNB_index, ulsch_input_buffer, TBS_bytes, tx_power, P_CMAX, &BSRsent); - pdu->tx_request_body.fapiTxPdu = ulsch_input_buffer; - pdu->tx_request_body.pdu_length = TBS_bytes; - number_of_pdus++; - T(T_NRUE_MAC_UL_PDU_WITH_DATA, T_INT(mac->crnti), T_INT(frame_tx), T_INT(slot_tx), - T_INT(ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id), T_BUFFER(ulsch_input_buffer, TBS_bytes)); - // start or restart dataInactivityTimer if any MAC entity transmits a MAC SDU for DTCH logical channel, - // or DCCH logical channel - if (mac->data_inactivity_timer) - nr_timer_start(mac->data_inactivity_timer); - } + AssertFatal(false, "RA type %d not implemented!\n", ra->ra_type); } - - if (ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION && !ra->cfra) { - LOG_I(NR_MAC, "[RAPROC][%d.%d] RA-Msg3 retransmitted\n", frame_tx, slot_tx); - // 38.321 restart the ra-ContentionResolutionTimer at each HARQ retransmission in the first symbol after the end of the Msg3 - // transmission - nr_Msg3_transmitted(mac, cc_id, frame_tx, slot_tx, gNB_index); - } - if (ra->ra_state == nrRA_WAIT_RAR && !ra->cfra) { - LOG_A(NR_MAC, "[RAPROC][%d.%d] RA-Msg3 transmitted\n", frame_tx, slot_tx); - nr_Msg3_transmitted(mac, cc_id, frame_tx, slot_tx, gNB_index); - } - if (ra->ra_state == nrRA_WAIT_MSGB && !ra->cfra) { - LOG_A(NR_MAC, "[RAPROC][%d.%d] RA-MsgA-PUSCH transmitted\n", frame_tx, slot_tx); - } - } - ulcfg_pdu++; - } - release_ul_config(ulcfg_pdu, false); - - if(mac->state >= UE_PERFORMING_RA && mac->state < UE_DETACHING) - nr_ue_pucch_scheduler(mac, frame_tx, slot_tx); - - if (mac->if_module != NULL && mac->if_module->scheduled_response != NULL) { - LOG_D(NR_MAC, "3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); - nr_scheduled_response_t scheduled_response = {.ul_config = mac->ul_config_request + slot_tx, - .mac = mac, - .module_id = mac->ue_id, - .CC_id = cc_id, - .phy_data = ul_info->phy_data}; - mac->if_module->scheduled_response(&scheduled_response); - } - - if(mac->state == UE_CONNECTED) - nr_update_sr(mac, BSRsent); - // Global variables implicit logic - // far away, BSR_reporting_active is set - mac->scheduling_info.BSR_reporting_active = NR_BSR_TRIGGER_NONE; - - // update Bj for all active lcids before LCP procedure - if (mac->current_UL_BWP) { - LOG_D(NR_MAC, "%4d.%2d Logical Channel Prioritization\n", frame_tx, slot_tx); - for (int i = 0; i < mac->lc_ordered_list.count; i++) { - nr_lcordered_info_t *lc_info = mac->lc_ordered_list.array[i]; - int lcid = lc_info->lcid; - // max amount of data that can be buffered/accumulated in a logical channel buffer - uint32_t bucketSize_max = lc_info->bucket_size; - // measure Bj increment the value of Bj by product PBR * T - NR_LC_SCHEDULING_INFO *sched_info = get_scheduling_info_from_lcid(mac, lcid); - int32_t bj = sched_info->Bj; - if (lc_info->pbr < UINT_MAX) { - uint32_t slots_elapsed = nr_timer_elapsed_time(&sched_info->Bj_timer); // slots elapsed since Bj was last incremented - // it is safe to divide by 1k since pbr in lc_info is computed multiplying by 1000 the RRC value to convert kB/s to B/s - uint32_t pbr_ms = lc_info->pbr / 1000; - bj += ((pbr_ms * slots_elapsed) >> mac->current_UL_BWP->scs); // each slot length is 1/scs ms - } - else - bj = INT_MAX; - // bj > max bucket size, set bj to max bucket size, as in ts38.321 5.4.3.1 Logical Channel Prioritization - sched_info->Bj = min(bj, bucketSize_max); - // reset bj timer - nr_timer_start(&sched_info->Bj_timer); - } - } -} - -static uint8_t nr_locate_BsrIndexByBufferSize(int size, int value) -{ - if (value == 0) { - return 0; //elseif (value > 150000) return 63; - } - - uint32_t (*get_bsr_value)(int); - if (size == NR_SHORT_BSR_TABLE_SIZE) - get_bsr_value = &get_short_bsr_value; - else - get_bsr_value = &get_long_bsr_value; - - int jl = 0; // lower bound - int ju = size - 1; // upper bound - // determine the order of the the table: 1 if ascending order of table, 0 otherwise - bool ascend = (*get_bsr_value)(ju) >= (*get_bsr_value)(jl) ? 1 : 0; - while (ju - jl > 1) { //If we are not yet done, - int jm = (ju + jl) / 2; // compute a midpoint, - if ((value >= (*get_bsr_value)(jm)) == ascend) { - jl = jm; // replace the lower limit - } else { - ju = jm; //replace the upper limit - } - - LOG_T(NR_MAC, - "[UE] searching BSR index %d for (BSR TABLE %d < value %d)\n", - jm, - (*get_bsr_value)(jm), - value); - } - - if (value == (*get_bsr_value)(jl)) { - return jl; - } else { - return jl + 1; //equally ju - } -} - -// PUSCH scheduler: -// - Calculate the slot in which ULSCH should be scheduled. This is current slot + K2, -// - where K2 is the offset between the slot in which UL DCI is received and the slot -// - in which ULSCH should be scheduled. K2 is configured in RRC configuration. -// PUSCH Msg3 scheduler: -// - scheduled by RAR UL grant according to 8.3 of TS 38.213 -// Note: Msg3 tx in the uplink symbols of mixed slot -int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac, - const uint8_t is_Msg3, - const frame_t current_frame, - const int current_slot, - frame_t *frame_tx, - int *slot_tx, - const long k2) -{ - int delta = 0; - const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; - - // Get the numerology to calculate the Tx frame and slot - int mu = current_UL_BWP->scs; - int slots_per_frame = mac->frame_structure.numb_slots_frame; - - // k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1 - // PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon - - if (is_Msg3) { - - delta = get_delta_for_k2(mu); - - AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - "Slot offset (%ld) for Msg3 needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to " - "%ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", - k2, - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); - - *slot_tx = (current_slot + k2 + delta) % slots_per_frame; - *frame_tx = (current_frame + (current_slot + k2 + delta) / slots_per_frame) % MAX_FRAME_NUMBER; - } else { - AssertFatal(k2 >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - "Slot offset K2 (%ld) needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in " - "gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", - k2, - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), - GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); - - if (k2 < 0) { // This can happen when a false DCI is received - LOG_W(PHY, "%d.%d. Received k2 %ld\n", current_frame, current_slot, k2); - return -1; - } - - // Calculate TX slot and frame - *slot_tx = (current_slot + k2) % slots_per_frame; - *frame_tx = (current_frame + (current_slot + k2) / slots_per_frame) % MAX_FRAME_NUMBER; - } - - LOG_D(NR_MAC, "[%04d.%02d] UL transmission in [%04d.%02d] (k2 %ld delta %d)\n", current_frame, current_slot, *frame_tx, *slot_tx, k2, delta); - return 0; + } // is_nr_prach_slot + } // if is_nr_UL_slot } static bool schedule_uci_on_pusch(NR_UE_MAC_INST_t *mac, @@ -1782,472 +1876,43 @@ static void nr_ue_pucch_scheduler(NR_UE_MAC_INST_t *mac, frame_t frame, int slot } } -static void nr_schedule_csi_for_im(NR_UE_MAC_INST_t *mac, int frame, int slot) +static void nr_ue_fill_phr(NR_UE_MAC_INST_t *mac, + NR_SINGLE_ENTRY_PHR_MAC_CE *phr, + float P_CMAX, + float tx_power, + frame_t frame, + slot_t slot) { - if (!mac->sc_info.csi_MeasConfig) - return; - - NR_CSI_MeasConfig_t *csi_measconfig = mac->sc_info.csi_MeasConfig; - - if (csi_measconfig->csi_IM_ResourceToAddModList == NULL) - return; - - fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot); - NR_CSI_IM_Resource_t *imcsi; - int period, offset; - - NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; - int mu = current_DL_BWP->scs; - uint16_t bwp_size = current_DL_BWP->BWPSize; - uint16_t bwp_start = current_DL_BWP->BWPStart; - - for (int id = 0; id < csi_measconfig->csi_IM_ResourceToAddModList->list.count; id++){ - imcsi = csi_measconfig->csi_IM_ResourceToAddModList->list.array[id]; - csi_period_offset(NULL,imcsi->periodicityAndOffset,&period,&offset); - if((frame * mac->frame_structure.numb_slots_frame + slot - offset ) %period != 0) - continue; - fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu = &dl_config->dl_config_list[dl_config->number_pdus].csiim_config_pdu.csiim_config_rel15; - csiim_config_pdu->bwp_size = bwp_size; - csiim_config_pdu->bwp_start = bwp_start; - csiim_config_pdu->subcarrier_spacing = mu; - csiim_config_pdu->start_rb = imcsi->freqBand->startingRB; - csiim_config_pdu->nr_of_rbs = imcsi->freqBand->nrofRBs; - // As specified in 5.2.2.4 of 38.214 - switch (imcsi->csi_IM_ResourceElementPattern->present) { - case NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern0: - for (int i = 0; i < 4; i++) { - csiim_config_pdu->k_csiim[i] = - (imcsi->csi_IM_ResourceElementPattern->choice.pattern0->subcarrierLocation_p0 << 1) + (i >> 1); - csiim_config_pdu->l_csiim[i] = imcsi->csi_IM_ResourceElementPattern->choice.pattern0->symbolLocation_p0 + (i % 2); - } - break; - case NR_CSI_IM_Resource__csi_IM_ResourceElementPattern_PR_pattern1: - for (int i = 0; i < 4; i++) { - csiim_config_pdu->k_csiim[i] = (imcsi->csi_IM_ResourceElementPattern->choice.pattern1->subcarrierLocation_p1 << 2) + i; - csiim_config_pdu->l_csiim[i] = imcsi->csi_IM_ResourceElementPattern->choice.pattern1->symbolLocation_p1; - } - break; - default: - AssertFatal(1 == 0, "Invalid CSI-IM pattern\n"); - } - dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_CSI_IM; - dl_config->number_pdus += 1; + nr_phr_info_t *phr_info = &mac->scheduling_info.phr_info; + // Value mapping according to 38.133 10.1.18.1 + const int PC_MAX_00 = -29; + *phr = (NR_SINGLE_ENTRY_PHR_MAC_CE){.PCMAX = max(0, (int)P_CMAX - PC_MAX_00) & 0x3f}; + // Value mapping according to 38.133 10.1.17.1.1 + const int POWER_HEADROOM_55 = 22; + int headroom = P_CMAX - tx_power; + if (headroom < POWER_HEADROOM_55) { + const int POWER_HEADROOM_0 = -32; + phr->PH = max(0, headroom - POWER_HEADROOM_0); + } else { + phr->PH = min(0x3f, 55 + (headroom - POWER_HEADROOM_55) / 2); } -} -NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *csi_measconfig, - NR_BWP_Id_t dl_bwp_id, - NR_NZP_CSI_RS_ResourceId_t csi_id) -{ - bool found = false; - for (int csi_list = 0; csi_list < csi_measconfig->csi_ResourceConfigToAddModList->list.count; csi_list++) { - NR_CSI_ResourceConfig_t *csires = csi_measconfig->csi_ResourceConfigToAddModList->list.array[csi_list]; - if(csires->bwp_Id != dl_bwp_id) - continue; - struct NR_CSI_ResourceConfig__csi_RS_ResourceSetList *resset = &csires->csi_RS_ResourceSetList; - if(resset->present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) - continue; - if(!resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList) - continue; - for(int i = 0; i < resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.count; i++) { - NR_NZP_CSI_RS_ResourceSetId_t *res_id = resset->choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.array[i]; - AssertFatal(res_id, "NR_NZP_CSI_RS_ResourceSetId shouldn't be NULL\n"); - struct NR_CSI_MeasConfig__nzp_CSI_RS_ResourceSetToAddModList *res_list = csi_measconfig->nzp_CSI_RS_ResourceSetToAddModList; - AssertFatal(res_list, "nzp_CSI_RS_ResourceSetToAddModList shouldn't be NULL\n"); - for (int j = 0; j < res_list->list.count; j++) { - NR_NZP_CSI_RS_ResourceSet_t *csi_res = res_list->list.array[j]; - if (*res_id != csi_res->nzp_CSI_ResourceSetId) - continue; - for (int k = 0; k < csi_res->nzp_CSI_RS_Resources.list.count; k++) { - AssertFatal(csi_res->nzp_CSI_RS_Resources.list.array[k], "NZP_CSI_RS_ResourceId shoulan't be NULL\n"); - if (csi_id == *csi_res->nzp_CSI_RS_Resources.list.array[k]) { - found = true; - break; - } - } - if (found && csi_res->trs_Info) - // CRI-RS for Tracking (not implemented yet) - // in this case we there is no associated CSI report - // therefore to signal this we return a value higher than - // maxNrofCSI-ResourceConfigurations - return NR_maxNrofCSI_ResourceConfigurations + 1; - else if (found) - return csires->csi_ResourceConfigId; - } - } - } - return -1; // not found any CSI-resource in current DL BWP associated with this CSI-RS ID -} + LOG_D(NR_MAC, + "PHR Reporting sfn.slot %d.%d reason = %d, P_CMAX = %d (%5.2f dBm), headrom = %d (%d dB) tx_power = %5.2f dBm\n", + frame, + slot, + phr_info->phr_reporting, + phr->PCMAX, + P_CMAX, + phr->PH, + headroom, + tx_power); -uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI_ResourceConfigId_t csi_res_id) -{ - uint8_t meas_bitmap = 0; - if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) - return meas_bitmap; // CSI-RS for tracking - for(int i = 0; i < csi_measconfig->csi_ReportConfigToAddModList->list.count; i++) { - NR_CSI_ReportConfig_t *report_config = csi_measconfig->csi_ReportConfigToAddModList->list.array[i]; - if(report_config->resourcesForChannelMeasurement != csi_res_id) - continue; - // bit 0 RSRP bit 1 RI bit 2 LI bit 3 PMI bit 4 CQI bit 5 i1 - switch (report_config->reportQuantity.present) { - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI: - meas_bitmap += (1 << 1) + (1 << 3) + (1 << 4); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1: - meas_bitmap += (1 << 1) + (1 << 5); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1_CQI: - meas_bitmap += (1 << 1) + (1 << 4) + (1 << 5); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI: - meas_bitmap += (1 << 1) + (1 << 4); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP: - meas_bitmap += 1; - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI: - meas_bitmap += (1 << 1) + (1 << 2) + (1 << 3) + (1 << 4); - break; - default: - AssertFatal(false, "Unexpected measurement report type %d\n", report_config->reportQuantity.present); - } - } - return meas_bitmap; -} - -void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu, - const NR_CSI_RS_ResourceMapping_t *resourceMapping, - uint32_t bwp_size, - uint32_t bwp_start) -{ - // According to last paragraph of TS 38.214 5.2.2.3.1 - if (resourceMapping->freqBand.startingRB < bwp_start) - csirs_config_pdu->start_rb = bwp_start; - else - csirs_config_pdu->start_rb = resourceMapping->freqBand.startingRB; - if (resourceMapping->freqBand.nrofRBs > (bwp_start + bwp_size - csirs_config_pdu->start_rb)) - csirs_config_pdu->nr_of_rbs = bwp_start + bwp_size - csirs_config_pdu->start_rb; - else - csirs_config_pdu->nr_of_rbs = resourceMapping->freqBand.nrofRBs; - AssertFatal(csirs_config_pdu->nr_of_rbs >= 24, "CSI-RS has %d RBs, but the minimum is 24\n", csirs_config_pdu->nr_of_rbs); - - csirs_config_pdu->symb_l0 = resourceMapping->firstOFDMSymbolInTimeDomain; - if (resourceMapping->firstOFDMSymbolInTimeDomain2) - csirs_config_pdu->symb_l1 = *resourceMapping->firstOFDMSymbolInTimeDomain2; - csirs_config_pdu->cdm_type = resourceMapping->cdm_Type; - - csirs_config_pdu->freq_density = resourceMapping->density.present; - if ((resourceMapping->density.present == NR_CSI_RS_ResourceMapping__density_PR_dot5) - && (resourceMapping->density.choice.dot5 == NR_CSI_RS_ResourceMapping__density__dot5_evenPRBs)) - csirs_config_pdu->freq_density--; - - switch(resourceMapping->frequencyDomainAllocation.present){ - case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row1: - csirs_config_pdu->row = 1; - csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.row1.buf[0]) >> 4) & 0x0f; - break; - case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row2: - csirs_config_pdu->row = 2; - csirs_config_pdu->freq_domain = (((resourceMapping->frequencyDomainAllocation.choice.row2.buf[1] >> 4) & 0x0f) - | ((resourceMapping->frequencyDomainAllocation.choice.row2.buf[0] << 4) & 0xff0)); - break; - case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row4: - csirs_config_pdu->row = 4; - csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.row4.buf[0]) >> 5) & 0x07; - break; - case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_other: - csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.other.buf[0]) >> 2) & 0x3f; - // determining the row of table 7.4.1.5.3-1 in 38.211 - switch (resourceMapping->nrofPorts) { - case NR_CSI_RS_ResourceMapping__nrofPorts_p1: - AssertFatal(1 == 0, "Resource with 1 CSI port shouldn't be within other rows\n"); - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p2: - csirs_config_pdu->row = 3; - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p4: - csirs_config_pdu->row = 5; - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p8: - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) - csirs_config_pdu->row = 8; - else { - int num_k = 0; - for (int k = 0; k < 6; k++) - num_k += (((csirs_config_pdu->freq_domain) >> k) & 0x01); - if (num_k == 4) - csirs_config_pdu->row = 6; - else - csirs_config_pdu->row = 7; - } - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p12: - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) - csirs_config_pdu->row = 10; - else - csirs_config_pdu->row = 9; - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p16: - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) - csirs_config_pdu->row = 12; - else - csirs_config_pdu->row = 11; - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p24: - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) - csirs_config_pdu->row = 14; - else { - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm8_FD2_TD4) - csirs_config_pdu->row = 15; - else - csirs_config_pdu->row = 13; - } - break; - case NR_CSI_RS_ResourceMapping__nrofPorts_p32: - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) - csirs_config_pdu->row = 17; - else { - if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm8_FD2_TD4) - csirs_config_pdu->row = 18; - else - csirs_config_pdu->row = 16; - } - break; - default: - AssertFatal(false, "Invalid number of ports in CSI-RS resource\n"); - } - break; - default: - AssertFatal(false, "Invalid freqency domain allocation in CSI-RS resource\n"); - } -} - -static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int slot) -{ - if (!mac->sc_info.csi_MeasConfig) - return; - - NR_CSI_MeasConfig_t *csi_measconfig = mac->sc_info.csi_MeasConfig; - - if (csi_measconfig->nzp_CSI_RS_ResourceToAddModList == NULL) - return; - - fapi_nr_dl_config_request_t *dl_config = get_dl_config_request(mac, slot); - NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; - NR_BWP_Id_t dl_bwp_id = current_DL_BWP->bwp_id; - - int mu = current_DL_BWP->scs; - uint16_t bwp_size = current_DL_BWP->BWPSize; - uint16_t bwp_start = current_DL_BWP->BWPStart; - - for (int id = 0; id < csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.count; id++) { - NR_NZP_CSI_RS_Resource_t *nzpcsi = csi_measconfig->nzp_CSI_RS_ResourceToAddModList->list.array[id]; - int period, offset; - csi_period_offset(NULL, nzpcsi->periodicityAndOffset, &period, &offset); - if((frame * mac->frame_structure.numb_slots_frame + slot-offset) % period != 0) - continue; - NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId); - // do not schedule reseption of this CSI-RS if not associated with current BWP - if(csi_res_id < 0) - continue; - LOG_D(MAC,"Scheduling reception of CSI-RS in frame %d slot %d\n", frame, slot); - fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu = &dl_config->dl_config_list[dl_config->number_pdus].csirs_config_pdu.csirs_config_rel15; - csirs_config_pdu->measurement_bitmap = set_csirs_measurement_bitmap(csi_measconfig, csi_res_id); - csirs_config_pdu->subcarrier_spacing = mu; - csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0; - - if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) - csirs_config_pdu->csi_type = 0; // TRS - else - csirs_config_pdu->csi_type = 1; // NZP-CSI-RS - - csirs_config_pdu->scramb_id = nzpcsi->scramblingID; - csirs_config_pdu->power_control_offset = nzpcsi->powerControlOffset + 8; - if (nzpcsi->powerControlOffsetSS) - csirs_config_pdu->power_control_offset_ss = *nzpcsi->powerControlOffsetSS; - else - csirs_config_pdu->power_control_offset_ss = 1; // 0 dB - - configure_csi_resource_mapping(csirs_config_pdu, &nzpcsi->resourceMapping, bwp_size, bwp_start); - - dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_CSI_RS; - dl_config->number_pdus += 1; - } -} - -static bool is_prach_frame(frame_t frame, prach_occasion_info_t *prach_occasion_info, int association_periods) -{ - int config_period = prach_occasion_info->frame_info[0]; - int frame_period = config_period * association_periods; - int frame_in_period = frame % frame_period; - if (association_periods > 1) { - int current_assoc_period = frame_in_period / config_period; - if (current_assoc_period != prach_occasion_info->association_period_idx) - return false; - frame_in_period %= config_period; - } - if (frame_in_period == prach_occasion_info->frame_info[1]) // SFN % x == y (see Tables in Table 6.3.3.2 of 38.211) - return true; - else - return false; -} - -// This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x -// PRACH formats 9, 10, 11 are corresponding to dual PRACH format configurations A1/B1, A2/B2, A3/B3. -// - todo: -// - Partial configuration is actually already stored in (fapi_nr_prach_config_t) &mac->phy_config.config_req->prach_config -static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t slotP) -{ - RA_config_t *ra = &mac->ra; - - // Get any valid PRACH occasion in the current slot for the selected SSB index - prach_occasion_info_t *prach_occasion_info = &ra->sched_ro_info; - - if (!is_prach_frame(frameP, prach_occasion_info, ra->association_periods)) - return; - - if (is_ul_slot(slotP, &mac->frame_structure)) { - if (slotP == prach_occasion_info->slot) { - fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frameP, slotP, FAPI_NR_UL_CONFIG_TYPE_PRACH); - if (!pdu) { - LOG_E(NR_MAC, "Error in PRACH allocation\n"); - return; - } - - int format = prach_occasion_info->format; - fapi_nr_prach_config_t *prach_config = &mac->phy_config.config_req.prach_config; - pdu->prach_config_pdu = (fapi_nr_ul_config_prach_pdu){ - .phys_cell_id = mac->physCellId, - .num_prach_ocas = 1, - .prach_slot = prach_occasion_info->slot, - .prach_start_symbol = prach_occasion_info->start_symbol, - .num_ra = prach_occasion_info->fdm, - .num_cs = get_NCS(ra->zeroCorrelationZoneConfig, format, ra->restricted_set_config), - .root_seq_id = prach_config->num_prach_fd_occasions_list[prach_occasion_info->fdm].prach_root_sequence_index, - .restricted_set = prach_config->restricted_set_config, - .freq_msg1 = prach_config->num_prach_fd_occasions_list[prach_occasion_info->fdm].k1}; - - LOG_I(NR_MAC, - "PRACH scheduler: Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", - frameP, - pdu->prach_config_pdu.prach_slot, - pdu->prach_config_pdu.prach_start_symbol, - pdu->prach_config_pdu.num_ra); - - switch (format) { // single PRACH format - case 0: - pdu->prach_config_pdu.prach_format = 0; - break; - case 1: - pdu->prach_config_pdu.prach_format = 1; - break; - case 2: - pdu->prach_config_pdu.prach_format = 2; - break; - case 3: - pdu->prach_config_pdu.prach_format = 3; - break; - case 0xa1: - pdu->prach_config_pdu.prach_format = 4; - break; - case 0xa2: - pdu->prach_config_pdu.prach_format = 5; - break; - case 0xa3: - pdu->prach_config_pdu.prach_format = 6; - break; - case 0xb1: - pdu->prach_config_pdu.prach_format = 7; - break; - case 0xb4: - pdu->prach_config_pdu.prach_format = 8; - break; - case 0xc0: - pdu->prach_config_pdu.prach_format = 9; - break; - case 0xc2: - pdu->prach_config_pdu.prach_format = 10; - break; - default: - AssertFatal(false, "Invalid PRACH format"); - } - - pdu->prach_config_pdu.ra_PreambleIndex = ra->ra_PreambleIndex; - pdu->prach_config_pdu.prach_tx_power = get_prach_tx_power(mac); - release_ul_config(pdu, false); - nr_scheduled_response_t scheduled_response = {.ul_config = mac->ul_config_request + slotP, - .mac = mac, - .module_id = mac->ue_id, - .CC_id = 0 /*TBR fix*/}; - if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL) - mac->if_module->scheduled_response(&scheduled_response); - - T(T_UE_PHY_INITIATE_RA_PROCEDURE, T_INT(frameP), T_INT(pdu->prach_config_pdu.prach_slot), - T_INT(pdu->prach_config_pdu.ra_PreambleIndex), T_INT(pdu->prach_config_pdu.prach_tx_power)); - - const int n_slots_frame = mac->frame_structure.numb_slots_frame; - if (ra->ra_type == RA_4_STEP) { - ra->ra_state = nrRA_WAIT_RAR; - // we start to monitor DCI for RAR in the first valid occasion after transmitting RACH - // the response window timer should be started at that time - // but for processing reasons it is better to start it here and to add the slot difference - // that also takes into account the rx to tx slot offset - int next_slot = (slotP + 1) % n_slots_frame; - int next_frame = (frameP + (next_slot < slotP)) % MAX_FRAME_NUMBER; - int add_slots = 1; - NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[mac->current_DL_BWP->bwp_id]; - const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id); - while (!is_dl_slot(next_slot, &mac->frame_structure) - || !is_ss_monitor_occasion(next_frame, next_slot, n_slots_frame, ra_SS)) { - int temp_slot = (next_slot + 1) % n_slots_frame; - next_frame = (next_frame + (temp_slot < next_slot)) % MAX_FRAME_NUMBER; - next_slot = temp_slot; - add_slots++; - } - add_slots += GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mac->current_DL_BWP->scs); - nr_timer_setup(&ra->response_window_timer, ra->response_window_setup_time + add_slots, 1); - nr_timer_start(&ra->response_window_timer); - } else if (ra->ra_type == RA_2_STEP) { - NR_MsgA_PUSCH_Resource_r16_t *msgA_PUSCH_Resource = - mac->current_UL_BWP->msgA_ConfigCommon_r16->msgA_PUSCH_Config_r16->msgA_PUSCH_ResourceGroupA_r16; - slot_t msgA_pusch_slot = (slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) % n_slots_frame; - frame_t msgA_pusch_frame = - (frameP + ((slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) / n_slots_frame)) % 1024; - - fapi_nr_ul_config_request_pdu_t *pdu = - lockGet_ul_config(mac, msgA_pusch_frame, msgA_pusch_slot, FAPI_NR_UL_CONFIG_TYPE_PUSCH); - if (!pdu) - return; - - // Config Msg3/MsgA-PUSCH PDU - NR_tda_info_t tda_info = {0}; - AssertFatal(msgA_PUSCH_Resource->startSymbolAndLengthMsgA_PO_r16, - "Only SLIV based on startSymbolAndLengthMsgA_PO_r16 implemented\n"); - - int ret = nr_config_pusch_pdu(mac, - &tda_info, - &pdu->pusch_config_pdu, - NULL, - NULL, - NULL, - mac->ra.ra_rnti, - NR_SearchSpace__searchSpaceType_PR_common, - NR_DCI_NONE); - - if (ret != 0) - remove_ul_config_last_item(pdu); - release_ul_config(pdu, false); - - LOG_D(NR_MAC, "ra->ra_state %s\n", nrra_ue_text[ra->ra_state]); - ra->ra_state = nrRA_WAIT_MSGB; - ra->t_crnti = 0; - mac->crnti = 0; - ra->ra_rnti = 0; - } else { - AssertFatal(false, "RA type %d not implemented!\n", ra->ra_type); - } - } // is_nr_prach_slot - } // if is_nr_UL_slot + phr_info->PathlossLastValue = compute_nr_SSB_PL(mac); + // Restart both timers according to 38.321 + nr_timer_start(&phr_info->periodicPHR_Timer); + nr_timer_start(&phr_info->prohibitPHR_Timer); + phr_info->phr_reporting = 0; } typedef struct { @@ -2265,6 +1930,44 @@ typedef struct { uint32_t lcp_allocation_counter; } NR_UE_MAC_CE_INFO; +static uint8_t nr_locate_BsrIndexByBufferSize(int size, int value) +{ + if (value == 0) { + return 0; //elseif (value > 150000) return 63; + } + + uint32_t (*get_bsr_value)(int); + if (size == NR_SHORT_BSR_TABLE_SIZE) + get_bsr_value = &get_short_bsr_value; + else + get_bsr_value = &get_long_bsr_value; + + int jl = 0; // lower bound + int ju = size - 1; // upper bound + // determine the order of the the table: 1 if ascending order of table, 0 otherwise + bool ascend = (*get_bsr_value)(ju) >= (*get_bsr_value)(jl) ? 1 : 0; + while (ju - jl > 1) { //If we are not yet done, + int jm = (ju + jl) / 2; // compute a midpoint, + if ((value >= (*get_bsr_value)(jm)) == ascend) { + jl = jm; // replace the lower limit + } else { + ju = jm; //replace the upper limit + } + + LOG_T(NR_MAC, + "[UE] searching BSR index %d for (BSR TABLE %d < value %d)\n", + jm, + (*get_bsr_value)(jm), + value); + } + + if (value == (*get_bsr_value)(jl)) { + return jl; + } else { + return jl + 1; //equally ju + } +} + static void nr_ue_get_sdu_mac_ce_pre(NR_UE_MAC_INST_t *mac, int CC_id, frame_t frame, @@ -2837,54 +2540,329 @@ static uint8_t nr_ue_get_sdu(NR_UE_MAC_INST_t *mac, return mac_ce_info.num_sdus > 0; // success if we got at least one sdu } -static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MAC_INST_t *mac) +void nr_ue_ul_scheduler(NR_UE_MAC_INST_t *mac, nr_uplink_indication_t *ul_info) { - NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; - fapi_nr_ta_command_pdu *ta = &dl_config->dl_config_list[dl_config->number_pdus].ta_command_pdu; - ta->ta_frame = ul_time_alignment->frame; - ta->ta_slot = ul_time_alignment->slot; - ta->is_rar = ul_time_alignment->ta_apply == rar_ta; - ta->ta_command = ul_time_alignment->ta_command; - dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_CONFIG_TA_COMMAND; - dl_config->number_pdus += 1; - ul_time_alignment->ta_apply = no_ta; -} + int cc_id = ul_info->cc_id; + frame_t frame_tx = ul_info->frame; + slot_t slot_tx = ul_info->slot; + uint32_t gNB_index = ul_info->gNB_index; -static void nr_ue_fill_phr(NR_UE_MAC_INST_t *mac, - NR_SINGLE_ENTRY_PHR_MAC_CE *phr, - float P_CMAX, - float tx_power, - frame_t frame, - slot_t slot) -{ - nr_phr_info_t *phr_info = &mac->scheduling_info.phr_info; - // Value mapping according to 38.133 10.1.18.1 - const int PC_MAX_00 = -29; - *phr = (NR_SINGLE_ENTRY_PHR_MAC_CE){.PCMAX = max(0, (int)P_CMAX - PC_MAX_00) & 0x3f}; - // Value mapping according to 38.133 10.1.17.1.1 - const int POWER_HEADROOM_55 = 22; - int headroom = P_CMAX - tx_power; - if (headroom < POWER_HEADROOM_55) { - const int POWER_HEADROOM_0 = -32; - phr->PH = max(0, headroom - POWER_HEADROOM_0); - } else { - phr->PH = min(0x3f, 55 + (headroom - POWER_HEADROOM_55) / 2); + RA_config_t *ra = &mac->ra; + + if (mac->state == UE_PERFORMING_RA && ra->ra_state == nrRA_UE_IDLE) { + init_RA(mac, frame_tx); + // perform the Random Access Resource selection procedure (see clause 5.1.2 and .2a) + ra_resource_selection(mac); } - LOG_D(NR_MAC, - "PHR Reporting sfn.slot %d.%d reason = %d, P_CMAX = %d (%5.2f dBm), headrom = %d (%d dB) tx_power = %5.2f dBm\n", - frame, - slot, - phr_info->phr_reporting, - phr->PCMAX, - P_CMAX, - phr->PH, - headroom, - tx_power); + if (mac->state == UE_PERFORMING_RA && ra->ra_state == nrRA_GENERATE_PREAMBLE) + nr_ue_prach_scheduler(mac, frame_tx, slot_tx); - phr_info->PathlossLastValue = compute_nr_SSB_PL(mac); - // Restart both timers according to 38.321 - nr_timer_start(&phr_info->periodicPHR_Timer); - nr_timer_start(&phr_info->prohibitPHR_Timer); - phr_info->phr_reporting = 0; + bool BSRsent = false; + if (mac->state == UE_CONNECTED) { + nr_ue_periodic_srs_scheduling(mac, frame_tx, slot_tx); + nr_update_rlc_buffers_status(mac, frame_tx, slot_tx, gNB_index); + } + + // Schedule ULSCH only if the current frame and slot match those in ul_config_req + // AND if a UL grant (UL DCI or Msg3) has been received (as indicated by num_pdus) + uint8_t ulsch_input_buffer_array[NFAPI_MAX_NUM_UL_PDU][MAX_ULSCH_PAYLOAD_BYTES]; + int number_of_pdus = 0; + + fapi_nr_ul_config_request_pdu_t *ulcfg_pdu = lockGet_ul_iterator(mac, frame_tx, slot_tx); + if (!ulcfg_pdu) + return; + LOG_D(NR_MAC, "number of UL PDUs: %d with UL transmission in sfn [%d.%d]\n", *ulcfg_pdu->privateNBpdus, frame_tx, slot_tx); + + while (ulcfg_pdu->pdu_type != FAPI_NR_END) { + uint8_t *ulsch_input_buffer = ulsch_input_buffer_array[number_of_pdus]; + if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) { + nfapi_nr_ue_pusch_pdu_t *pdu = &ulcfg_pdu->pusch_config_pdu; + uint32_t TBS_bytes = pdu->pusch_data.tb_size; + LOG_D(NR_MAC, + "harq_id %d, new_data_indicator %d, TBS_bytes %d (ra_state %d)\n", + pdu->pusch_data.harq_process_id, + pdu->pusch_data.new_data_indicator, + TBS_bytes, + ra->ra_state); + pdu->tx_request_body.fapiTxPdu = NULL; + if ((ra->ra_state == nrRA_WAIT_RAR || ra->ra_state == nrRA_WAIT_MSGB) && !ra->cfra) { + nr_get_Msg3_MsgA_PUSCH_payload(mac, ulsch_input_buffer, TBS_bytes); + for (int k = 0; k < TBS_bytes; k++) { + LOG_D(NR_MAC, "(%i): 0x%x\n", k, ulsch_input_buffer[k]); + } + pdu->tx_request_body.fapiTxPdu = ulsch_input_buffer; + pdu->tx_request_body.pdu_length = TBS_bytes; + number_of_pdus++; + T(T_NRUE_MAC_UL_PDU_WITH_DATA, T_INT(mac->crnti), T_INT(frame_tx), T_INT(slot_tx), + T_INT(ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id), T_BUFFER(ulsch_input_buffer, TBS_bytes)); + } else { + if (ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator + && (mac->state == UE_CONNECTED || (ra->ra_state == nrRA_WAIT_RAR && ra->cfra))) { + if (!nr_timer_is_active(&mac->time_alignment_timer) && mac->state == UE_CONNECTED && !get_softmodem_params()->phy_test) { + // UL data arrival during RRC_CONNECTED when UL synchronisation status is "non-synchronised" + trigger_MAC_UE_RA(mac, NULL); + return; + } + // Getting IP traffic to be transmitted + int tx_power = pdu->tx_power; + int P_CMAX = nr_get_Pcmax(mac->p_Max, + mac->nr_band, + mac->frame_structure.frame_type, + mac->frequency_range, + mac->current_UL_BWP->channel_bandwidth, + pdu->qam_mod_order, + false, + mac->current_UL_BWP->scs, + mac->current_UL_BWP->BWPSize, + pdu->transform_precoding, + pdu->rb_size, + pdu->rb_start); + + nr_ue_get_sdu(mac, cc_id, frame_tx, slot_tx, gNB_index, ulsch_input_buffer, TBS_bytes, tx_power, P_CMAX, &BSRsent); + pdu->tx_request_body.fapiTxPdu = ulsch_input_buffer; + pdu->tx_request_body.pdu_length = TBS_bytes; + number_of_pdus++; + T(T_NRUE_MAC_UL_PDU_WITH_DATA, T_INT(mac->crnti), T_INT(frame_tx), T_INT(slot_tx), + T_INT(ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id), T_BUFFER(ulsch_input_buffer, TBS_bytes)); + // start or restart dataInactivityTimer if any MAC entity transmits a MAC SDU for DTCH logical channel, + // or DCCH logical channel + if (mac->data_inactivity_timer) + nr_timer_start(mac->data_inactivity_timer); + } + } + + if (ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION && !ra->cfra) { + LOG_I(NR_MAC, "[RAPROC][%d.%d] RA-Msg3 retransmitted\n", frame_tx, slot_tx); + // 38.321 restart the ra-ContentionResolutionTimer at each HARQ retransmission in the first symbol after the end of the Msg3 + // transmission + nr_Msg3_transmitted(mac, cc_id, frame_tx, slot_tx, gNB_index); + } + if (ra->ra_state == nrRA_WAIT_RAR && !ra->cfra) { + LOG_A(NR_MAC, "[RAPROC][%d.%d] RA-Msg3 transmitted\n", frame_tx, slot_tx); + nr_Msg3_transmitted(mac, cc_id, frame_tx, slot_tx, gNB_index); + } + if (ra->ra_state == nrRA_WAIT_MSGB && !ra->cfra) { + LOG_A(NR_MAC, "[RAPROC][%d.%d] RA-MsgA-PUSCH transmitted\n", frame_tx, slot_tx); + } + } + ulcfg_pdu++; + } + release_ul_config(ulcfg_pdu, false); + + if(mac->state >= UE_PERFORMING_RA && mac->state < UE_DETACHING) + nr_ue_pucch_scheduler(mac, frame_tx, slot_tx); + + if (mac->if_module != NULL && mac->if_module->scheduled_response != NULL) { + LOG_D(NR_MAC, "3# scheduled_response transmitted,%d, %d\n", frame_tx, slot_tx); + nr_scheduled_response_t scheduled_response = {.ul_config = mac->ul_config_request + slot_tx, + .mac = mac, + .module_id = mac->ue_id, + .CC_id = cc_id, + .phy_data = ul_info->phy_data}; + mac->if_module->scheduled_response(&scheduled_response); + } + + if(mac->state == UE_CONNECTED) + nr_update_sr(mac, BSRsent); + // Global variables implicit logic + // far away, BSR_reporting_active is set + mac->scheduling_info.BSR_reporting_active = NR_BSR_TRIGGER_NONE; + + // update Bj for all active lcids before LCP procedure + if (mac->current_UL_BWP) { + LOG_D(NR_MAC, "%4d.%2d Logical Channel Prioritization\n", frame_tx, slot_tx); + for (int i = 0; i < mac->lc_ordered_list.count; i++) { + nr_lcordered_info_t *lc_info = mac->lc_ordered_list.array[i]; + int lcid = lc_info->lcid; + // max amount of data that can be buffered/accumulated in a logical channel buffer + uint32_t bucketSize_max = lc_info->bucket_size; + // measure Bj increment the value of Bj by product PBR * T + NR_LC_SCHEDULING_INFO *sched_info = get_scheduling_info_from_lcid(mac, lcid); + int32_t bj = sched_info->Bj; + if (lc_info->pbr < UINT_MAX) { + uint32_t slots_elapsed = nr_timer_elapsed_time(&sched_info->Bj_timer); // slots elapsed since Bj was last incremented + // it is safe to divide by 1k since pbr in lc_info is computed multiplying by 1000 the RRC value to convert kB/s to B/s + uint32_t pbr_ms = lc_info->pbr / 1000; + bj += ((pbr_ms * slots_elapsed) >> mac->current_UL_BWP->scs); // each slot length is 1/scs ms + } + else + bj = INT_MAX; + // bj > max bucket size, set bj to max bucket size, as in ts38.321 5.4.3.1 Logical Channel Prioritization + sched_info->Bj = min(bj, bucketSize_max); + // reset bj timer + nr_timer_start(&sched_info->Bj_timer); + } + } +} + +// PUSCH scheduler: +// - Calculate the slot in which ULSCH should be scheduled. This is current slot + K2, +// - where K2 is the offset between the slot in which UL DCI is received and the slot +// - in which ULSCH should be scheduled. K2 is configured in RRC configuration. +// PUSCH Msg3 scheduler: +// - scheduled by RAR UL grant according to 8.3 of TS 38.213 +// Note: Msg3 tx in the uplink symbols of mixed slot +int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac, + const uint8_t is_Msg3, + const frame_t current_frame, + const int current_slot, + frame_t *frame_tx, + int *slot_tx, + const long k2) +{ + int delta = 0; + const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; + + // Get the numerology to calculate the Tx frame and slot + int mu = current_UL_BWP->scs; + int slots_per_frame = mac->frame_structure.numb_slots_frame; + + // k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1 + // PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon + + if (is_Msg3) { + + delta = get_delta_for_k2(mu); + + AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + "Slot offset (%ld) for Msg3 needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to " + "%ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", + k2, + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); + + + *slot_tx = (current_slot + k2 + delta) % slots_per_frame; + *frame_tx = (current_frame + (current_slot + k2 + delta) / slots_per_frame) % MAX_FRAME_NUMBER; + } else { + AssertFatal(k2 >= GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + "Slot offset K2 (%ld) needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in " + "gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n", + k2, + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu), + GET_DURATION_RX_TO_TX(&mac->phy_config.config_req.ntn_config, mu)); + + + if (k2 < 0) { // This can happen when a false DCI is received + LOG_W(PHY, "%d.%d. Received k2 %ld\n", current_frame, current_slot, k2); + return -1; + } + + // Calculate TX slot and frame + *slot_tx = (current_slot + k2) % slots_per_frame; + *frame_tx = (current_frame + (current_slot + k2) / slots_per_frame) % MAX_FRAME_NUMBER; + } + + LOG_D(NR_MAC, "[%04d.%02d] UL transmission in [%04d.%02d] (k2 %ld delta %d)\n", current_frame, current_slot, *frame_tx, *slot_tx, k2, delta); + return 0; +} + +void configure_csi_resource_mapping(fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu, + const NR_CSI_RS_ResourceMapping_t *resourceMapping, + uint32_t bwp_size, + uint32_t bwp_start) +{ + // According to last paragraph of TS 38.214 5.2.2.3.1 + if (resourceMapping->freqBand.startingRB < bwp_start) + csirs_config_pdu->start_rb = bwp_start; + else + csirs_config_pdu->start_rb = resourceMapping->freqBand.startingRB; + if (resourceMapping->freqBand.nrofRBs > (bwp_start + bwp_size - csirs_config_pdu->start_rb)) + csirs_config_pdu->nr_of_rbs = bwp_start + bwp_size - csirs_config_pdu->start_rb; + else + csirs_config_pdu->nr_of_rbs = resourceMapping->freqBand.nrofRBs; + AssertFatal(csirs_config_pdu->nr_of_rbs >= 24, "CSI-RS has %d RBs, but the minimum is 24\n", csirs_config_pdu->nr_of_rbs); + + csirs_config_pdu->symb_l0 = resourceMapping->firstOFDMSymbolInTimeDomain; + if (resourceMapping->firstOFDMSymbolInTimeDomain2) + csirs_config_pdu->symb_l1 = *resourceMapping->firstOFDMSymbolInTimeDomain2; + csirs_config_pdu->cdm_type = resourceMapping->cdm_Type; + + csirs_config_pdu->freq_density = resourceMapping->density.present; + if ((resourceMapping->density.present == NR_CSI_RS_ResourceMapping__density_PR_dot5) + && (resourceMapping->density.choice.dot5 == NR_CSI_RS_ResourceMapping__density__dot5_evenPRBs)) + csirs_config_pdu->freq_density--; + + switch(resourceMapping->frequencyDomainAllocation.present){ + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row1: + csirs_config_pdu->row = 1; + csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.row1.buf[0]) >> 4) & 0x0f; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row2: + csirs_config_pdu->row = 2; + csirs_config_pdu->freq_domain = (((resourceMapping->frequencyDomainAllocation.choice.row2.buf[1] >> 4) & 0x0f) + | ((resourceMapping->frequencyDomainAllocation.choice.row2.buf[0] << 4) & 0xff0)); + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row4: + csirs_config_pdu->row = 4; + csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.row4.buf[0]) >> 5) & 0x07; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_other: + csirs_config_pdu->freq_domain = ((resourceMapping->frequencyDomainAllocation.choice.other.buf[0]) >> 2) & 0x3f; + // determining the row of table 7.4.1.5.3-1 in 38.211 + switch (resourceMapping->nrofPorts) { + case NR_CSI_RS_ResourceMapping__nrofPorts_p1: + AssertFatal(1 == 0, "Resource with 1 CSI port shouldn't be within other rows\n"); + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p2: + csirs_config_pdu->row = 3; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p4: + csirs_config_pdu->row = 5; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p8: + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + csirs_config_pdu->row = 8; + else { + int num_k = 0; + for (int k = 0; k < 6; k++) + num_k += (((csirs_config_pdu->freq_domain) >> k) & 0x01); + if (num_k == 4) + csirs_config_pdu->row = 6; + else + csirs_config_pdu->row = 7; + } + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p12: + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + csirs_config_pdu->row = 10; + else + csirs_config_pdu->row = 9; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p16: + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + csirs_config_pdu->row = 12; + else + csirs_config_pdu->row = 11; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p24: + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + csirs_config_pdu->row = 14; + else { + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm8_FD2_TD4) + csirs_config_pdu->row = 15; + else + csirs_config_pdu->row = 13; + } + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p32: + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + csirs_config_pdu->row = 17; + else { + if (resourceMapping->cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm8_FD2_TD4) + csirs_config_pdu->row = 18; + else + csirs_config_pdu->row = 16; + } + break; + default: + AssertFatal(false, "Invalid number of ports in CSI-RS resource\n"); + } + break; + default: + AssertFatal(false, "Invalid freqency domain allocation in CSI-RS resource\n"); + } } From 98530b5b7084bfef2f72bbe9189db312f2b72dec Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 19 Dec 2025 15:44:33 +0100 Subject: [PATCH 05/23] cleanup of functions in nr_ue_procedures (no code change, just moving things around) --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 957 +++++++++---------- 1 file changed, 450 insertions(+), 507 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 241a56b180..3ba221386f 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -104,58 +104,7 @@ static const int sequence_cyclic_shift_2_harq_ack_bits[4] /* HARQ-ACK Value (0,0) (0,1) (1,0) (1,1) */ /* Sequence cyclic shift */ = { 0, 3, 9, 6 }; -/* \brief Function called by PHY to process the received RAR and check that the preamble matches what was sent by the gNB. It -provides the timing advance and t-CRNTI. -@param Mod_id Index of UE instance -@param CC_id Index to a component carrier -@param frame Frame index -@param ra_rnti RA_RNTI value -@param dlsch_buffer Pointer to dlsch_buffer containing RAR PDU -@param t_crnti Pointer to PHY variable containing the T_CRNTI -@param preamble_index Preamble Index used by PHY to transmit the PRACH. This should match the received RAR to trigger the rest of -random-access procedure -@param selected_rar_buffer the output buffer for storing the selected RAR header and RAR payload -@returns timing advance or 0xffff if preamble doesn't match -*/ -static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id); - -/*! \fn int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); - \brief This function schedules a positive or negative SR for schedulingRequestID sr_id - depending on the presence of any active SR and the prohibit timer. - If the max number of retransmissions is reached, it triggers a new RA */ -static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); - -static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig); - -static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - NR_CSI_MeasConfig_t *csi_MeasConfig, - CSI_mapping_t mapping_type); -static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, - struct NR_CSI_ReportConfig *csi_reportconfig, - NR_CSI_ResourceConfigId_t csi_ResourceConfigId, - const NR_CSI_MeasConfig_t *csi_MeasConfig); - -static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp); - -static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id); - -static void set_harq_status(NR_UE_MAC_INST_t *mac, - uint8_t pucch_id, - uint8_t harq_id, - int8_t delta_pucch, - uint16_t data_toul_fb, - uint8_t dai, - int n_CCE, - int N_CCE, - frame_t frame, - int slot); - -int get_pucch0_mcs(const int O_ACK, const int O_SR, const int ack_payload, const int sr_payload) +static int get_pucch0_mcs(const int O_ACK, const int O_SR, const int ack_payload, const int sr_payload) { int mcs = 0; if (O_SR == 0 || sr_payload == 0) { /* only ack is transmitted TS 36.213 9.2.3 UE procedure for reporting HARQ-ACK */ @@ -197,14 +146,6 @@ const initial_pucch_resource_t initial_pucch_resource[16] = { /* 15 */ { 1, 0, 14, 0, 4, { 0, 3, 6, 9 } }, }; -static nr_dci_format_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, - const nfapi_nr_dci_formats_e dci_format, - const uint8_t dci_size, - const uint16_t rnti, - const int ss_type, - const uint8_t *dci_pdu, - const int slot); - int get_rnti_type(const NR_UE_MAC_INST_t *mac, const uint16_t rnti) { const RA_config_t *ra = &mac->ra; @@ -523,6 +464,71 @@ int8_t nr_ue_process_dci_freq_dom_resource_assignment(nfapi_nr_ue_pusch_pdu_t *p return 0; } +static void set_harq_status(NR_UE_MAC_INST_t *mac, + uint8_t pucch_id, + uint8_t harq_id, + int8_t delta_pucch, + uint16_t data_toul_fb, + uint8_t dai, + int n_CCE, + int N_CCE, + frame_t frame, + int slot) +{ + NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[harq_id]; + current_harq->active = true; + current_harq->ack_received = false; + current_harq->pucch_resource_indicator = pucch_id; + current_harq->n_CCE = n_CCE; + current_harq->N_CCE = N_CCE; + current_harq->dai_cumul = 0; + current_harq->delta_pucch = delta_pucch; + // FIXME k0 != 0 currently not taken into consideration + int slots_per_frame = mac->frame_structure.numb_slots_frame; + current_harq->ul_frame = frame; + current_harq->ul_slot = slot + data_toul_fb; + if (current_harq->ul_slot >= slots_per_frame) { + current_harq->ul_frame = (frame + current_harq->ul_slot / slots_per_frame) % MAX_FRAME_NUMBER; + current_harq->ul_slot %= slots_per_frame; + } + // counter DAI in DCI ranges from 0 to 3 + // we might have more than 4 HARQ processes to report per PUCCH + // we need to keep track of how many DAI we received in a slot (dai_cumul) despite the modulo operation + int highest_dai = -1; + int temp_dai = dai; + const int num_dl_harq = get_nrofHARQ_ProcessesForPDSCH(&mac->sc_info); + for (int i = 0; i < num_dl_harq; i++) { + // looking for other active HARQ processes with feedback in the same frame/slot + if (i == harq_id) + continue; + NR_UE_DL_HARQ_STATUS_t *harq = &mac->dl_harq_info[i]; + if (harq->active && + harq->ul_frame == current_harq->ul_frame && + harq->ul_slot == current_harq->ul_slot) { + // highest_dai is the largest cumulative dai in the set of HARQ allocations for a given slot + if (harq->dai_cumul > highest_dai) + highest_dai = harq->dai_cumul - 1; + } + } + + current_harq->dai_cumul = temp_dai + 1; // DAI = 0 (temp_dai) corresponds to 1st assignment and so on + // if temp_dai is less or equal than cumulative highest dai for given slot + // it's an indication dai was reset due to modulo 4 operation + if (temp_dai <= highest_dai) { + int mod4_count = (highest_dai + 1) / 4; // to take into account how many times dai wrapped up (modulo 4) + current_harq->dai_cumul += (mod4_count * 4); + } + LOG_D(NR_MAC, + "Setting harq_status for harq_id %d, dl %d.%d, sched ul %d.%d fb time %d total dai %d\n", + harq_id, + frame, + slot, + current_harq->ul_frame, + current_harq->ul_slot, + data_toul_fb, + current_harq->dai_cumul); +} + static int nr_ue_process_dci_ul_00(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, @@ -1486,27 +1492,6 @@ static int8_t nr_ue_process_dci(NR_UE_MAC_INST_t *mac, return -1; } -nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci) -{ - LOG_D(NR_MAC, - "Received dci indication (rnti %x, dci format %d, n_CCE %d, payloadSize %d, payload %llx)\n", - dci->rnti, - dci->dci_format, - dci->n_CCE, - dci->payloadSize, - *(unsigned long long *)dci->payloadBits); - const nr_dci_format_t format = - nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, dci->ss_type, dci->payloadBits, slot); - if (format == NR_DCI_NONE) - return NR_DCI_NONE; - int ret = nr_ue_process_dci(mac, frame, slot, mac->def_dci_pdu_rel15[slot] + format, dci, format); - if (ret < 0) { - mac->stats.bad_dci++; - return NR_DCI_NONE; - } - return format; -} - void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements) { LOG_D(NR_MAC, "(%d.%d) Received measurements from L1\n", frame, slot); @@ -1530,71 +1515,6 @@ void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slo l1_measurements->rsrp_dBm); } -static void set_harq_status(NR_UE_MAC_INST_t *mac, - uint8_t pucch_id, - uint8_t harq_id, - int8_t delta_pucch, - uint16_t data_toul_fb, - uint8_t dai, - int n_CCE, - int N_CCE, - frame_t frame, - int slot) -{ - NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[harq_id]; - current_harq->active = true; - current_harq->ack_received = false; - current_harq->pucch_resource_indicator = pucch_id; - current_harq->n_CCE = n_CCE; - current_harq->N_CCE = N_CCE; - current_harq->dai_cumul = 0; - current_harq->delta_pucch = delta_pucch; - // FIXME k0 != 0 currently not taken into consideration - int slots_per_frame = mac->frame_structure.numb_slots_frame; - current_harq->ul_frame = frame; - current_harq->ul_slot = slot + data_toul_fb; - if (current_harq->ul_slot >= slots_per_frame) { - current_harq->ul_frame = (frame + current_harq->ul_slot / slots_per_frame) % MAX_FRAME_NUMBER; - current_harq->ul_slot %= slots_per_frame; - } - // counter DAI in DCI ranges from 0 to 3 - // we might have more than 4 HARQ processes to report per PUCCH - // we need to keep track of how many DAI we received in a slot (dai_cumul) despite the modulo operation - int highest_dai = -1; - int temp_dai = dai; - const int num_dl_harq = get_nrofHARQ_ProcessesForPDSCH(&mac->sc_info); - for (int i = 0; i < num_dl_harq; i++) { - // looking for other active HARQ processes with feedback in the same frame/slot - if (i == harq_id) - continue; - NR_UE_DL_HARQ_STATUS_t *harq = &mac->dl_harq_info[i]; - if (harq->active && - harq->ul_frame == current_harq->ul_frame && - harq->ul_slot == current_harq->ul_slot) { - // highest_dai is the largest cumulative dai in the set of HARQ allocations for a given slot - if (harq->dai_cumul > highest_dai) - highest_dai = harq->dai_cumul - 1; - } - } - - current_harq->dai_cumul = temp_dai + 1; // DAI = 0 (temp_dai) corresponds to 1st assignment and so on - // if temp_dai is less or equal than cumulative highest dai for given slot - // it's an indication dai was reset due to modulo 4 operation - if (temp_dai <= highest_dai) { - int mod4_count = (highest_dai + 1) / 4; // to take into account how many times dai wrapped up (modulo 4) - current_harq->dai_cumul += (mod4_count * 4); - } - LOG_D(NR_MAC, - "Setting harq_status for harq_id %d, dl %d.%d, sched ul %d.%d fb time %d total dai %d\n", - harq_id, - frame, - slot, - current_harq->ul_frame, - current_harq->ul_slot, - data_toul_fb, - current_harq->dai_cumul); -} - initial_pucch_resource_t get_initial_pucch_resource(const int idx) { return initial_pucch_resource[idx]; @@ -2566,51 +2486,11 @@ bool get_downlink_ack(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, PUCCH_sche return (number_harq_feedback > 0); } -bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, frame_t frame, int slot) -{ - NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; - NR_PUCCH_Config_t *pucch_Config = current_UL_BWP ? current_UL_BWP->pucch_Config : NULL; - - if(!pucch_Config || - !pucch_Config->schedulingRequestResourceToAddModList || - pucch_Config->schedulingRequestResourceToAddModList->list.count == 0) - return false; // SR not configured - - int sr_count = 0; - for (int id = 0; id < pucch_Config->schedulingRequestResourceToAddModList->list.count; id++) { - NR_SchedulingRequestResourceConfig_t *sr_Config = pucch_Config->schedulingRequestResourceToAddModList->list.array[id]; - int SR_period; int SR_offset; - - find_period_offset_SR(sr_Config, &SR_period, &SR_offset); - const int n_slots_frame = mac->frame_structure.numb_slots_frame; - int sfn_sf = frame * n_slots_frame + slot; - - if ((sfn_sf - SR_offset) % SR_period == 0) { - LOG_D(MAC, "Scheduling Request active in frame %d slot %d \n", frame, slot); - if (!sr_Config->resource) { - LOG_E(MAC, "No resource associated with SR. SR not scheduled\n"); - break; - } - NR_PUCCH_Resource_t *sr_pucch = - find_pucch_resource_from_list(pucch_Config->resourceToAddModList, *sr_Config->resource); - AssertFatal(sr_pucch != NULL, "Couldn't find PUCCH Resource ID for SR in PUCCH resource list\n"); - pucch->pucch_resource = sr_pucch; - pucch->n_sr = 1; - /* sr_payload = 1 means that this is a positive SR, sr_payload = 0 means that it is a negative SR */ - int ret = nr_ue_get_SR(mac, frame, slot, sr_Config->schedulingRequestID); - if (ret < 0) { - memset(pucch, 0, sizeof(*pucch)); - return false; - } - pucch->sr_payload = ret; - sr_count++; - AssertFatal(sr_count < 2, "Cannot handle more than 1 SR per slot yet\n"); - } - } - return sr_count > 0; -} - -static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id) +/*! \fn int nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id); + \brief This function schedules a positive or negative SR for schedulingRequestID sr_id + depending on the presence of any active SR and the prohibit timer. + If the max number of retransmissions is reached, it triggers a new RA */ +static int nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_SchedulingRequestId_t sr_id) { // no UL-SCH resources available for this tti && UE has a valid PUCCH resources for SR configuration for this tti NR_UE_SCHEDULING_INFO *si = &mac->scheduling_info; @@ -2660,6 +2540,50 @@ static int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR return -1; } +bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, frame_t frame, int slot) +{ + NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; + NR_PUCCH_Config_t *pucch_Config = current_UL_BWP ? current_UL_BWP->pucch_Config : NULL; + + if(!pucch_Config || + !pucch_Config->schedulingRequestResourceToAddModList || + pucch_Config->schedulingRequestResourceToAddModList->list.count == 0) + return false; // SR not configured + + int sr_count = 0; + for (int id = 0; id < pucch_Config->schedulingRequestResourceToAddModList->list.count; id++) { + NR_SchedulingRequestResourceConfig_t *sr_Config = pucch_Config->schedulingRequestResourceToAddModList->list.array[id]; + int SR_period; int SR_offset; + + find_period_offset_SR(sr_Config, &SR_period, &SR_offset); + const int n_slots_frame = mac->frame_structure.numb_slots_frame; + int sfn_sf = frame * n_slots_frame + slot; + + if ((sfn_sf - SR_offset) % SR_period == 0) { + LOG_D(MAC, "Scheduling Request active in frame %d slot %d \n", frame, slot); + if (!sr_Config->resource) { + LOG_E(MAC, "No resource associated with SR. SR not scheduled\n"); + break; + } + NR_PUCCH_Resource_t *sr_pucch = + find_pucch_resource_from_list(pucch_Config->resourceToAddModList, *sr_Config->resource); + AssertFatal(sr_pucch != NULL, "Couldn't find PUCCH Resource ID for SR in PUCCH resource list\n"); + pucch->pucch_resource = sr_pucch; + pucch->n_sr = 1; + /* sr_payload = 1 means that this is a positive SR, sr_payload = 0 means that it is a negative SR */ + int ret = nr_ue_get_SR(mac, frame, slot, sr_Config->schedulingRequestID); + if (ret < 0) { + memset(pucch, 0, sizeof(*pucch)); + return false; + } + pucch->sr_payload = ret; + sr_count++; + AssertFatal(sr_count < 2, "Cannot handle more than 1 SR per slot yet\n"); + } + } + return sr_count > 0; +} + // section 5.2.5 of 38.214 int compute_csi_priority(NR_UE_MAC_INST_t *mac, NR_CSI_ReportConfig_t *csirep) { @@ -2855,52 +2779,6 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac, return csi; } -csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac, - int csi_report_id, - CSI_mapping_t mapping_type, - NR_CSI_MeasConfig_t *csi_MeasConfig) -{ - AssertFatal(csi_MeasConfig->csi_ReportConfigToAddModList->list.count > 0,"No CSI Report configuration available\n"); - csi_payload_t csi = {0}; - struct NR_CSI_ReportConfig *csi_reportconfig = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]; - NR_CSI_ResourceConfigId_t csi_ResourceConfigId = csi_reportconfig->resourcesForChannelMeasurement; - if (csi_reportconfig->ext2 && csi_reportconfig->ext2->reportQuantity_r16) { - switch (csi_reportconfig->ext2->reportQuantity_r16->present) { - case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16: - csi = get_ssb_sinr_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); - break; - case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_cri_SINR_r16: - LOG_E(NR_MAC, "CSI Reporting of CSI-RS based SINR not yet available\n"); - break; - default: - AssertFatal(1 == 0, "Invalid CSI report quantity r16 type %d\n", csi_reportconfig->ext2->reportQuantity_r16->present); - } - } else { - switch (csi_reportconfig->reportQuantity.present) { - case NR_CSI_ReportConfig__reportQuantity_PR_none: - break; - case NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP: - csi = get_ssb_rsrp_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI: - csi = get_csirs_RI_PMI_CQI_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig, mapping_type); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP: - csi = get_csirs_RSRP_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); - break; - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1: - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1_CQI: - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI: - case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI: - LOG_E(NR_MAC, "Measurement report %d based on CSI-RS is not available\n", csi_reportconfig->reportQuantity.present); - break; - default: - AssertFatal(1 == 0, "Invalid CSI report quantity type %d\n", csi_reportconfig->reportQuantity.present); - } - } - return csi; -} - // Comparison function for sorting SSB RSRP measurements in descending order static int compare_ssb_rsrp(const void *a, const void *b) { @@ -2909,6 +2787,16 @@ static int compare_ssb_rsrp(const void *a, const void *b) return mb->ssb_rsrp_dBm - ma->ssb_rsrp_dBm; } +// returns index from differential RSRP +// according to Table 10.1.6.1-2 in 38.133 +static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp) +{ + int diff = best_rsrp-current_rsrp; + if (diff>30) + return 15; + else + return (diff>>1); +} static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac, struct NR_CSI_ReportConfig *csi_reportconfig, @@ -3121,49 +3009,300 @@ static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac, return csi; } -// returns index from differential RSRP -// according to Table 10.1.6.1-2 in 38.133 -static uint8_t get_rsrp_diff_index(int best_rsrp, int current_rsrp) +csi_payload_t nr_get_csi_payload(NR_UE_MAC_INST_t *mac, + int csi_report_id, + CSI_mapping_t mapping_type, + NR_CSI_MeasConfig_t *csi_MeasConfig) { - int diff = best_rsrp-current_rsrp; - if (diff>30) - return 15; - else - return (diff>>1); + AssertFatal(csi_MeasConfig->csi_ReportConfigToAddModList->list.count > 0,"No CSI Report configuration available\n"); + csi_payload_t csi = {0}; + struct NR_CSI_ReportConfig *csi_reportconfig = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]; + NR_CSI_ResourceConfigId_t csi_ResourceConfigId = csi_reportconfig->resourcesForChannelMeasurement; + if (csi_reportconfig->ext2 && csi_reportconfig->ext2->reportQuantity_r16) { + switch (csi_reportconfig->ext2->reportQuantity_r16->present) { + case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16: + csi = get_ssb_sinr_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); + break; + case NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_cri_SINR_r16: + LOG_E(NR_MAC, "CSI Reporting of CSI-RS based SINR not yet available\n"); + break; + default: + AssertFatal(1 == 0, "Invalid CSI report quantity r16 type %d\n", csi_reportconfig->ext2->reportQuantity_r16->present); + } + } else { + switch (csi_reportconfig->reportQuantity.present) { + case NR_CSI_ReportConfig__reportQuantity_PR_none: + break; + case NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP: + csi = get_ssb_rsrp_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI: + csi = get_csirs_RI_PMI_CQI_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig, mapping_type); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP: + csi = get_csirs_RSRP_payload(mac, csi_reportconfig, csi_ResourceConfigId, csi_MeasConfig); + break; + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1: + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_i1_CQI: + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI: + case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI: + LOG_E(NR_MAC, "Measurement report %d based on CSI-RS is not available\n", csi_reportconfig->reportQuantity.present); + break; + default: + AssertFatal(1 == 0, "Invalid CSI report quantity type %d\n", csi_reportconfig->reportQuantity.present); + } + } + return csi; } -void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) +static void set_time_alignment(NR_UE_MAC_INST_t *mac, int ta, ta_type_t type, int frame, int slot) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN); + NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; + ul_time_alignment->ta_command = ta; + ul_time_alignment->ta_apply = type; + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); + const int n_slots_frame = mac->frame_structure.numb_slots_frame; + ul_time_alignment->frame = (frame + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER; + ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame; + // start or restart the timeAlignmentTimer associated with the indicated TAG + nr_timer_start(&mac->time_alignment_timer); +} - LOG_D(NR_MAC, - "In [%d.%d] Handling DLSCH PDU type %d\n", - dl_info->frame, - dl_info->slot, - dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type); +static void handle_rar_reception(NR_UE_MAC_INST_t *mac, NR_MAC_RAR *rar, frame_t frame, int slot) +{ + RAR_grant_t rar_grant; + RA_config_t *ra = &mac->ra; +#ifdef DEBUG_RAR + // CSI + unsigned char csi_req = (unsigned char)(rar->UL_GRANT_4 & 0x01); +#endif - // Processing MAC PDU - // it parses MAC CEs subheaders, MAC CEs, SDU subheaderds and SDUs - switch (dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type) { - case FAPI_NR_RX_PDU_TYPE_DLSCH : - // start or restart dataInactivityTimer if any MAC entity receives a MAC SDU for DTCH logical channel, - // DCCH logical channel, or CCCH logical channel - if (mac->data_inactivity_timer) - nr_timer_start(mac->data_inactivity_timer); - // DL data arrival during RRC_CONNECTED when UL synchronisation status is "non-synchronised" - if (!nr_timer_is_active(&mac->time_alignment_timer) && mac->state == UE_CONNECTED && !get_softmodem_params()->phy_test) { - trigger_MAC_UE_RA(mac, NULL); + // TPC + unsigned char tpc_command = (unsigned char)((rar->UL_GRANT_4 >> 1) & 0x07); + ra->Msg3_TPC = (tpc_command << 1) - 6; + + // MCS + rar_grant.mcs = (unsigned char)(rar->UL_GRANT_4 >> 4); + // time alloc + rar_grant.Msg3_t_alloc = (unsigned char)(rar->UL_GRANT_3 & 0x0f); + // frequency alloc + rar_grant.Msg3_f_alloc = (uint16_t)((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12)); + // frequency hopping + rar_grant.freq_hopping = (unsigned char)(rar->UL_GRANT_1 >> 2); + + // Schedule Msg3 + const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; + const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; + const NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[current_DL_BWP->bwp_id]; + const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id); + NR_tda_info_t tda_info = get_ul_tda_info(current_UL_BWP, + *ra_SS->controlResourceSetId, + ra_SS->searchSpaceType->present, + TYPE_RA_RNTI_, + rar_grant.Msg3_t_alloc); + if (!tda_info.valid_tda || tda_info.nrOfSymbols == 0) { + LOG_E(MAC, "Cannot schedule Msg3. Something wrong in TDA information\n"); + // resume RAR response window timer if MSG2 decoding failed + nr_timer_suspension(&mac->ra.response_window_timer); + return; + } + frame_t frame_tx = 0; + int slot_tx = 0; + const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); + int ret = nr_ue_pusch_scheduler(mac, 1, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset); + + // TA command + // if the timeAlignmentTimer associated with this TAG is not running + if (!nr_timer_is_active(&mac->time_alignment_timer)) { + const int ta = rar->TA2 + (rar->TA1 << 5); + set_time_alignment(mac, ta, rar_ta, frame_tx, slot_tx); + LOG_W(MAC, "received TA command %d\n", 31 + ta); + } + // else ignore the received Timing Advance Command + +#ifdef DEBUG_RAR + LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E); + LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T); + LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID); + + LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R); + LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1); + + LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2); + LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1); + + LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2); + LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3); + LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4); + + LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1); + LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2); + + LOG_I(NR_MAC, + "[%d.%d]: [UE %d] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d\n", + frame, + slot, + mac->ue_id, + rar_grant.Msg3_t_alloc, + rar_grant.Msg3_f_alloc, + ta_command, + rar_grant.mcs, + rar_grant.freq_hopping, + tpc_command); +#endif + + if (ret != -1) { + uint16_t rnti = mac->crnti; + // Upon successful reception, set the T-CRNTI to the RAR value + // if the RA preamble is selected among the contention-based RA Preambles + if (!ra->cfra) { + ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8); + rnti = ra->t_crnti; + if (!mac->msg3_C_RNTI) + nr_mac_rrc_msg3_ind(mac->ue_id, rnti, false); + } + fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH); + if (!pdu) + return; + // Config Msg3 PDU + int ret = nr_config_pusch_pdu(mac, + &tda_info, + &pdu->pusch_config_pdu, + NULL, + NULL, + &rar_grant, + rnti, + NR_SearchSpace__searchSpaceType_PR_common, + NR_DCI_NONE); + if (ret != 0) + remove_ul_config_last_item(pdu); + release_ul_config(pdu, false); + } +} + +///////////////////////////////////// +// Random Access Response PDU // +// TS 38.213 ch 8.2 // +// TS 38.321 ch 6.2.3 // +///////////////////////////////////// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise +//| E | T | R A P I D |// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// +//| R | T A |// +//| T A | UL grant |// +//| UL grant |// +//| UL grant |// +//| UL grant |// +//| T C - R N T I |// +//| T C - R N T I |// +///////////////////////////////////// +// UL grant (27 bits) // +///////////////////////////////////// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise +//|-------------------|FHF|F_alloc|// +//| Freq allocation |// +//| F_alloc |Time allocation|// +//| MCS | TPC |CSI|// +///////////////////////////////////// +// TbD WIP Msg3 development ongoing +// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213 +// - apply tpc command +// WIP fix: +// - time domain indication hardcoded to 0 for k2 offset +// - extend TS 38.213 ch 8.3 Msg3 PUSCH +// - b buffer +// - ulsch power offset +// - optimize: mu_pusch, j and table_6_1_2_1_1_2_time_dom_res_alloc_A are already defined in nr_ue_procedures +static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) +{ + frame_t frame = dl_info->frame; + int slot = dl_info->slot; + + if(dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.ack_nack == 0) { + LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] CRC check failed on RAR (NAK)\n", mac->ue_id, frame, slot); + return; + } + + RA_config_t *ra = &mac->ra; + ra->t_crnti = 0; + uint8_t n_subPDUs = 0; // number of RAR payloads + uint8_t n_subheaders = 0; // number of MAC RAR subheaders + uint8_t *dlsch_buffer = dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu; + NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer + NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); // RAR subPDU pointer + uint8_t preamble_index = ra->ra_PreambleIndex; + uint16_t rnti = mac->ra.ra_rnti; + + T(T_NRUE_MAC_DL_RAR_PDU_WITH_DATA, T_INT(rnti), T_INT(frame), T_INT(slot), + T_BUFFER(dlsch_buffer, dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu_length)); + + ra->RA_backoff_limit = 0; + LOG_D(NR_MAC, "[%d.%d]: [UE %d][RAPROC] MAC received RAR (current preamble %d)\n", frame, slot, mac->ue_id, preamble_index); + + while (1) { + n_subheaders++; + if (rarh->T == 1) { + n_subPDUs++; + LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got RAPID RAR subPDU\n", mac->ue_id, rnti); + } else { + int bi_ms = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI] * ra->scaling_factor_bi; + int slots_per_ms = mac->frame_structure.numb_slots_frame / 10; + ra->RA_backoff_limit = bi_ms * slots_per_ms; + LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got BI RAR subPDU %d ms\n", mac->ue_id, rnti, bi_ms); + if (((NR_RA_HEADER_BI *)rarh)->E == 1) { + rarh += sizeof(NR_RA_HEADER_BI); + continue; + } else { break; } - nr_ue_process_mac_pdu(mac, dl_info, pdu_id); + } + if (rarh->RAPID == preamble_index) { + // The MAC entity may stop ra-ResponseWindow (and hence monitoring for Random Access Response(s)) after + // successful reception of a Random Access Response containing Random Access Preamble identifiers + // that matches the transmitted PREAMBLE_INDEX. + nr_timer_stop(&ra->response_window_timer); + LOG_A(NR_MAC, "[UE %d][RAPROC][%d.%d] Found RAR with the intended RAPID %d\n", mac->ue_id, frame, slot, rarh->RAPID); + rar = (NR_MAC_RAR *) (dlsch_buffer + n_subheaders + (n_subPDUs - 1) * sizeof(NR_MAC_RAR)); + handle_rar_reception(mac, rar, frame, slot); + if (ra->cfra) + nr_ra_succeeded(mac, dl_info->gNB_index, frame, slot); break; - case FAPI_NR_RX_PDU_TYPE_RAR : - nr_ue_process_rar(mac, dl_info, pdu_id); + } + if (rarh->E == 0) { + LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] Received RAR preamble (%d) doesn't match the intended RAPID (%d)\n", + mac->ue_id, + frame, + slot, + rarh->RAPID, + preamble_index); + // resume RAR response window timer if MSG2 decoding failed + nr_timer_suspension(&mac->ra.response_window_timer); break; - default : - AssertFatal(false, "Invalid DLSCH PDU type\n"); + } else { + rarh += sizeof(NR_MAC_RAR) + 1; + } } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT); + +#ifdef DEBUG_RAR + LOG_D(NR_MAC, + "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n", + frame, + slot, + n_subheaders, + n_subPDUs); + LOG_D(NR_MAC, + "[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n", + *(uint8_t *) rarh, + rar[0], + rar[1], + rar[2], + rar[3], + rar[4], + rar[5], + rarh->RAPID, + preamble_index); +#endif + return; } // #define EXTRACT_DCI_ITEM(val,size) val= readBits(dci_pdu, &pos, size); @@ -3615,17 +3754,25 @@ static nr_dci_format_t nr_extract_dci_info(NR_UE_MAC_INST_t *mac, return format; } -static void set_time_alignment(NR_UE_MAC_INST_t *mac, int ta, ta_type_t type, int frame, int slot) +nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci) { - NR_UL_TIME_ALIGNMENT_t *ul_time_alignment = &mac->ul_time_alignment; - ul_time_alignment->ta_command = ta; - ul_time_alignment->ta_apply = type; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); - const int n_slots_frame = mac->frame_structure.numb_slots_frame; - ul_time_alignment->frame = (frame + (slot + ntn_ue_koffset) / n_slots_frame) % MAX_FRAME_NUMBER; - ul_time_alignment->slot = (slot + ntn_ue_koffset) % n_slots_frame; - // start or restart the timeAlignmentTimer associated with the indicated TAG - nr_timer_start(&mac->time_alignment_timer); + LOG_D(NR_MAC, + "Received dci indication (rnti %x, dci format %d, n_CCE %d, payloadSize %d, payload %llx)\n", + dci->rnti, + dci->dci_format, + dci->n_CCE, + dci->payloadSize, + *(unsigned long long *)dci->payloadBits); + const nr_dci_format_t format = + nr_extract_dci_info(mac, dci->dci_format, dci->payloadSize, dci->rnti, dci->ss_type, dci->payloadBits, slot); + if (format == NR_DCI_NONE) + return NR_DCI_NONE; + int ret = nr_ue_process_dci(mac, frame, slot, mac->def_dci_pdu_rel15[slot] + format, dci, format); + if (ret < 0) { + mac->stats.bad_dci++; + return NR_DCI_NONE; + } + return format; } static bool check_ra_contention_resolution(const uint8_t *pdu, const uint8_t *cont_res) @@ -4055,240 +4202,36 @@ int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, return mac_ce - pdu; } -static void handle_rar_reception(NR_UE_MAC_INST_t *mac, NR_MAC_RAR *rar, frame_t frame, int slot) +void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) { - RAR_grant_t rar_grant; - RA_config_t *ra = &mac->ra; -#ifdef DEBUG_RAR - // CSI - unsigned char csi_req = (unsigned char)(rar->UL_GRANT_4 & 0x01); -#endif + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN); - // TPC - unsigned char tpc_command = (unsigned char)((rar->UL_GRANT_4 >> 1) & 0x07); - ra->Msg3_TPC = (tpc_command << 1) - 6; + LOG_D(NR_MAC, + "In [%d.%d] Handling DLSCH PDU type %d\n", + dl_info->frame, + dl_info->slot, + dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type); - // MCS - rar_grant.mcs = (unsigned char)(rar->UL_GRANT_4 >> 4); - // time alloc - rar_grant.Msg3_t_alloc = (unsigned char)(rar->UL_GRANT_3 & 0x0f); - // frequency alloc - rar_grant.Msg3_f_alloc = (uint16_t)((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12)); - // frequency hopping - rar_grant.freq_hopping = (unsigned char)(rar->UL_GRANT_1 >> 2); - - // Schedule Msg3 - const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP; - const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP; - const NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[current_DL_BWP->bwp_id]; - const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id); - NR_tda_info_t tda_info = get_ul_tda_info(current_UL_BWP, - *ra_SS->controlResourceSetId, - ra_SS->searchSpaceType->present, - TYPE_RA_RNTI_, - rar_grant.Msg3_t_alloc); - if (!tda_info.valid_tda || tda_info.nrOfSymbols == 0) { - LOG_E(MAC, "Cannot schedule Msg3. Something wrong in TDA information\n"); - // resume RAR response window timer if MSG2 decoding failed - nr_timer_suspension(&mac->ra.response_window_timer); - return; - } - frame_t frame_tx = 0; - int slot_tx = 0; - const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs); - int ret = nr_ue_pusch_scheduler(mac, 1, frame, slot, &frame_tx, &slot_tx, tda_info.k2 + ntn_ue_koffset); - - // TA command - // if the timeAlignmentTimer associated with this TAG is not running - if (!nr_timer_is_active(&mac->time_alignment_timer)) { - const int ta = rar->TA2 + (rar->TA1 << 5); - set_time_alignment(mac, ta, rar_ta, frame_tx, slot_tx); - LOG_W(MAC, "received TA command %d\n", 31 + ta); - } - // else ignore the received Timing Advance Command - -#ifdef DEBUG_RAR - LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E); - LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T); - LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID); - - LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R); - LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1); - - LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2); - LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1); - - LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2); - LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3); - LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4); - - LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1); - LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2); - - LOG_I(NR_MAC, - "[%d.%d]: [UE %d] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d\n", - frame, - slot, - mac->ue_id, - rar_grant.Msg3_t_alloc, - rar_grant.Msg3_f_alloc, - ta_command, - rar_grant.mcs, - rar_grant.freq_hopping, - tpc_command); -#endif - - if (ret != -1) { - uint16_t rnti = mac->crnti; - // Upon successful reception, set the T-CRNTI to the RAR value - // if the RA preamble is selected among the contention-based RA Preambles - if (!ra->cfra) { - ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8); - rnti = ra->t_crnti; - if (!mac->msg3_C_RNTI) - nr_mac_rrc_msg3_ind(mac->ue_id, rnti, false); - } - fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH); - if (!pdu) - return; - // Config Msg3 PDU - int ret = nr_config_pusch_pdu(mac, - &tda_info, - &pdu->pusch_config_pdu, - NULL, - NULL, - &rar_grant, - rnti, - NR_SearchSpace__searchSpaceType_PR_common, - NR_DCI_NONE); - if (ret != 0) - remove_ul_config_last_item(pdu); - release_ul_config(pdu, false); - } -} - -///////////////////////////////////// -// Random Access Response PDU // -// TS 38.213 ch 8.2 // -// TS 38.321 ch 6.2.3 // -///////////////////////////////////// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise -//| E | T | R A P I D |// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// -//| R | T A |// -//| T A | UL grant |// -//| UL grant |// -//| UL grant |// -//| UL grant |// -//| T C - R N T I |// -//| T C - R N T I |// -///////////////////////////////////// -// UL grant (27 bits) // -///////////////////////////////////// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise -//|-------------------|FHF|F_alloc|// -//| Freq allocation |// -//| F_alloc |Time allocation|// -//| MCS | TPC |CSI|// -///////////////////////////////////// -// TbD WIP Msg3 development ongoing -// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213 -// - apply tpc command -// WIP fix: -// - time domain indication hardcoded to 0 for k2 offset -// - extend TS 38.213 ch 8.3 Msg3 PUSCH -// - b buffer -// - ulsch power offset -// - optimize: mu_pusch, j and table_6_1_2_1_1_2_time_dom_res_alloc_A are already defined in nr_ue_procedures -static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) -{ - frame_t frame = dl_info->frame; - int slot = dl_info->slot; - - if(dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.ack_nack == 0) { - LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] CRC check failed on RAR (NAK)\n", mac->ue_id, frame, slot); - return; - } - - RA_config_t *ra = &mac->ra; - ra->t_crnti = 0; - uint8_t n_subPDUs = 0; // number of RAR payloads - uint8_t n_subheaders = 0; // number of MAC RAR subheaders - uint8_t *dlsch_buffer = dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu; - NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer - NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); // RAR subPDU pointer - uint8_t preamble_index = ra->ra_PreambleIndex; - uint16_t rnti = mac->ra.ra_rnti; - - T(T_NRUE_MAC_DL_RAR_PDU_WITH_DATA, T_INT(rnti), T_INT(frame), T_INT(slot), - T_BUFFER(dlsch_buffer, dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu_length)); - - ra->RA_backoff_limit = 0; - LOG_D(NR_MAC, "[%d.%d]: [UE %d][RAPROC] MAC received RAR (current preamble %d)\n", frame, slot, mac->ue_id, preamble_index); - - while (1) { - n_subheaders++; - if (rarh->T == 1) { - n_subPDUs++; - LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got RAPID RAR subPDU\n", mac->ue_id, rnti); - } else { - int bi_ms = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI] * ra->scaling_factor_bi; - int slots_per_ms = mac->frame_structure.numb_slots_frame / 10; - ra->RA_backoff_limit = bi_ms * slots_per_ms; - LOG_I(NR_MAC, "[UE %d][RAPROC][RA-RNTI %04x] Got BI RAR subPDU %d ms\n", mac->ue_id, rnti, bi_ms); - if (((NR_RA_HEADER_BI *)rarh)->E == 1) { - rarh += sizeof(NR_RA_HEADER_BI); - continue; - } else { + // Processing MAC PDU + // it parses MAC CEs subheaders, MAC CEs, SDU subheaderds and SDUs + switch (dl_info->rx_ind->rx_indication_body[pdu_id].pdu_type) { + case FAPI_NR_RX_PDU_TYPE_DLSCH : + // start or restart dataInactivityTimer if any MAC entity receives a MAC SDU for DTCH logical channel, + // DCCH logical channel, or CCCH logical channel + if (mac->data_inactivity_timer) + nr_timer_start(mac->data_inactivity_timer); + // DL data arrival during RRC_CONNECTED when UL synchronisation status is "non-synchronised" + if (!nr_timer_is_active(&mac->time_alignment_timer) && mac->state == UE_CONNECTED && !get_softmodem_params()->phy_test) { + trigger_MAC_UE_RA(mac, NULL); break; } - } - if (rarh->RAPID == preamble_index) { - // The MAC entity may stop ra-ResponseWindow (and hence monitoring for Random Access Response(s)) after - // successful reception of a Random Access Response containing Random Access Preamble identifiers - // that matches the transmitted PREAMBLE_INDEX. - nr_timer_stop(&ra->response_window_timer); - LOG_A(NR_MAC, "[UE %d][RAPROC][%d.%d] Found RAR with the intended RAPID %d\n", mac->ue_id, frame, slot, rarh->RAPID); - rar = (NR_MAC_RAR *) (dlsch_buffer + n_subheaders + (n_subPDUs - 1) * sizeof(NR_MAC_RAR)); - handle_rar_reception(mac, rar, frame, slot); - if (ra->cfra) - nr_ra_succeeded(mac, dl_info->gNB_index, frame, slot); + nr_ue_process_mac_pdu(mac, dl_info, pdu_id); break; - } - if (rarh->E == 0) { - LOG_W(NR_MAC,"[UE %d][RAPROC][%d.%d] Received RAR preamble (%d) doesn't match the intended RAPID (%d)\n", - mac->ue_id, - frame, - slot, - rarh->RAPID, - preamble_index); - // resume RAR response window timer if MSG2 decoding failed - nr_timer_suspension(&mac->ra.response_window_timer); + case FAPI_NR_RX_PDU_TYPE_RAR : + nr_ue_process_rar(mac, dl_info, pdu_id); break; - } else { - rarh += sizeof(NR_MAC_RAR) + 1; - } + default : + AssertFatal(false, "Invalid DLSCH PDU type\n"); } - -#ifdef DEBUG_RAR - LOG_D(NR_MAC, - "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n", - frame, - slot, - n_subheaders, - n_subPDUs); - LOG_D(NR_MAC, - "[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n", - *(uint8_t *) rarh, - rar[0], - rar[1], - rar[2], - rar[3], - rar[4], - rar[5], - rarh->RAPID, - preamble_index); -#endif - return; + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT); } - From a6239085185a1646fc807dfc44ba19dd3477322a Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 19 Dec 2025 15:48:13 +0100 Subject: [PATCH 06/23] declare static some static functions in dci procedure file --- .../LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c index af6bf37aff..477e39a60a 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_dci_configuration.c @@ -38,7 +38,7 @@ #include "executables/softmodem-common.h" #include -void fill_dci_search_candidates(const NR_SearchSpace_t *ss, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, const uint32_t Y) +static void fill_dci_search_candidates(const NR_SearchSpace_t *ss, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, const uint32_t Y) { LOG_T(NR_MAC_DCI, "Filling search candidates for DCI\n"); @@ -89,7 +89,7 @@ void fill_dci_search_candidates(const NR_SearchSpace_t *ss, fapi_nr_dl_config_dc rel15->number_of_candidates = i; } -NR_ControlResourceSet_t *ue_get_coreset(const NR_BWP_PDCCH_t *config, const int coreset_id) +static NR_ControlResourceSet_t *ue_get_coreset(const NR_BWP_PDCCH_t *config, const int coreset_id) { if (config->commonControlResourceSet && coreset_id == config->commonControlResourceSet->controlResourceSetId) return config->commonControlResourceSet; @@ -362,12 +362,12 @@ bool is_ss_monitor_occasion(const int frame, const int slot, const int slots_per return monitor; } -bool search_space_monitoring_ocasion_other_si(NR_UE_MAC_INST_t *mac, - const NR_SearchSpace_t *ss, - const int abs_slot, - const int frame, - const int slot, - const int slots_per_frame) +static bool search_space_monitoring_ocasion_other_si(NR_UE_MAC_INST_t *mac, + const NR_SearchSpace_t *ss, + const int abs_slot, + const int frame, + const int slot, + const int slots_per_frame) { const int duration = ss->duration ? *ss->duration : 1; int period, offset; From 90d4d2726c1a23d10c225d33f3b00e8f94798bee Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 19 Dec 2025 15:49:56 +0100 Subject: [PATCH 07/23] cleanup of functions in nr_ue_power_procedures (no code change, just moving things around) --- .../LAYER2/NR_MAC_UE/nr_ue_power_procedures.c | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index 860089165f..a409cbe653 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -58,13 +58,6 @@ static const int nb_symbols_excluding_dmrs[11][2][2] /* 14 */ {{ 12 , 12 } , { 10 , 10 }}, }; -static int get_deltatf(uint16_t nb_of_prbs, - uint8_t N_symb_PUCCH, - uint8_t freq_hop_flag, - uint8_t add_dmrs_flag, - int N_sc_ctrl_RB, - int O_UCI); - // ∆MPR according to Table 6.2.2-3 38.101-1 static float get_delta_mpr(uint16_t nr_band, frame_type_t frame_type, int scs, int channel_bandwidth, int n_prbs, int start_prb, int power_class) { @@ -256,6 +249,28 @@ int get_sum_delta_pucch(NR_UE_MAC_INST_t *mac, int slot, frame_t frame) return delta_tpc_sum; } +static int get_deltatf(uint16_t nb_of_prbs, + uint8_t N_symb_PUCCH, + uint8_t freq_hop_flag, + uint8_t add_dmrs_flag, + int N_sc_ctrl_RB, + int O_UCI) +{ + int DELTA_TF; + int O_CRC = compute_pucch_crc_size(O_UCI); + int N_symb = N_symb_PUCCH < 4 ? N_symb_PUCCH : nb_symbols_excluding_dmrs[N_symb_PUCCH - 4][add_dmrs_flag][freq_hop_flag]; + float N_RE = nb_of_prbs * N_sc_ctrl_RB * N_symb; + float K1 = 6; + if (O_UCI + O_CRC < 12) + DELTA_TF = 10 * log10((double)(((K1 * (O_UCI)) / N_RE))); + else { + float K2 = 2.4; + float BPRE = (O_UCI + O_CRC) / N_RE; + DELTA_TF = 10 * log10((double)(pow(2, (K2 * BPRE)) - 1)); + } + return DELTA_TF; +} + // PUCCH Power control according to 38.213 section 7.2.1 int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, int scs, @@ -399,28 +414,6 @@ int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, return pucch_power; } -static int get_deltatf(uint16_t nb_of_prbs, - uint8_t N_symb_PUCCH, - uint8_t freq_hop_flag, - uint8_t add_dmrs_flag, - int N_sc_ctrl_RB, - int O_UCI) -{ - int DELTA_TF; - int O_CRC = compute_pucch_crc_size(O_UCI); - int N_symb = N_symb_PUCCH < 4 ? N_symb_PUCCH : nb_symbols_excluding_dmrs[N_symb_PUCCH - 4][add_dmrs_flag][freq_hop_flag]; - float N_RE = nb_of_prbs * N_sc_ctrl_RB * N_symb; - float K1 = 6; - if (O_UCI + O_CRC < 12) - DELTA_TF = 10 * log10((double)(((K1 * (O_UCI)) / N_RE))); - else { - float K2 = 2.4; - float BPRE = (O_UCI + O_CRC) / N_RE; - DELTA_TF = 10 * log10((double)(pow(2, (K2 * BPRE)) - 1)); - } - return DELTA_TF; -} - // Returns the pathloss in dB for the active UL BWP on the selected carrier based on the DL RS associated with the PRACH transmission // computation according to clause 7.4 (Physical random access channel) of 3GPP TS 38.213 version 16.3.0 Release 16 // Assumptions: From 3aad8d774805a03a974f06437584d5f94b68678e Mon Sep 17 00:00:00 2001 From: francescomani Date: Sun, 28 Dec 2025 11:14:55 +0100 Subject: [PATCH 08/23] move NTN L1 functions to a new file from nr-ue --- CMakeLists.txt | 1 + executables/nr-ue.c | 171 ---------------- openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c | 191 ++++++++++++++++++ .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 12 +- 4 files changed, 203 insertions(+), 172 deletions(-) create mode 100644 openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 70ea409d36..036088abf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -979,6 +979,7 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/cic_filter_nr.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_initial_sync_sl.c + ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_pbch.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 2772fc5aae..f9740978cd 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -497,8 +497,6 @@ static uint64_t get_carrier_frequency(const int N_RB, const int mu, const uint32 return carrier_freq; } -static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx); - static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE) { NR_DL_FRAME_PARMS *fp = &UE->frame_parms; @@ -735,175 +733,6 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) { return rem_samples + next_slot_first_symbol; } -static void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) -{ - if (!UE->nrUE_config.ntn_config.is_targetcell) - return; - UE->nrUE_config.ntn_config.is_targetcell = false; - - const int epoch_frame = UE->nrUE_config.ntn_config.epoch_sfn; - const int diff1 = (frame - epoch_frame + 1024) % 1024; - const int diff2 = (epoch_frame - frame + 1024) % 1024; - - int *epoch_hfn = &UE->nrUE_config.ntn_config.epoch_hfn; - if (diff1 < diff2) { // epoch_frame in the past - LOG_I(PHY, "epoch is %d frames in the past\n", diff1); - if (epoch_frame <= frame) - *epoch_hfn = hfn; - else - *epoch_hfn = hfn - 1; - } else { // epoch_frame in the future - LOG_I(PHY, "epoch is %d frames in the future\n", diff2); - if (epoch_frame >= frame) - *epoch_hfn = hfn; - else - *epoch_hfn = hfn + 1; - } - LOG_I(PHY, "setting epoch_hfn = %d\n", *epoch_hfn); -} - -#define SPEED_OF_LIGHT 299792458 - -// calculate TA and Doppler based on the NTN-Config, if abs_subframe_tx < 0 then apply only Doppler -static void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) -{ - const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; - - // Handle terrestrial networks - if (ntn_config_params->cell_specific_k_offset == 0) { - if (abs_subframe_tx >= 0) - UE->timing_advance_ntn = 0; - UE->dl_Doppler_shift = 0; - UE->ul_Doppler_shift = 0; - return; - } - - const int abs_subframe_epoch = ntn_config_params->epoch_subframe - + ntn_config_params->epoch_sfn * 10 - + ntn_config_params->epoch_hfn * 10240; - const int ms_since_epoch = abs_subframe_tx < 0 ? 0 : abs_subframe_tx - abs_subframe_epoch; - - const position_t pos_sat_0 = ntn_config_params->pos_sat_0; - const position_t vel_sat_0 = ntn_config_params->vel_sat_0; - - position_t pos_sat; - position_t vel_sat; - - const double omega = ntn_config_params->omega; - if (omega) { - const double cos_wt = cos(omega * ms_since_epoch); - const double sin_wt = sin(omega * ms_since_epoch); - - const position_t pos_sat_90 = ntn_config_params->pos_sat_90; - pos_sat = (position_t){pos_sat_0.X * cos_wt + pos_sat_90.X * sin_wt, - pos_sat_0.Y * cos_wt + pos_sat_90.Y * sin_wt, - pos_sat_0.Z * cos_wt + pos_sat_90.Z * sin_wt}; - - const position_t vel_sat_90 = ntn_config_params->vel_sat_90; - vel_sat = (position_t){vel_sat_0.X * cos_wt + vel_sat_90.X * sin_wt, - vel_sat_0.Y * cos_wt + vel_sat_90.Y * sin_wt, - vel_sat_0.Z * cos_wt + vel_sat_90.Z * sin_wt}; - } else { - pos_sat = (position_t){pos_sat_0.X + vel_sat_0.X * ms_since_epoch / 1000, - pos_sat_0.Y + vel_sat_0.Y * ms_since_epoch / 1000, - pos_sat_0.Z + vel_sat_0.Z * ms_since_epoch / 1000}; - vel_sat = vel_sat_0; - } - - position_t pos_ue = {0}; - get_position_coordinates(UE->Mod_id, &pos_ue); - - // calculate directional vector from SAT to UE - const position_t dir_sat_ue = {pos_ue.X - pos_sat.X, pos_ue.Y - pos_sat.Y, pos_ue.Z - pos_sat.Z}; - - // calculate distance between SAT and UE - const double distance = sqrt(dir_sat_ue.X * dir_sat_ue.X + dir_sat_ue.Y * dir_sat_ue.Y + dir_sat_ue.Z * dir_sat_ue.Z); - - // calculate projected velocity from SAT towards UE - const double vel_sat_ue = (vel_sat.X * dir_sat_ue.X + vel_sat.Y * dir_sat_ue.Y + vel_sat.Z * dir_sat_ue.Z) / distance; - - // calculate DL Doppler shift (moving source) - const double dl_Doppler_shift = (vel_sat_ue / (SPEED_OF_LIGHT - vel_sat_ue)) * fp->dl_CarrierFreq; - - // calculate UL Doppler shift (moving target) - const double ul_Doppler_shift = (vel_sat_ue / SPEED_OF_LIGHT) * fp->ul_CarrierFreq; - - // calculate round-trip-time (factor 2) between SAT and UE in ms (factor 1000) - const double N_UE_TA_adj = 2000 * distance / SPEED_OF_LIGHT; - - const double N_common_ta_adj = ntn_config_params->N_common_ta_adj; - const double N_common_ta_drift = ntn_config_params->N_common_ta_drift; - const double N_common_ta_drift_variant = ntn_config_params->N_common_ta_drift_variant; - - if (abs_subframe_tx >= 0) { - UE->timing_advance_ntn = (N_UE_TA_adj + N_common_ta_adj + N_common_ta_drift * ms_since_epoch / 1e6 - + N_common_ta_drift_variant * ((int64_t)ms_since_epoch * ms_since_epoch) / 1e9) - * fp->samples_per_subframe; - } - - UE->freq_offset -= dl_Doppler_shift - UE->dl_Doppler_shift; - UE->dl_Doppler_shift = dl_Doppler_shift; - UE->ul_Doppler_shift = ul_Doppler_shift; - - if (abs_subframe_tx < 0) { - LOG_I(PHY, - "satellite velocity towards UE = %f m/s, DL Doppler shift = %f kHz, UL Doppler shift = %f kHz\n", - vel_sat_ue, - dl_Doppler_shift / 1000, - ul_Doppler_shift / 1000); - } -} - -// apply values based on the NTN-Config -static void apply_ntn_config(PHY_VARS_NR_UE *UE, - NR_DL_FRAME_PARMS *fp, - int hfn_rx, - int frame_rx, - int slot_rx, - int *duration_rx_to_tx, - int *timing_advance, - int *ntn_koffset, - bool *ntn_targetcell) -{ - // return if there is no pending update - if (!UE->nrUE_config.ntn_config.params_changed) - return; - - *ntn_targetcell = UE->nrUE_config.ntn_config.is_targetcell; - - // skip update if it is for the target cell - if (UE->nrUE_config.ntn_config.is_targetcell) - return; - - UE->nrUE_config.ntn_config.params_changed = false; - - const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; - - const int mu = fp->numerology_index; - const int koffset = ntn_config_params->cell_specific_k_offset; - - *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); - if (koffset > *ntn_koffset) - *timing_advance += get_samples_slot_duration(fp, slot_rx, (koffset - *ntn_koffset) << mu); - else if (koffset < *ntn_koffset) - *timing_advance -= get_samples_slot_duration(fp, slot_rx, (*ntn_koffset - koffset) << mu); - *ntn_koffset = koffset; - - const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); - apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); - - LOG_I(PHY, - "k_offset: %dms, N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s², " - "timing_advance_ntn: %d samples, DL Doppler shift: %fkHz, UL Doppler shift: %fkHz\n", - koffset << mu, - ntn_config_params->N_common_ta_adj, - ntn_config_params->N_common_ta_drift, - ntn_config_params->N_common_ta_drift_variant, - UE->timing_advance_ntn, - UE->dl_Doppler_shift / 1000, - UE->ul_Doppler_shift / 1000); -} - void *UE_thread(void *arg) { //this thread should be over the processing thread to keep in real time diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c new file mode 100644 index 0000000000..51031d5449 --- /dev/null +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ntn_l1.c @@ -0,0 +1,191 @@ +/* + * 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 "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" + +void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame) +{ + if (!UE->nrUE_config.ntn_config.is_targetcell) + return; + UE->nrUE_config.ntn_config.is_targetcell = false; + + const int epoch_frame = UE->nrUE_config.ntn_config.epoch_sfn; + const int diff1 = (frame - epoch_frame + 1024) % 1024; + const int diff2 = (epoch_frame - frame + 1024) % 1024; + + int *epoch_hfn = &UE->nrUE_config.ntn_config.epoch_hfn; + if (diff1 < diff2) { // epoch_frame in the past + LOG_I(PHY, "epoch is %d frames in the past\n", diff1); + if (epoch_frame <= frame) + *epoch_hfn = hfn; + else + *epoch_hfn = hfn - 1; + } else { // epoch_frame in the future + LOG_I(PHY, "epoch is %d frames in the future\n", diff2); + if (epoch_frame >= frame) + *epoch_hfn = hfn; + else + *epoch_hfn = hfn + 1; + } + LOG_I(PHY, "setting epoch_hfn = %d\n", *epoch_hfn); +} + +#define SPEED_OF_LIGHT 299792458 + +// calculate TA and Doppler based on the NTN-Config, if abs_subframe_tx < 0 then apply only Doppler +void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx) +{ + const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; + + // Handle terrestrial networks + if (ntn_config_params->cell_specific_k_offset == 0) { + if (abs_subframe_tx >= 0) + UE->timing_advance_ntn = 0; + UE->dl_Doppler_shift = 0; + UE->ul_Doppler_shift = 0; + return; + } + + const int abs_subframe_epoch = ntn_config_params->epoch_subframe + + ntn_config_params->epoch_sfn * 10 + + ntn_config_params->epoch_hfn * 10240; + const int ms_since_epoch = abs_subframe_tx < 0 ? 0 : abs_subframe_tx - abs_subframe_epoch; + + const position_t pos_sat_0 = ntn_config_params->pos_sat_0; + const position_t vel_sat_0 = ntn_config_params->vel_sat_0; + + position_t pos_sat; + position_t vel_sat; + + const double omega = ntn_config_params->omega; + if (omega) { + const double cos_wt = cos(omega * ms_since_epoch); + const double sin_wt = sin(omega * ms_since_epoch); + + const position_t pos_sat_90 = ntn_config_params->pos_sat_90; + pos_sat = (position_t){pos_sat_0.X * cos_wt + pos_sat_90.X * sin_wt, + pos_sat_0.Y * cos_wt + pos_sat_90.Y * sin_wt, + pos_sat_0.Z * cos_wt + pos_sat_90.Z * sin_wt}; + + const position_t vel_sat_90 = ntn_config_params->vel_sat_90; + vel_sat = (position_t){vel_sat_0.X * cos_wt + vel_sat_90.X * sin_wt, + vel_sat_0.Y * cos_wt + vel_sat_90.Y * sin_wt, + vel_sat_0.Z * cos_wt + vel_sat_90.Z * sin_wt}; + } else { + pos_sat = (position_t){pos_sat_0.X + vel_sat_0.X * ms_since_epoch / 1000, + pos_sat_0.Y + vel_sat_0.Y * ms_since_epoch / 1000, + pos_sat_0.Z + vel_sat_0.Z * ms_since_epoch / 1000}; + vel_sat = vel_sat_0; + } + + position_t pos_ue = {0}; + get_position_coordinates(UE->Mod_id, &pos_ue); + + // calculate directional vector from SAT to UE + const position_t dir_sat_ue = {pos_ue.X - pos_sat.X, pos_ue.Y - pos_sat.Y, pos_ue.Z - pos_sat.Z}; + + // calculate distance between SAT and UE + const double distance = sqrt(dir_sat_ue.X * dir_sat_ue.X + dir_sat_ue.Y * dir_sat_ue.Y + dir_sat_ue.Z * dir_sat_ue.Z); + + // calculate projected velocity from SAT towards UE + const double vel_sat_ue = (vel_sat.X * dir_sat_ue.X + vel_sat.Y * dir_sat_ue.Y + vel_sat.Z * dir_sat_ue.Z) / distance; + + // calculate DL Doppler shift (moving source) + const double dl_Doppler_shift = (vel_sat_ue / (SPEED_OF_LIGHT - vel_sat_ue)) * fp->dl_CarrierFreq; + + // calculate UL Doppler shift (moving target) + const double ul_Doppler_shift = (vel_sat_ue / SPEED_OF_LIGHT) * fp->ul_CarrierFreq; + + // calculate round-trip-time (factor 2) between SAT and UE in ms (factor 1000) + const double N_UE_TA_adj = 2000 * distance / SPEED_OF_LIGHT; + + const double N_common_ta_adj = ntn_config_params->N_common_ta_adj; + const double N_common_ta_drift = ntn_config_params->N_common_ta_drift; + const double N_common_ta_drift_variant = ntn_config_params->N_common_ta_drift_variant; + + if (abs_subframe_tx >= 0) { + UE->timing_advance_ntn = (N_UE_TA_adj + N_common_ta_adj + N_common_ta_drift * ms_since_epoch / 1e6 + + N_common_ta_drift_variant * ((int64_t)ms_since_epoch * ms_since_epoch) / 1e9) + * fp->samples_per_subframe; + } + + UE->freq_offset -= dl_Doppler_shift - UE->dl_Doppler_shift; + UE->dl_Doppler_shift = dl_Doppler_shift; + UE->ul_Doppler_shift = ul_Doppler_shift; + + if (abs_subframe_tx < 0) { + LOG_I(PHY, + "satellite velocity towards UE = %f m/s, DL Doppler shift = %f kHz, UL Doppler shift = %f kHz\n", + vel_sat_ue, + dl_Doppler_shift / 1000, + ul_Doppler_shift / 1000); + } +} + +// apply values based on the NTN-Config +void apply_ntn_config(PHY_VARS_NR_UE *UE, + NR_DL_FRAME_PARMS *fp, + int hfn_rx, + int frame_rx, + int slot_rx, + int *duration_rx_to_tx, + int *timing_advance, + int *ntn_koffset, + bool *ntn_targetcell) +{ + // return if there is no pending update + if (!UE->nrUE_config.ntn_config.params_changed) + return; + + *ntn_targetcell = UE->nrUE_config.ntn_config.is_targetcell; + + // skip update if it is for the target cell + if (UE->nrUE_config.ntn_config.is_targetcell) + return; + + UE->nrUE_config.ntn_config.params_changed = false; + + const fapi_nr_ntn_config_t *ntn_config_params = &UE->nrUE_config.ntn_config; + + const int mu = fp->numerology_index; + const int koffset = ntn_config_params->cell_specific_k_offset; + + *duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu); + if (koffset > *ntn_koffset) + *timing_advance += get_samples_slot_duration(fp, slot_rx, (koffset - *ntn_koffset) << mu); + else if (koffset < *ntn_koffset) + *timing_advance -= get_samples_slot_duration(fp, slot_rx, (*ntn_koffset - koffset) << mu); + *ntn_koffset = koffset; + + const int abs_subframe_tx = 10240 * hfn_rx + 10 * frame_rx + ((slot_rx + *duration_rx_to_tx) >> mu); + apply_ntn_timing_advance_and_doppler(UE, fp, abs_subframe_tx); + + LOG_I(PHY, + "k_offset: %dms, N_Common_Ta: %fms, drift: %fµs/s, variant: %fµs/s², " + "timing_advance_ntn: %d samples, DL Doppler shift: %fkHz, UL Doppler shift: %fkHz\n", + koffset << mu, + ntn_config_params->N_common_ta_adj, + ntn_config_params->N_common_ta_drift, + ntn_config_params->N_common_ta_drift_variant, + UE->timing_advance_ntn, + UE->dl_Doppler_shift / 1000, + UE->ul_Doppler_shift / 1000); +} diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index ed348986e8..2a24902295 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -330,7 +330,17 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, pdsch_scope_req_t *scope_req); int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t slot, c16_t **txData); - +void apply_ntn_config(PHY_VARS_NR_UE *UE, + NR_DL_FRAME_PARMS *fp, + int hfn_rx, + int frame_rx, + int slot_rx, + int *duration_rx_to_tx, + int *timing_advance, + int *ntn_koffset, + bool *ntn_targetcell); +void fix_ntn_epoch_hfn(PHY_VARS_NR_UE *UE, int hfn, int frame); +void apply_ntn_timing_advance_and_doppler(PHY_VARS_NR_UE *UE, const NR_DL_FRAME_PARMS *fp, int abs_subframe_tx); void dump_nrdlsch(PHY_VARS_NR_UE *ue,uint8_t gNB_id,uint8_t nr_slot_rx,unsigned int *coded_bits_per_codeword,int round, unsigned char harq_pid); void nr_a_sum_b(c16_t *input_x, c16_t *input_y, unsigned short nb_rb); From 2a94a42397d6b695dd14bf1b1d31be95899de3ba Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 5 Jan 2026 08:42:12 +0100 Subject: [PATCH 09/23] rfsim: fix linking issue Fix an issue caused by assuming C++ linkage of get_currentchannels_type --- radio/rfsimulator/simulator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/rfsimulator/simulator.cpp b/radio/rfsimulator/simulator.cpp index a29001e0bf..65e32216c4 100644 --- a/radio/rfsimulator/simulator.cpp +++ b/radio/rfsimulator/simulator.cpp @@ -54,6 +54,10 @@ extern "C" { #include #include #include "rfsimulator.h" +extern int get_currentchannels_type(const char *buf, + int debug, + webdatadef_t *tdata, + telnet_printfunc_t prnt); // in random_channel.c } #include #include @@ -112,10 +116,6 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole; }; // clang-format on static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt); -extern int get_currentchannels_type(const char *buf, - int debug, - webdatadef_t *tdata, - telnet_printfunc_t prnt); // in random_channel.c static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); From f8f7bc890bff4fb3a0153b03a548125ae0798412 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 5 Jan 2026 08:49:20 +0100 Subject: [PATCH 10/23] rfsim: fix replay_node when used for rfsim without beamforming --- radio/rfsimulator/stored_node.c | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/rfsimulator/stored_node.c b/radio/rfsimulator/stored_node.c index 51db5bfea1..5792304671 100644 --- a/radio/rfsimulator/stored_node.c +++ b/radio/rfsimulator/stored_node.c @@ -177,6 +177,7 @@ int main(int argc, char *argv[]) { timestamp+=blockSize; header.option_value=0; header.option_flag=0; + header.beam_map = 1; } else { AssertFatal(read(fd,&header,sizeof(header)), ""); } From 692b3cf99c414641fd0fc59226111db4c1c66e35 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 5 Jan 2026 14:37:03 +0100 Subject: [PATCH 11/23] Fix missing return statements in SIB1 TDA validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provides fix to commit d7cc1c2c1db047a54556a0864632e63ca12299ec (MR !3641). Addresses an issue with low DL throughput in 100 MHz 2×2 test case observed in CI. --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index dd5558d546..6dd4886911 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -490,16 +490,16 @@ static bool check_sib1_tda(gNB_MAC_INST *gNB_mac, // the UE may assume that this PDSCH resource allocation is not applied (Table 5.1.2.1.1-4 and 5 in 38.214) int row = tda + 1; if (table_type == defaultB && (row == 12 || row == 13 || row == 14)) - false; + return false; if (table_type == defaultC && (row == 1 || row == 6 || row == 7 ||row > 12)) - false; + return false; // assuming normal CP NR_tda_info_t tda_info = get_info_from_tda_tables(table_type, tda, scc->dmrs_TypeA_Position, true); // SIB1 TDA going to the last symbol would cause overlap with CSI-RS if (!tda_info.valid_tda || tda_info.startSymbolIndex + tda_info.nrOfSymbols == 14) - false; + return false; if (tda_info.startSymbolIndex < type0_PDCCH_CSS_config->first_symbol_index + type0_PDCCH_CSS_config->num_symbols) - false; + return false; NR_pdsch_dmrs_t dmrs_parms = get_dl_dmrs_params(scc, NULL, &tda_info, 1); // Configure sched_ctrlCommon for SIB1 From 98e278b0d5f5b166e58396b200f9e9c3fbac31ab Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 26 Dec 2025 14:00:53 +0100 Subject: [PATCH 12/23] Correctly include cblas include directories RHEL uses a dedicated cblas include directory. Correctly include this for the one target PHY_UE that relies on this. Co-authored-by: Robert Schmidt --- CMakeLists.txt | 6 +++--- openair1/PHY/LTE_UE_TRANSPORT/linear_preprocessing_rec.c | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70ea409d36..c3d8624d86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1038,11 +1038,11 @@ pkg_check_modules(lapacke REQUIRED lapacke) add_library(PHY_UE ${PHY_SRC_UE}) target_link_libraries(PHY_UE PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs UTIL) -# RHEL needs also cblas, but Ubuntu does not have it. So `cblas_LIBRARIES` will -# be empty for Ubuntu, a no-op +# RHEL needs also cblas, but Ubuntu does not have it. So `cblas_LIBRARIES` and +# `cblas_INCLUDE_DIRS` will be empty for Ubuntu, a no-op pkg_check_modules(cblas cblas) target_link_libraries(PHY_UE PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES}) -target_include_directories(PHY_UE PRIVATE ${blas_INCLUDE_DIRS} ${lapacke_INCLUDE_DIRS}) +target_include_directories(PHY_UE PRIVATE ${blas_INCLUDE_DIRS} ${cblas_INCLUDE_DIRS} ${lapacke_INCLUDE_DIRS}) add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON}) target_link_libraries(PHY_NR_COMMON PUBLIC UTIL) diff --git a/openair1/PHY/LTE_UE_TRANSPORT/linear_preprocessing_rec.c b/openair1/PHY/LTE_UE_TRANSPORT/linear_preprocessing_rec.c index 998b95d0d3..86a4b93ddd 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/linear_preprocessing_rec.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/linear_preprocessing_rec.c @@ -12,11 +12,7 @@ data storage. */ #include #include #include -#if defined RHEL_RELEASE_CODE -#include -#else #include -#endif #include #include #include From 40ea8f75929cd32d62295f1454e7a0c2ca3d026d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 6 Jan 2026 08:14:21 +0100 Subject: [PATCH 13/23] Remove unused snap_environment.sh Signed-off-by: Robert Schmidt --- cmake_targets/snap_environment.sh | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 cmake_targets/snap_environment.sh diff --git a/cmake_targets/snap_environment.sh b/cmake_targets/snap_environment.sh deleted file mode 100755 index 1c0725d8b5..0000000000 --- a/cmake_targets/snap_environment.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -export UHD_IMAGES_DIR=$SNAP/uhd_images -exec $@ From d5cf363bfebc933f8f0dfcc02c51b9a3867fbe47 Mon Sep 17 00:00:00 2001 From: Rakesh BB Date: Tue, 6 Jan 2026 12:51:39 +0530 Subject: [PATCH 14/23] NR NGAP: Fix Time UE Stayed in Cell IE type and calculation According to 3GPP TS 38.413, the 'Time UE Stayed in Cell' IE is an integer ranging from 0 to 4095. This patch updates the data structure to reflect the correct bit width and implements the dynamic calculation based on the UE's residence time. - Change time_in_cell from long to uint16_t in ngap_messages_types.h. - Replace dummy value with real-time calculation in rrc_gNB_NGAP.c. - Implement a ceiling of 4095 to comply with the 12-bit protocol limit. Ref: 3GPP TS 38.413 Section 9.3.1.97 --- openair2/COMMON/ngap_messages_types.h | 2 +- openair2/RRC/NR/rrc_gNB_NGAP.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index ed93cdaadf..76abc3783d 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -474,7 +474,7 @@ typedef struct { // Cell type uint8_t type; // Time UE Stayed in Cell - long time_in_cell; + uint16_t time_in_cell; // Cause ngap_cause_t *cause; } last_visited_ngran_cell_info_t; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 5c212438e1..65991f714d 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1649,7 +1649,7 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, msg.source2target->ue_history_info.cause->type = NGAP_CAUSE_RADIO_NETWORK; msg.source2target->ue_history_info.cause->value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON; msg.source2target->ue_history_info.type = NGAP_CellSize_small; - msg.source2target->ue_history_info.time_in_cell = 500; // dummy number for now + msg.source2target->ue_history_info.time_in_cell = min(time(NULL) - UE->last_seen, 4095); msg.source2target->ue_history_info.id = target_cell; /* Fill both PDU Session Resource List IE (M) in Handover Required From 8176fd30638e07cfd56f1d27050836b41d62c2b9 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 6 Jan 2026 08:23:20 +0100 Subject: [PATCH 15/23] Remove old FlexRAN file Setup routes was added by the author of FlexRAN. The UEs always set up routes automatically, and this file is therefore not needed. See-also: 287c182925e6 ("Remove FlexRAN") Signed-off-by: Robert Schmidt --- cmake_targets/tools/make_agent_cache | 21 --------------------- cmake_targets/tools/setup_routes.sh | 13 ------------- 2 files changed, 34 deletions(-) delete mode 100755 cmake_targets/tools/make_agent_cache delete mode 100755 cmake_targets/tools/setup_routes.sh diff --git a/cmake_targets/tools/make_agent_cache b/cmake_targets/tools/make_agent_cache deleted file mode 100755 index d2eab20fc0..0000000000 --- a/cmake_targets/tools/make_agent_cache +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -CACHE_DIR=/mnt/oai_agent_cache - -if [ ! -d $CACHE_DIR ]; then - echo "Creating cache dir in $CACHE_DIR" - sudo mkdir $CACHE_DIR -fi - -if grep -qs "$CACHE_DIR" /proc/mounts; then - echo "Agent cache is already mounted" -else - echo "Agent cache was not mounted" - echo "Mounting..." - sudo mount -o size=100m -t tmpfs none "$CACHE_DIR" - if [ $? -eq 0 ]; then - echo "Mount success" - else - echo "Something went wrong with the mount" - fi -fi diff --git a/cmake_targets/tools/setup_routes.sh b/cmake_targets/tools/setup_routes.sh deleted file mode 100755 index 67fe250be7..0000000000 --- a/cmake_targets/tools/setup_routes.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -for i in $(seq 1 $1); -do - let table=1000+$i - echo "sudo ip route add 10.0.1.0/24 dev oaitun_ue$i table $table" - sudo ip route add 10.0.1.0/24 dev oaitun_ue$i table $table - echo "sudo ip route add default via 10.0.1.1 dev oaitun_ue$i table $table" - sudo ip route add default via 10.0.1.1 dev oaitun_ue$i table $table - let octet=$i+1 - echo "sudo ip rule add from 10.0.1.$octet table $table" - sudo ip rule add from 10.0.1.$octet table $table -done From ca707b398752a57e7c764dd48f0fbf3b35a28d4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 6 Jan 2026 08:26:45 +0100 Subject: [PATCH 16/23] Remove old MME/eNB instructions There is no HSS/MME in this repository, which is the majority of this file. Remove it, since it is outdated. Signed-off-by: Robert Schmidt --- .../example_oaisim_enb_ue_mme_virtual.txt | 98 ------------------- 1 file changed, 98 deletions(-) delete mode 100755 cmake_targets/tools/example_oaisim_enb_ue_mme_virtual.txt diff --git a/cmake_targets/tools/example_oaisim_enb_ue_mme_virtual.txt b/cmake_targets/tools/example_oaisim_enb_ue_mme_virtual.txt deleted file mode 100755 index 805d121e6d..0000000000 --- a/cmake_targets/tools/example_oaisim_enb_ue_mme_virtual.txt +++ /dev/null @@ -1,98 +0,0 @@ -#/* -# * 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 example_oaisim_enb_ue_mme_virtual.txt -# brief -# author Lionel Gauthier - -TODO: a document file, PDF for example, with nice pictures, etc -This example shows how to generate and launch enb, 1 ue (NAS still not virtualized in UE) in virtualised mode, mme_gw, hss executables on the same host: - -################ -Configuration -################ -freediameter 1.2.0 (not 1.1.5, 1.1.6,...) has to be installed and patched (done by check_install_oai_software() calling install_freediameter_from_source() in script build_helper) - -Config File: -/etc/hosts must contain the fqdn of the MME and HSS (same since both run on the same host): -(assuming the realm is openair4G.eur) -127.0.0.1 localhost -127.0.1.1 yang.openair4G.eur yang -... - -################ -BUILDING, RUNNING -################ - -Building HSS: -yang@yang:~/openair4G/trunk/cmake_targets$ tools/build_hss -c --connect-to-mme yang.openair4G.eur -t -T --realm openair4G.eur - -Configuring the HSS: -In oai_db (easier for newbies to access HSS database through http://127.0.0.1/phpmyadmin) - In table mmeidentity, enter the record corresponding to your MME: - (idmmeidentity, mmehost, mmerealm) - In table pdn, enter the record allowing an IMSI to connect to an APN - In table users, enter the record corresponding to your USIM card. - -Running the HSS: -yang@yang:~/openair4G/trunk/cmake_targets$ sudo tools/run_hss -g - - - -Building the MME-GW: -yang@yang:~/openair4G/trunk/cmake_targets/tools$ ./build_epc -c -d -t -T --s6a-server - -Configuring the MME-GW: - You can configure in file openair4G/trunk/cmake_targets/tools/epc.local.enb.conf.in the following parameters: - MCC/MNC of your network - SGI interface (outgoing interface of the S/P-GW) - Integrity algorithms, Ciphering algorithms - Please note that DNS addresses are hardcoded in source code (TODO in list). - -Running the MME-GW: -yang@yang:~/openair4G/trunk/cmake_targets/tools$ sudo ./run_epc -l -g -K - - - -Building eNB: -yang@yang:~/openair4G/trunk/cmake_targets$ ./build_oai --oaisim - -Configuring the eNB: - The default configuration file for the eNB is trunk/targets/PROJECTS/GENERIC-LTE-EPC/CONF/enb.band7.generic.oaisim.local_mme.conf - -Running eNB: -yang@yang:~/openair4G/trunk/cmake_targets$ tools/run_enb_ue_virt_s1 -m /tmp -K /tmp/enb_ue_virt_s1_itti.log - - -Putting some IP UL/DL traffic in the bearer: -ping -s 1000 -m 1 192.168.12.100 -I oip1 - - -################ -Tools available: -############### - -1) itti_analyser (list messages exchanged between protocol layers) -2) xforms (Layer 1) -3) vcd (timing mesurements) - - - From b1fa6e6cdfe5cdef22e15f831c2d3fea9425745d Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 6 Jan 2026 08:32:39 +0100 Subject: [PATCH 17/23] Remove unused CMake helper files These files are not used anywhere. The only remaining cmake find files are: - findarmral and findxran: used by FHI7.2 driver - findsctp: used by top-level CMakeLists.txt - findgnutls: NOT actually used but GnuTLS is a dependency of websrv, so I leave it for the moment (should be removed if the webserver is removed) Signed-off-by: Robert Schmidt --- cmake_targets/tools/Kbuild.cmake | 4 - .../tools/MODULES/CMakeParseArguments.cmake | 66 - .../tools/MODULES/CMakeUserFindMySQL.cmake | 48 - .../tools/MODULES/CMakeUserUseBison.cmake | 71 -- .../tools/MODULES/CMakeUserUseFlex.cmake | 46 - cmake_targets/tools/MODULES/FindGCCXML.cmake | 8 - cmake_targets/tools/MODULES/FindKbuild.cmake | 105 -- cmake_targets/tools/MODULES/Kbuild.cmake | 4 - .../tools/MODULES/cmake_useful.cmake | 229 ---- .../tools/MODULES/kbuild_system.cmake | 1131 ----------------- cmake_targets/tools/MODULES/kmodule.cmake | 539 -------- 11 files changed, 2251 deletions(-) delete mode 100755 cmake_targets/tools/Kbuild.cmake delete mode 100755 cmake_targets/tools/MODULES/CMakeParseArguments.cmake delete mode 100755 cmake_targets/tools/MODULES/CMakeUserFindMySQL.cmake delete mode 100755 cmake_targets/tools/MODULES/CMakeUserUseBison.cmake delete mode 100755 cmake_targets/tools/MODULES/CMakeUserUseFlex.cmake delete mode 100755 cmake_targets/tools/MODULES/FindGCCXML.cmake delete mode 100755 cmake_targets/tools/MODULES/FindKbuild.cmake delete mode 100755 cmake_targets/tools/MODULES/Kbuild.cmake delete mode 100755 cmake_targets/tools/MODULES/cmake_useful.cmake delete mode 100755 cmake_targets/tools/MODULES/kbuild_system.cmake delete mode 100755 cmake_targets/tools/MODULES/kmodule.cmake diff --git a/cmake_targets/tools/Kbuild.cmake b/cmake_targets/tools/Kbuild.cmake deleted file mode 100755 index 7403cd04df..0000000000 --- a/cmake_targets/tools/Kbuild.cmake +++ /dev/null @@ -1,4 +0,0 @@ -src=${dir} -obj-m += ${name}.o -${name}-y += ${objs} -ccflags-y += ${module_cc_opt} diff --git a/cmake_targets/tools/MODULES/CMakeParseArguments.cmake b/cmake_targets/tools/MODULES/CMakeParseArguments.cmake deleted file mode 100755 index ab53b112fa..0000000000 --- a/cmake_targets/tools/MODULES/CMakeParseArguments.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Analogue for standard CMake module with same name, appeared in CMake 2.8.3. -# -# Used for compatibility with older versions of cmake. - -function(cmake_parse_arguments prefix options one_value_keywords multi_value_keywords) - set(all_keywords ${options} ${one_value_keywords} ${multi_value_keywords}) - # Clear variables before parsing. - foreach(arg ${one_value_keywords} ${multie_value_keywords}) - set(cpa_${arg}) - endforeach() - - foreach(arg ${options}) - set(cpa_${arg} FALSE) - endforeach() - - set(cpa_UNPARSED_ARGUMENTS) - - set(current_keyword) - # Classification for current_keyword: - # 'ONE' or 'MULTY'. - set(current_keyword_type) - - # now iterate over all arguments and fill the result variables - foreach(arg ${ARGN}) - list(FIND all_keywords ${arg} keyword_index) - - if(keyword_index EQUAL -1) - if(current_keyword) - if(current_keyword_type STREQUAL "ONE") - set(cpa_${current_keyword} ${arg}) - set(current_keyword) - else(current_keyword_type STREQUAL "ONE") - list(APPEND cpa_${current_keyword} ${arg}) - endif(current_keyword_type STREQUAL "ONE") - else(current_keyword) - list(APPEND cpa_UNPARSED_ARGUMENTS ${arg}) - endif(current_keyword) - else(keyword_index EQUAL -1) - if(current_keyword AND current_keyword_type STREQUAL "ONE") - message(SEND_ERROR "Value is expected for one-value-keyword ${current_keyword}") - endif(current_keyword AND current_keyword_type STREQUAL "ONE") - list(FIND options ${arg} option_index) - if(option_index EQUAL -1) - set(current_keyword ${arg}) - list(FIND one_value_keywords ${arg} one_value_index) - if(one_value_index EQUAL -1) - set(current_keyword_type "MULTI") - else(one_value_index EQUAL -1) - set(current_keyword_type "ONE") - endif(one_value_index EQUAL -1) - else(option_index EQUAL -1) - set(current_keyword) - set(cpa_${arg} TRUE) - endif(option_index EQUAL -1) - endif(keyword_index EQUAL -1) - endforeach(arg ${ARGN}) - - if(current_keyword AND current_keyword_type STREQUAL "ONE") - message(SEND_ERROR "Value is expected for one-value-keyword ${current_keyword}") - endif(current_keyword AND current_keyword_type STREQUAL "ONE") - - # propagate the result variables to the caller: - foreach(keyword ${all_keywords} UNPARSED_ARGUMENTS) - set(${prefix}_${keyword} ${cpa_${keyword}} PARENT_SCOPE) - endforeach(keyword ${all_keywords}) -endfunction(cmake_parse_arguments prefix options one_value_keywords multi_value_keywords) diff --git a/cmake_targets/tools/MODULES/CMakeUserFindMySQL.cmake b/cmake_targets/tools/MODULES/CMakeUserFindMySQL.cmake deleted file mode 100755 index 1b5de7e3ac..0000000000 --- a/cmake_targets/tools/MODULES/CMakeUserFindMySQL.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# - Find mysqlclient -# Find the native MySQL includes and library -# -# MYSQL_INCLUDE_DIR - where to find mysql.h, etc. -# MYSQL_LIBRARIES - List of libraries when using MySQL. -# MYSQL_FOUND - True if MySQL found. - -IF (MYSQL_INCLUDE_DIR) - # Already in cache, be silent - SET(MYSQL_FIND_QUIETLY TRUE) -ENDIF (MYSQL_INCLUDE_DIR) - -FIND_PATH(MYSQL_INCLUDE_DIR mysql.h - /usr/local/include/mysql - /usr/include/mysql -) - -SET(MYSQL_NAMES mysqlclient mysqlclient_r) -FIND_LIBRARY(MYSQL_LIBRARY - NAMES ${MYSQL_NAMES} - PATHS /usr/lib /usr/local/lib - PATH_SUFFIXES mysql -) - -IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - SET(MYSQL_FOUND TRUE) - SET( MYSQL_LIBRARIES ${MYSQL_LIBRARY} ) -ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - SET(MYSQL_FOUND FALSE) - SET( MYSQL_LIBRARIES ) -ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - -IF (MYSQL_FOUND) - IF (NOT MYSQL_FIND_QUIETLY) - MESSAGE(STATUS "Found MySQL: ${MYSQL_LIBRARY}") - ENDIF (NOT MYSQL_FIND_QUIETLY) -ELSE (MYSQL_FOUND) - IF (MYSQL_FIND_REQUIRED) - MESSAGE(STATUS "Looked for MySQL libraries named ${MYSQL_NAMES}.") - MESSAGE(FATAL_ERROR "Could NOT find MySQL library") - ENDIF (MYSQL_FIND_REQUIRED) -ENDIF (MYSQL_FOUND) - -MARK_AS_ADVANCED( - MYSQL_LIBRARY - MYSQL_INCLUDE_DIR - ) - diff --git a/cmake_targets/tools/MODULES/CMakeUserUseBison.cmake b/cmake_targets/tools/MODULES/CMakeUserUseBison.cmake deleted file mode 100755 index 5295a1ba39..0000000000 --- a/cmake_targets/tools/MODULES/CMakeUserUseBison.cmake +++ /dev/null @@ -1,71 +0,0 @@ -# - Look for GNU Bison, the parser generator -# Based off a news post from Andy Cedilnik at Kitware -# Defines the following: -# BISON_EXECUTABLE - path to the bison executable -# BISON_FILE - parse a file with bison -# BISON_PREFIX_OUTPUTS - Set to true to make BISON_FILE produce prefixed -# symbols in the generated output based on filename. -# So for ${filename}.y, you'll get ${filename}parse(), etc. -# instead of yyparse(). -# BISON_GENERATE_DEFINES - Set to true to make BISON_FILE output the matching -# .h file for a .c file. You want this if you're using -# flex. - -IF(NOT DEFINED BISON_PREFIX_OUTPUTS) - SET(BISON_PREFIX_OUTPUTS FALSE) -ENDIF(NOT DEFINED BISON_PREFIX_OUTPUTS) - -IF(NOT DEFINED BISON_GENERATE_DEFINES) - SET(BISON_GENERATE_DEFINES FALSE) -ENDIF(NOT DEFINED BISON_GENERATE_DEFINES) - -IF(NOT BISON_EXECUTABLE) - MESSAGE(STATUS "Looking for bison") - FIND_PROGRAM(BISON_EXECUTABLE bison) - IF(BISON_EXECUTABLE) - MESSAGE(STATUS "Looking for bison -- ${BISON_EXECUTABLE}") - ENDIF(BISON_EXECUTABLE) -ENDIF(NOT BISON_EXECUTABLE) - -IF(BISON_EXECUTABLE) - MACRO(BISON_FILE FILENAME) - GET_FILENAME_COMPONENT(PATH "${FILENAME}" PATH) - IF("${PATH}" STREQUAL "") - SET(PATH_OPT "") - ELSE("${PATH}" STREQUAL "") - SET(PATH_OPT "/${PATH}") - ENDIF("${PATH}" STREQUAL "") - GET_FILENAME_COMPONENT(HEAD "${FILENAME}" NAME_WE) - IF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - ENDIF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - IF(BISON_PREFIX_OUTPUTS) - SET(PREFIX "${HEAD}") - ELSE(BISON_PREFIX_OUTPUTS) - SET(PREFIX "yy") - ENDIF(BISON_PREFIX_OUTPUTS) - SET(OUTFILE "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/${HEAD}.tab.c") - IF(BISON_GENERATE_DEFINES) - SET(HEADER "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/${HEAD}.tab.h") - ADD_CUSTOM_COMMAND( - OUTPUT "${OUTFILE}" "${HEADER}" - COMMAND "${BISON_EXECUTABLE}" - ARGS "--name-prefix=${PREFIX}" - "--defines" - "--output-file=${OUTFILE}" - "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") - SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" "${HEADER}" PROPERTIES GENERATED TRUE) - SET_SOURCE_FILES_PROPERTIES("${HEADER}" PROPERTIES HEADER_FILE_ONLY TRUE) - ELSE(BISON_GENERATE_DEFINES) - ADD_CUSTOM_COMMAND( - OUTPUT "${OUTFILE}" - COMMAND "${BISON_EXECUTABLE}" - ARGS "--name-prefix=${PREFIX}" - "--output-file=${OUTFILE}" - "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") - SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" PROPERTIES GENERATED TRUE) - ENDIF(BISON_GENERATE_DEFINES) - ENDMACRO(BISON_FILE) -ENDIF(BISON_EXECUTABLE) diff --git a/cmake_targets/tools/MODULES/CMakeUserUseFlex.cmake b/cmake_targets/tools/MODULES/CMakeUserUseFlex.cmake deleted file mode 100755 index 09a3e71e6d..0000000000 --- a/cmake_targets/tools/MODULES/CMakeUserUseFlex.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# - Look for GNU flex, the lexer generator. -# Defines the following: -# FLEX_EXECUTABLE - path to the flex executable -# FLEX_FILE - parse a file with flex -# FLEX_PREFIX_OUTPUTS - Set to true to make FLEX_FILE produce outputs of -# lex.${filename}.c, not lex.yy.c . Passes -P to flex. - -IF(NOT DEFINED FLEX_PREFIX_OUTPUTS) - SET(FLEX_PREFIX_OUTPUTS FALSE) -ENDIF(NOT DEFINED FLEX_PREFIX_OUTPUTS) - -IF(NOT FLEX_EXECUTABLE) - MESSAGE(STATUS "Looking for flex") - FIND_PROGRAM(FLEX_EXECUTABLE flex) - IF(FLEX_EXECUTABLE) - MESSAGE(STATUS "Looking for flex -- ${FLEX_EXECUTABLE}") - ENDIF(FLEX_EXECUTABLE) -ENDIF(NOT FLEX_EXECUTABLE) - -IF(FLEX_EXECUTABLE) - MACRO(FLEX_FILE FILENAME) - GET_FILENAME_COMPONENT(PATH "${FILENAME}" PATH) - IF("${PATH}" STREQUAL "") - SET(PATH_OPT "") - ELSE("${PATH}" STREQUAL "") - SET(PATH_OPT "/${PATH}") - ENDIF("${PATH}" STREQUAL "") - IF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - ENDIF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") - IF(FLEX_PREFIX_OUTPUTS) - GET_FILENAME_COMPONENT(PREFIX "${FILENAME}" NAME_WE) - ELSE(FLEX_PREFIX_OUTPUTS) - SET(PREFIX "yy") - ENDIF(FLEX_PREFIX_OUTPUTS) - SET(OUTFILE "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/lex.${PREFIX}.c") - ADD_CUSTOM_COMMAND( - OUTPUT "${OUTFILE}" - COMMAND "${FLEX_EXECUTABLE}" - ARGS "--prefix=${PREFIX}" - "--outfile=${OUTFILE}" - "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") - SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" PROPERTIES GENERATED TRUE) - ENDMACRO(FLEX_FILE) -ENDIF(FLEX_EXECUTABLE) diff --git a/cmake_targets/tools/MODULES/FindGCCXML.cmake b/cmake_targets/tools/MODULES/FindGCCXML.cmake deleted file mode 100755 index 6a4daa084d..0000000000 --- a/cmake_targets/tools/MODULES/FindGCCXML.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# FindGCCXML -# ---------- -find_program(GCCXML -NAMES gccxml -PATHS "/usr/bin" -"usr/local/bin" -) -mark_as_advanced(GCCXML) diff --git a/cmake_targets/tools/MODULES/FindKbuild.cmake b/cmake_targets/tools/MODULES/FindKbuild.cmake deleted file mode 100755 index 08accb8551..0000000000 --- a/cmake_targets/tools/MODULES/FindKbuild.cmake +++ /dev/null @@ -1,105 +0,0 @@ -# - Support for building kernel modules - -# This module defines several variables which may be used when build -# kernel modules. -# -# The following variables are set here: -# Kbuild_VERSION_STRING - kernel version. -# Kbuild_ARCH - architecture. -# Kbuild_BUILD_DIR - directory for build kernel and/or its components. -# Kbuild_VERSION_{MAJOR|MINOR|TWEAK} - kernel version components -# Kbuild_VERSION_STRING_CLASSIC - classic representation of version, -# where parts are delimited by dots. -# -# Cache variables which affect on this package: -# CMAKE_SYSTEM_VERSION - change Kbuild_VERSION_STRING -# Also, setting CMAKE_SYSTEM_VERSION will be interpreted by cmake as crosscompile. -# -# Cache variables(ADVANCED) which affect on this package: -# ARCH - if not empty, change Kbuild_ARCH -# KBUILD_DIR - change Kbuild_BUILD_DIR - -set(Kbuild_VERSION_STRING ${CMAKE_SYSTEM_VERSION}) - -# Form classic version view. -string(REGEX MATCH "([0-9]+)\\.([0-9]+)[.-]([0-9]+)" - _kernel_version_match - "${Kbuild_VERSION_STRING}" -) - -if(NOT _kernel_version_match) - message(FATAL_ERROR "Kernel version has unexpected format: ${Kbuild_VERSION_STRING}") -endif(NOT _kernel_version_match) - -set(Kbuild_VERSION_MAJOR ${CMAKE_MATCH_1}) -set(Kbuild_VERSION_MINOR ${CMAKE_MATCH_2}) -set(Kbuild_VERSION_TWEAK ${CMAKE_MATCH_3}) -# Version string for compare -set(Kbuild_VERSION_STRING_CLASSIC "${Kbuild_VERSION_MAJOR}.${Kbuild_VERSION_MINOR}.${Kbuild_VERSION_TWEAK}") - -set(KBUILD_DIR "/lib/modules/${Kbuild_VERSION_STRING}/build" CACHE PATH - "Directory for build linux kernel and/or its components." -) -mark_as_advanced(KBUILD_DIR) - -set(Kbuild_BUILD_DIR "${KBUILD_DIR}") - -set(ARCH "" CACHE STRING - "Architecture for build linux kernel components for, empty string means autodetect." -) -mark_as_advanced(ARCH) - -if(NOT ARCH) - # Autodetect arch. - execute_process(COMMAND uname -m - RESULT_VARIABLE uname_m_result - OUTPUT_VARIABLE ARCH_DEFAULT - ) - if(NOT uname_m_result EQUAL 0) - message("'uname -m' failed:") - message("${ARCH_DEFAULT}") - message(FATAL_ERROR "Failed to determine system architecture.") - endif(NOT uname_m_result EQUAL 0) - - string(REGEX REPLACE "\n$" "" ARCH_DEFAULT "${ARCH_DEFAULT}") - - # Pairs of pattern-replace for postprocess architecture string from - # 'uname -m'. - # Taken from ./Makefile of the kernel build. - set(_kbuild_arch_replacers - "i.86" "x86" - "x86_64" "x86" - "sun4u" "sparc64" - "arm.*" "arm" - "sa110" "arm" - "s390x" "s390" - "parisc64" "parisc" - "ppc.*" "powerpc" - "mips.*" "mips" - "sh[234].*" "sh" - "aarch64.*" "arm64" - ) - - set(_current_pattern) - foreach(p ${_kbuild_arch_replacers}) - if(_current_pattern) - string(REGEX REPLACE "${_current_pattern}" "${p}" ARCH_DEFAULT "${ARCH_DEFAULT}") - set(_current_pattern) - else(_current_pattern) - set(_current_pattern "${p}") - endif(_current_pattern) - endforeach(p ${_kbuild_arch_replacers}) - - set(Kbuild_ARCH "${ARCH_DEFAULT}") -else(NOT ARCH) - # User-provided value is used. - set(Kbuild_ARCH "${ARCH}") -endif(NOT ARCH) - - - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Kbuild - REQUIRED_VARS Kbuild_BUILD_DIR - VERSION_VAR KBuild_VERSION_STRING_CLASSIC -) diff --git a/cmake_targets/tools/MODULES/Kbuild.cmake b/cmake_targets/tools/MODULES/Kbuild.cmake deleted file mode 100755 index 7403cd04df..0000000000 --- a/cmake_targets/tools/MODULES/Kbuild.cmake +++ /dev/null @@ -1,4 +0,0 @@ -src=${dir} -obj-m += ${name}.o -${name}-y += ${objs} -ccflags-y += ${module_cc_opt} diff --git a/cmake_targets/tools/MODULES/cmake_useful.cmake b/cmake_targets/tools/MODULES/cmake_useful.cmake deleted file mode 100755 index af6bbb56c7..0000000000 --- a/cmake_targets/tools/MODULES/cmake_useful.cmake +++ /dev/null @@ -1,229 +0,0 @@ -# Create rule for obtain one file by copying another one -function(rule_copy_file target_file source_file) - add_custom_command(OUTPUT ${target_file} - COMMAND cp -p ${source_file} ${target_file} - DEPENDS ${source_file} - ) -endfunction(rule_copy_file target_file source_file) - -# rule_copy_source([source_dir] file ...) -# -# Create rule for obtain file(s) in binary tree by copiing it from source tree. -# -# Files are given relative to ${source_dir}, if it is set, or -# relative to ${CMAKE_CURRENT_SOURCE_DIR}. -# -# Files will be copied into ${CMAKE_CURRENT_BINARY_DIR} with same -# relative paths. -# -# ${source_dir} should be absolute path(that is, starts from '/'). -# Otherwise first argument is treated as first file to copy. -function(rule_copy_source file) - string(REGEX MATCH "^/" is_abs_path ${file}) - if(is_abs_path) - set(source_dir ${file}) - set(files ${ARGN}) - else(is_abs_path) - set(source_dir ${CMAKE_CURRENT_SOURCE_DIR}) - set(files ${file} ${ARGN}) - endif(is_abs_path) - - foreach(file_real ${files}) - rule_copy_file("${CMAKE_CURRENT_BINARY_DIR}/${file_real}" - ${source_dir}/${file_real}) - endforeach(file_real ${files}) -endfunction(rule_copy_source file) - -# to_abs_path(output_var path [...]) -# -# Convert relative path of file to absolute path: -# use path in source tree, if file already exist there. -# otherwise use path in binary tree. -# If initial path already absolute, return it. -function(to_abs_path output_var) - set(result) - foreach(path ${ARGN}) - string(REGEX MATCH "^/" _is_abs_path ${path}) - if(_is_abs_path) - list(APPEND result ${path}) - else(_is_abs_path) - file(GLOB to_abs_path_file - "${CMAKE_CURRENT_SOURCE_DIR}/${path}" - ) - if(NOT to_abs_path_file) - set (to_abs_path_file "${CMAKE_CURRENT_BINARY_DIR}/${path}") - endif(NOT to_abs_path_file) - list(APPEND result ${to_abs_path_file}) - endif(_is_abs_path) - endforeach(path ${ARGN}) - set("${output_var}" ${result} PARENT_SCOPE) -endfunction(to_abs_path output_var path) - -# is_path_inside_dir(output_var dir path) -# -# Set output_var to true if path is absolute path inside given directory. -# NOTE: Path should be absolute. -macro(is_path_inside_dir output_var dir path) - file(RELATIVE_PATH _rel_path ${dir} ${path}) - string(REGEX MATCH "^\\.\\." _is_not_inside_dir ${_rel_path}) - if(_is_not_inside_dir) - set(${output_var} "FALSE") - else(_is_not_inside_dir) - set(${output_var} "TRUE") - endif(_is_not_inside_dir) -endmacro(is_path_inside_dir output_var dir path) - - -# Write given content to the file. -# -# If file is already exists and its content is same as written one, file -# is not rewritten, so its write timestamp remains unchanged. -function(file_update filename content) - if(EXISTS "${filename}") - file(READ "${filename}" old_content) - if(old_content STREQUAL "${content}") - return() - endif(old_content STREQUAL "${content}") - endif(EXISTS "${filename}") - # File doesn't exists or its content differ. - file(WRITE "${filename}" "${content}") -endfunction(file_update filename content) - - -# Write given content to the file in APPEND mode. -# -# For append we use position variable which should be initialized -# to 0 for every new build process and which is updated every time when -# this function is called. -# If file is already exists and its content at given is same as -# written one, file is not rewritten, so its write timestamp remains unchanged. -# -# Note, that file will not be rewritten if new build process issues less -# APPEND actions than one which create file. -# But similar problem exists for file_update() and even for built-in -# configure_file() command: file will not be removed if new build process -# do not call configure_file() for it. -function(file_update_append filename content POS) - set(pos "${${POS}}") - string(LENGTH "${content}" len) - # Update output variable first. - # This allows to use return() when need not to do anything - math(EXPR pos_new "${pos}+${len}") - set(${POS} "${pos_new}" PARENT_SCOPE) - if(EXISTS "${filename}") - file(READ "${filename}" old_content LIMIT "${len}" OFFSET "${pos}") - if(old_content STREQUAL "${content}") - return() - elseif(old_content STREQUAL "") - file(APPEND "${filename}" "${content}") - return() - endif(old_content STREQUAL "${content}") - else(EXISTS "${filename}") - if(NOT pos EQUAL 0) - message(FATAL_ERROR "Appending to non-zero position to non-existent file.") - endif(NOT pos EQUAL 0) - endif(EXISTS "${filename}") - # File doesn't exists or its content differ. - if(NOT pos EQUAL 0) - file(READ "${filename}" prefix LIMIT "${pos}") - else(NOT pos EQUAL 0) - set(prefix) - endif(NOT pos EQUAL 0) - file(WRITE "${filename}" "${prefix}${content}") -endfunction(file_update_append filename content POS) - - -# Common mechanism for output status message -# when checking different aspects. -# -# Normal using: -# -# check_begin("Checking <...>") -# if(NOT ) -# check_try() # Here message is printed -# # Perform check(try_compile(), etc.) -# endif(NOT ) -# check_end("") # Here message is printed with result. - -# Should be called (unconditionally) when new cheking is issued. -# Note, that given @status_msg is not printed at that step. -function(check_begin status_msg) - set(_check_status_msg ${status_msg} PARENT_SCOPE) - set(_check_has_tries PARENT_SCOPE) -endfunction(check_begin status_msg) - -# Should be called before every real checking, that is which is not come -# from cache variables comparision. -# -# First call of that function is trigger printing of @status_msg, -# passed to check_begin(). -function(check_try) - if(NOT _check_has_tries) - message(STATUS "${_check_status_msg}") - set(_check_has_tries "1" PARENT_SCOPE) - endif(NOT _check_has_tries) -endfunction(check_try) - -# Should be called when cheking is end, and @result_msg should be short -# description of check result. -# If any check_try() has been issued before, -# "@status_msg - @result_msg" -# will be printed. -# Otherwise, -# "@status_msg - [cached] @result_msg" -# will be printed, at it will be the only message for that check. -function(check_end result_msg) - if(NOT _check_has_tries) - message(STATUS "${_check_status_msg} [cached] - ${result_msg}") - else(NOT _check_has_tries) - message(STATUS "${_check_status_msg} - ${result_msg}") - endif(NOT _check_has_tries) - set(_check_status_msg PARENT_SCOPE) - set(_check_has_tries PARENT_SCOPE) -endfunction(check_end result_msg) - -# set_bool_string(var true_string false_string value [CACHE ... | PARENT_SCOPE]) -# -# Set variable to one of two string according to true property of some value. -# -# If 'value' is true-evaluated, 'var' will be set to 'true_string', -# otherwise to 'var' will be set to 'false_string'. -# Like standard set(), macro accept CACHE and PARENT_SCOPE modifiers. -# -# Useful for form meaningful value for cache variables, contained result -# of some operation. -# Also may be used for form message for check_end(). -macro(set_bool_string var true_string false_string value) - # Macro parameters are not a variables, so them cannot be tested - # using 'if(value)'. - # Usage 'if(${value})' leads to warnings since 2.6.4 when ${val} - # is boolean constant - # (because until 2.6.4 'if(value)' always dereference val). - # So copy 'value' to local variable for test it. - set(_set_bool_string_value ${value}) - if(_set_bool_string_value) - set(${var} "${true_string}" ${ARGN}) - else() - set(${var} "${false_string}" ${ARGN}) - endif() -endmacro(set_bool_string) - -# set_zero_string(var true_string false_string value [CACHE ... | PARENT_SCOPE]) -# -# Set variable to one of two string according to zero property of some value. -# -# If 'value' is '0' (presizely), 'var' will be set to 'zero_string', -# otherwise to 'var' will be set to 'nonzero_string'. -# Like standard set(), macro accept CACHE and PARENT_SCOPE modifiers. -# -# Useful for form meaningful value for cache variables, contained result -# of execute_process(). -# Also may be used for form message for check_end(). -macro(set_zero_string var zero_string nonzero_string value) - set(_set_zero_string_value ${value}) - if(_set_zero_string_value EQUAL "0") - set(${var} ${zero_string} ${ARGN}) - else() - set(${var} ${nonzero_string} ${ARGN}) - endif() -endmacro(set_zero_string) \ No newline at end of file diff --git a/cmake_targets/tools/MODULES/kbuild_system.cmake b/cmake_targets/tools/MODULES/kbuild_system.cmake deleted file mode 100755 index 42a1a2d42b..0000000000 --- a/cmake_targets/tools/MODULES/kbuild_system.cmake +++ /dev/null @@ -1,1131 +0,0 @@ -# Provide way for create kernel modules, and perform other actions on them -# in a way similar to standard cmake executables and libraries processing. -# -# NB: At the end of the whole configuration stage -# kbuild_finalize_linking() -# should be executed. -# -# Should be included after: -# find_package(Kbuild) -# -# Cached variables(ADVANCED), which affects on definitions below: -# CROSS_COMPILE - corresponded parameter for 'make' to build kernel objects. -# KBUILD_C_FLAGS - Additional kbuild flags for all builds -# KBUILD_C_FLAGS{DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL} - per-configuration flags. - -include(cmake_useful) - -# Compute path to the auxiliary files. -get_filename_component(kbuild_this_module_dir ${CMAKE_CURRENT_LIST_FILE} PATH) -set(kbuild_aux_dir "${kbuild_this_module_dir}/kbuild_system_files") - -# _declare_per_build_vars( ) -# -# [INTERNAL] Per-build type definitions for given . -# -# Create CACHE STRING variables _{DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL} -# with documentation string where %build% is replaced -# with corresponded build type description. -# -# Default value for variable _ is taken from __INIT. -macro(_declare_per_build_vars variable doc_pattern) - set(_build_type) - foreach(t - RELEASE "release" - DEBUG "debug" - RELWITHDEBINFO "Release With Debug Info" - MINSIZEREL "release minsize" - ) - if(_build_type) - string(REPLACE "%build%" "${t}" _docstring ${doc_pattern}) - set("${variable}_${_build_type}" "${${variable}_${_build_type}_INIT}" - CACHE STRING "${_docstring}") - set(_build_type) - else(_build_type) - set(_build_type "${t}") - endif(_build_type) - endforeach(t) -endmacro(_declare_per_build_vars variable doc_pattern) - - -# Default compiler flags. -# -# These flags are used directly when configuring 'Kbuild', so them should be -# expressed as single-string, where different flags are joinged using ' '. -set(KBUILD_C_FLAGS "" CACHE STRING - "Compiler flags used by Kbuild system during all builds." -) - -# Additional default compiler flags per build type. -set(KBUILD_C_FLAGS_DEBUG_INIT "-g") -set(KBUILD_C_FLAGS_RELWITHDEBINFO_INIT "-g") - -_declare_per_build_vars(KBUILD_C_FLAGS - "Compiler flags used by Kbuild system during %build% builds." -) -mark_as_advanced( - KBUILD_C_FLAGS - KBUILD_C_FLAGS_DEBUG - KBUILD_C_FLAGS_RELEASE - KBUILD_C_FLAGS_RELWITHDEBINFO - KBUILD_C_FLAGS_MINSIZEREL -) - - -# Additional definitions which are passed directly to 'make' for -# kbuild process. -# -# Unlike to compiler flags, these ones are expressed using cmake list. -set(KBUILD_MAKE_FLAGS "" CACHE STRING - "Make flags used by Kbuild system during all builds." -) - -_declare_per_build_vars(KBUILD_MAKE_FLAGS - "Make flags used by Kbuild system during %build% builds." -) - -mark_as_advanced( - KBUILD_MAKE_FLAGS - KBUILD_MAKE_FLAGS_DEBUG - KBUILD_MAKE_FLAGS_RELEASE - KBUILD_MAKE_FLAGS_RELWITHDEBINFO - KBUILD_MAKE_FLAGS_MINSIZEREL -) - -set(CROSS_COMPILE "" CACHE STRING "Cross compilation prefix for build linux kernel objects.") -mark_as_advanced(CROSS_COMPILE) - -# Property prefixed with 'KMODULE_' is readonly for outer use, -# unless explicitely noted in its description. - -# Property is set for every target described kernel module. -# Concrete property's value currently has no special meaning. -define_property(TARGET PROPERTY KMODULE_TYPE - BRIEF_DOCS "Whether given target describes kernel module." - FULL_DOCS "Whether given target describes kernel module." -) - -# Absolute filename of .ko file which is built. -# NOTE: Imported targets use -# KMODULE_IMPORTED_MODULE_LOCATION -# property instead. -define_property(TARGET PROPERTY KMODULE_MODULE_LOCATION - BRIEF_DOCS "Location of the built kernel module." - FULL_DOCS "Location of the built kernel module." -) - -# Name of the given module, like "ext4" or "kedr". -define_property(TARGET PROPERTY KMODULE_MODULE_NAME - BRIEF_DOCS "Name of the kernel module." - FULL_DOCS "Name of the kernel module." -) - - -# Absolute filename of Module.symvers file which is built. -# NOTE: Imported targets use -# KMODULE_IMPORTED_SYMVERS_LOCATION -# property instead. -define_property(TARGET PROPERTY KMODULE_SYMVERS_LOCATION - BRIEF_DOCS "Location of the symvers file of the kernel module." - FULL_DOCS "Location of the symvers file of the kernel module." -) - -# Property is "TRUE" for every target described imported kernel module, -# "FALSE" for other targets described kernel module. -define_property(TARGET PROPERTY KMODULE_IMPORTED - BRIEF_DOCS "Whether given target describes imported kernel module." - FULL_DOCS "Whether given target describes imported kernel module." -) - -# Absolute filename of .ko file which is imported. -# Property may be set after -# kbuild_add_module(... IMPORTED) -# for allow loading of given module or perform other actions required -# kernel module itself. -# -# Analogue for KMODULE_MODULE_LOCATION property of normal(non-exported) -# kernel module target. -define_property(TARGET PROPERTY KMODULE_IMPORTED_MODULE_LOCATION - BRIEF_DOCS "Location of the imported kernel module." - FULL_DOCS "Location of the imported kernel module." -) - -# Absolute filename of Module.symvers file for imported kernel module. -# Property may be set after -# kbuild_add_module(... IMPORTED) -# for allow linking with given module or perform other actions required -# its symbols. -# -# Analogue for KMODULE_MODULE_SYMVERS_LOCATION property of normal(non-exported) -# kernel module target. -define_property(TARGET PROPERTY KMODULE_IMPORTED_SYMVERS_LOCATION - BRIEF_DOCS "Location of the symvers file for imported kernel module." - FULL_DOCS "Location of the symvers file for imported kernel module." -) - -# Helpers for simple extract some kernel module properties. - -# kbuild_get_module_location(RESULT_VAR name) -# -# Return location of the module, determined by the property -# KMODULE_MODULE_LOCATION or KMODULE_IMPORTED_MODULE_LOCATION for -# imported target. In the last case the property is checked for being set. -function(kbuild_get_module_location RESULT_VAR name) - if(NOT TARGET ${name}) - message(FATAL_ERROR "\"${name}\" is not really a target.") - endif(NOT TARGET ${name}) - get_property(kmodule_type TARGET ${name} PROPERTY KMODULE_TYPE) - if(NOT kmodule_type) - message(FATAL_ERROR "\"${name}\" is not really a target for kernel module.") - endif(NOT kmodule_type) - get_property(kmodule_imported TARGET ${name} PROPERTY KMODULE_IMPORTED) - if(kmodule_imported) - get_property(module_location TARGET ${name} PROPERTY KMODULE_IMPORTED_MODULE_LOCATION) - if(NOT module_location) - message(FATAL_ERROR "target \"${name}\" for imported module has no property KMODULE_IMPORTED_MODULE_LOCATION set.") - endif(NOT module_location) - else(kmodule_imported) - get_property(module_location TARGET ${name} PROPERTY KMODULE_MODULE_LOCATION) - endif(kmodule_imported) - set(${RESULT_VAR} ${module_location} PARENT_SCOPE) -endfunction(kbuild_get_module_location RESULT_VAR module) - -# kbuild_get_symvers_location(RESULT_VAR name) -# -# Return location of the symvers file for the module, determined by the -# property KMODULE_SYMVERS_LOCATION or KMODULE_IMPORTED_SYMVERS_LOCATION -# for imported target. In the last case the property is checked for being set. -function(kbuild_get_symvers_location RESULT_VAR name) - if(NOT TARGET ${name}) - message(FATAL_ERROR "\"${name}\" is not really a target.") - endif(NOT TARGET ${name}) - get_property(kmodule_type TARGET ${name} PROPERTY KMODULE_TYPE) - if(NOT kmodule_type) - message(FATAL_ERROR "\"${name}\" is not really a target for kernel module.") - endif(NOT kmodule_type) - get_property(kmodule_imported TARGET ${name} PROPERTY KMODULE_IMPORTED) - if(kmodule_imported) - get_property(symvers_location TARGET ${name} PROPERTY KMODULE_IMPORTED_SYMVERS_LOCATION) - if(NOT symvers_location) - message(FATAL_ERROR "target \"${name}\" for imported module has no property KMODULE_IMPORTED_SYMVERS_LOCATION set.") - endif(NOT symvers_location) - else(kmodule_imported) - get_property(symvers_location TARGET ${name} PROPERTY KMODULE_SYMVERS_LOCATION) - endif(kmodule_imported) - set(${RESULT_VAR} ${symvers_location} PARENT_SCOPE) -endfunction(kbuild_get_symvers_location RESULT_VAR module) - -# kbuild_add_module( [EXCLUDE_FROM_ALL] [MODULE_NAME ] [ ...]) -# -# Build kernel module from , analogue of add_library(). -# -# Source files are divided into two categories: -# -Object sources -# -Other sourses -# -# Object sources are those sources, which may be used in building kernel -# module externally. -# Follow types of object sources are supported now: -# .c: a file with the code in C language; -# .S: a file with the code in assembly language; -# .o_shipped: shipped file in binary format, -# does not require additional preprocessing. -# -# Other sources are treated as only prerequisite of building process. -# -# -# kbuild_add_module( [MODULE_NAME ] IMPORTED) -# -# Create target, corresponded to the imported kernel module. -# In that case KMODULE_IMPORTED_* properties should be set manually if needed. -# -# In either case, if MODULE_NAME option is given, it determine name -# of the kernel module. Otherwise itself is used. -function(kbuild_add_module name) - cmake_parse_arguments(kbuild_add_module "IMPORTED;EXCLUDE_FROM_ALL" "MODULE_NAME" "" ${ARGN}) - if(kbuild_add_module_MODULE_NAME) - set(module_name "${kbuild_add_module_MODULE_NAME}") - else(kbuild_add_module_MODULE_NAME) - set(module_name "${name}") - endif(kbuild_add_module_MODULE_NAME) - - string(LENGTH ${module_name} module_name_len) - if(module_name_len GREATER 55) - # Name of the kernel module should fit into array of size (64-sizeof(unsigned long)). - # On 64-bit systems(most restricted) this is 56. - # Even 56 length is not good, as it is not include null character. - # Without it, old versions of rmmod failed to unload module. - message(SEND_ERROR "Kernel module name exceeds 55 characters: '${module_name}'") - endif(module_name_len GREATER 55) - - if(kbuild_add_module_IMPORTED) - # Creation of IMPORTED target is simple. - add_custom_target(${name}) - set_property(TARGET ${name} PROPERTY KMODULE_TYPE "kmodule") - set_property(TARGET ${name} PROPERTY KMODULE_IMPORTED "TRUE") - set_property(TARGET ${name} PROPERTY KMODULE_MODULE_NAME "${module_name}") - return() - endif(kbuild_add_module_IMPORTED) - - if(kbuild_add_module_EXCLUDE_FROM_ALL) - set(all_arg) - else(kbuild_add_module_EXCLUDE_FROM_ALL) - set(all_arg "ALL") - endif(kbuild_add_module_EXCLUDE_FROM_ALL) - - # List of all source files, which are given. - set(sources ${kbuild_add_module_UNPARSED_ARGUMENTS}) - - # Sources with absolute paths - to_abs_path(sources_abs ${sources}) - # list of files from which module building is depended - set(depend_files) - # Sources of "c" type, but without extension. - # Used for clean files, and for out-of-source builds do not create - # files in source tree. - set(c_sources_noext_abs) - # The sources with the code in assembly - set(asm_sources_noext_abs) - # Sources of "o_shipped" type, but without extension - set(shipped_sources_noext_abs) - # Categorize sources - foreach(source_abs ${sources_abs}) - get_filename_component(ext ${source_abs} EXT) - if(ext STREQUAL ".c" OR ext STREQUAL ".S" OR ext STREQUAL ".o_shipped") - # Real sources - # Move source into binary tree, if needed - copy_source_to_binary_dir("${source_abs}" source_abs) - - get_filename_component(source_noext "${source_abs}" NAME_WE) - get_filename_component(source_dir "${source_abs}" PATH) - set(source_noext_abs "${source_dir}/${source_noext}") - if(ext STREQUAL ".c") - # c-source - list(APPEND c_sources_noext_abs ${source_noext_abs}) - elseif(ext STREQUAL ".S") - # asm source - list(APPEND asm_sources_noext_abs ${source_noext_abs}) - elseif(ext STREQUAL ".o_shipped") - # shipped-source - list(APPEND shipped_sources_noext_abs ${source_noext_abs}) - endif(ext STREQUAL ".c") - endif(ext STREQUAL ".c" OR ext STREQUAL ".S" OR ext STREQUAL ".o_shipped") - # In any case, add file to depend list - list(APPEND depend_files ${source_abs}) - endforeach(source_abs ${sources_abs}) - - # Object sources relative to current binary dir - # (for $(module)-y :=) - set(obj_sources_noext_rel) - foreach(obj_sources_noext_abs - ${c_sources_noext_abs} ${asm_sources_noext_abs} ${shipped_sources_noext_abs}) - file(RELATIVE_PATH obj_source_noext_rel - ${CMAKE_CURRENT_BINARY_DIR} ${obj_sources_noext_abs}) - list(APPEND obj_sources_noext_rel ${obj_source_noext_rel}) - endforeach(obj_sources_noext_abs) - - if(NOT obj_sources_noext_rel) - message(FATAL_ERROR "List of object files for building kernel module ${name} is empty.") - endif(NOT obj_sources_noext_rel) - - set(obj_sources_rel) - # Detect, if build simple - source object name coincide with module name - - if(NOT obj_sources_noext_rel STREQUAL ${name}) - # Check, whether only one of source object names coincide with module name. - # This situation is incorrect for kbuild system. - list(FIND obj_sources_noext_rel ${name} is_objects_contain_name) - if(is_objects_contain_name GREATER -1) - message(FATAL_ERROR "Module should be built " - "either from only one object with same name, " - "or from objects with names different from the name of the module") - endif(is_objects_contain_name GREATER -1) - - foreach(obj_source_noext_rel ${obj_sources_noext_rel}) - list(APPEND obj_sources_rel "${obj_source_noext_rel}.o") - endforeach(obj_source_noext_rel ${obj_sources_noext_rel}) - endif(NOT obj_sources_noext_rel STREQUAL ${name}) - - _get_directory_property_chained(ccflags KBUILD_COMPILE_DEFINITIONS " ") - _get_directory_property_chained(include_dirs KBUILD_INCLUDE_DIRECTORIES) - - # Target for create module. - add_custom_target(${name} ALL - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" - "${CMAKE_CURRENT_BINARY_DIR}/${_kbuild_symvers}" - ) - - # Create .cmd files for 'shipped' sources. - # Gcc does not create them automatically for some reason. - set(cmd_create_command) - if(shipped_sources_noext_abs) - foreach(shipped_source_noext_abs ${shipped_source_noext_abs}) - get_filename_component(shipped_dir ${shipped_source_noext_abs} PATH) - get_filename_component(shipped_name ${shipped_source_noext_abs} NAME) - list(APPEND cmd_create_command - COMMAND printf "cmd_%s.o := cp -p %s.o_shipped %s.o\\n" - "${shipped_source_noext_abs}" - "${shipped_source_noext_abs}" - "${shipped_source_noext_abs}" - > "${shipped_dir}/.${shipped_name}.o.cmd") - endforeach(shipped_source_noext_abs ${shipped_source_noext_abs}) - endif(shipped_sources_noext_abs) - - # User-defined parameters for 'make' - set(make_flags ${KBUILD_MAKE_FLAGS}) - _get_per_build_var(make_flags_per_build KBUILD_MAKE_FLAGS) - list(APPEND make_flags ${make_flags_per_build}) - - # Rule for create module(and symvers file). - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" - "${CMAKE_CURRENT_BINARY_DIR}/${_kbuild_symvers}" - ${cmd_create_command} - COMMAND $(MAKE) ${make_flags} ${kbuild_additional_make_flags} - -C ${Kbuild_BUILD_DIR} M=${CMAKE_CURRENT_BINARY_DIR} modules -# Update timestamps for targets. -# In some cases Kbuild system may decide do not update resulted files -# even in case when depencies newer. -# Updating timestamps prevent command to be executed every make call. - COMMAND touch "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" - "${CMAKE_CURRENT_BINARY_DIR}/${_kbuild_symvers}" - DEPENDS ${depend_files} - "${CMAKE_CURRENT_BINARY_DIR}/Kbuild" - COMMENT "Building kernel module ${name}" - ) - - # Fill properties for the target. - set_property(TARGET ${name} PROPERTY KMODULE_TYPE "kmodule") - set_property(TARGET ${name} PROPERTY KMODULE_IMPORTED "FALSE") - set_property(TARGET ${name} PROPERTY KMODULE_MODULE_NAME "${module_name}") - - set_property(TARGET ${name} PROPERTY KMODULE_MODULE_LOCATION - "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" - ) - set_property(TARGET ${name} PROPERTY KMODULE_SYMVERS_LOCATION - "${CMAKE_CURRENT_BINARY_DIR}/${_kbuild_symvers}" - ) - - # Internal properties - set_property(TARGET ${name} PROPERTY KMODULE_BINARY_DIR - "${CMAKE_CURRENT_BINARY_DIR}" - ) - set_property(TARGET ${name} PROPERTY KMODULE_OBJ_SOURCES ${obj_sources_rel}) - set_property(TARGET ${name} PROPERTY KMODULE_COMPILE_FLAGS ${ccflags}) - set_property(TARGET ${name} PROPERTY KMODULE_INCLUDE_DIRECTORIES ${include_dirs}) - - - # Add target to the list of modules for built. - set_property(GLOBAL APPEND PROPERTY KMODULE_TARGETS "${name}") - - # The rule to clean files - _kbuild_module_clean_files(${name} - C_SOURCE ${c_sources_noext_abs} - ASM_SOURCE ${asm_sources_noext_abs} - SHIPPED_SOURCE ${shipped_sources_noext_abs}) -endfunction(kbuild_add_module name) - -# kbuild_include_directories(dirs ...) -# -# Add include directories for Kbuild process. -macro(kbuild_include_directories) - set_property(DIRECTORY APPEND PROPERTY KBUILD_INCLUDE_DIRECTORIES ${ARGN}) -endmacro(kbuild_include_directories) - -# kbuild_add_definitions (flags) -# -# Specify additional flags for compile kernel module. -# -# Note that multiple flags should be specified as single string, -# delimited with ' '. -function(kbuild_add_definitions flags) - get_property(current_flags DIRECTORY PROPERTY KBUILD_COMPILE_DEFINITIONS) - _string_join(" " current_flags "${current_flags}" "${flags}") - set_property(DIRECTORY PROPERTY KBUILD_COMPILE_DEFINITIONS "${current_flags}") -endfunction(kbuild_add_definitions flags) - -# There is no control for kbuild make flags except default values. -# Support for kbuild_add_make_definitions may be added if needed. - - -# kbuild_link_module( [ ...]) -# -# Link kernel module with other modules, that allows to use symbols from -# other modules. -# -# may be: -# 1) target name for other kernel module intended for build -# 2) target name for imported kernel module with -# KMODULE_IMPORTED_SYMVERS_LOCATION property set -# 3) absolute path to symvers file of other kernel module. -# -# In any case, file-level dependency will be created for symvers file to link with. -# In the first case, also target-level dependency will be added -# -# Analogue for target_link_library(). -# -# May be used only from the same directory, where module target is created. -function(kbuild_link_module name) - # Check that @name corresponds to kernel module target for build. - get_property(is_module TARGET ${name} PROPERTY KMODULE_TYPE SET) - if(NOT is_module) - message(FATAL_ERROR "kbuild_module_link: passed \n\t\"${name}\"\n which is not kernel module target.") - endif(NOT is_module) - get_property(module_imported TARGET ${name} PROPERTY KMODULE_IMPORTED) - if(kmodule_imported) - message(FATAL_ERROR "Imported kernel module target \"${name}\" may not be linked.") - endif(kmodule_imported) - - get_property(module_name TARGET ${name} PROPERTY KMODULE_MODULE_NAME) - - foreach(l ${ARGN}) - string(REGEX MATCH "/" link_is_file ${l}) - if(link_is_file) - string(REGEX MATCH "^/" file_is_absolute ${l}) - if(NOT file_is_absolute) - message(FATAL_ERROR "kbuild_module_link: passed filename\n\t\"${link}\"\nwhich is not absolute as .") - endif(NOT file_is_absolute) - set(symvers_location "${l}") - # Do not require symvers file to be already existed. - else(link_is_file) - if(NOT TARGET ${l}) - message(FATAL_ERROR "kbuild_module_link: passed link\n\t\"${l}\"\n which is neither an absolute path to symvers file nor a target.") - endif(NOT TARGET ${l}) - get_property(kmodule_type TARGET ${l} PROPERTY KMODULE_TYPE) - if(NOT kmodule_type) - message(FATAL_ERROR "kbuild_module_link: passed target\n\t\"${l}\"\n which is not a target for kernel module as link.") - endif(NOT kmodule_type) - get_property(kmodule_imported TARGET ${l} PROPERTY KMODULE_IMPORTED) - if(kmodule_imported) - get_property(symvers_location TARGET ${l} PROPERTY KMODULE_IMPORTED_SYMVERS_LOCATION) - if(NOT symvers_location) - message(FATAL_ERROR "kbuild_module_link: passed imported target\n\t\"${l}\"\n without \"KMODULE_IMPORTED_SYMVERS_LOCATION\" property set as link.") - endif(NOT symvers_location) - else(kmodule_imported) - get_property(symvers_location TARGET ${l} PROPERTY KMODULE_SYMVERS_LOCATION) - # Target dependency is added only for non-imported target. - add_dependencies(${name} ${l}) - endif(kmodule_imported) - endif(link_is_file) - add_custom_command(OUTPUT - "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.ko" - "${CMAKE_CURRENT_BINARY_DIR}/${_kbuild_symvers}" - DEPENDS ${symvers_location} - APPEND - ) - set_property(TARGET ${name} APPEND PROPERTY KMODULE_DEPEND_SYMVERS ${symvers_location}) - endforeach(l ${ARGN}) -endfunction(kbuild_link_module name) - -# kbuild_finalize_linking() -# -# Should be called after all kernel modules and their links defined. -# Usually this is the end of main CMakeLists.txt file. -function(kbuild_finalize_linking) - _get_per_build_var(ccflags_per_build KBUILD_C_FLAGS) - - get_property(kmodule_targets GLOBAL PROPERTY KMODULE_TARGETS) - foreach(m ${kmodule_targets}) - get_property(module_binary_dir TARGET ${m} PROPERTY KMODULE_BINARY_DIR) - get_property(module_name TARGET ${m} PROPERTY KMODULE_MODULE_NAME) - get_property(module_symvers_cmake TARGET ${m} PROPERTY KMODULE_DEPEND_SYMVERS) - - get_property(obj_sources TARGET ${m} PROPERTY KMODULE_OBJ_SOURCES) - get_property(ccflags_local TARGET ${m} PROPERTY KMODULE_COMPILE_FLAGS) - get_property(include_dirs TARGET ${m} PROPERTY KMODULE_INCLUDE_DIRECTORIES) - - # Combine all compiler flags together - _string_join(" " ccflags "${KBUILD_C_FLAGS}" "${ccflags_local}") - foreach(dir ${include_dirs}) - _string_join(" " ccflags "${ccflags}" "-I${dir}") - endforeach(dir ${include_dirs}) - _string_join(" " ccflags "${ccflags}" "${ccflags_per_build}") - - string(REPLACE ";" " " module_symvers "${module_symvers_cmake}") - - if(obj_sources) - set(obj_src_string "${module_name}-y := ") - foreach(obj ${obj_sources}) - set(obj_src_string "${obj_src_string} ${obj}") - endforeach(obj ${obj_sources}) - else(obj_sources) - # Simple building process - module name is same as the name - # of the only source. - set(obj_src_string "") - endif(obj_sources) - # Configure kbuild file - configure_file(${kbuild_this_module_dir}/kbuild_system_files/Kbuild.in - ${module_binary_dir}/Kbuild - ) - endforeach(m ${kmodule_targets}) -endfunction(kbuild_finalize_linking) - -# kbuild_install(TARGETS ... -# [[MODULE|SYMVERS] -# DESTINATION -# [CONFIGURATIONS [...]] -# [COMPONENT ] -# ]+) -# -# Install kernel module(s) and/or symvers file(s) into given directory. -# -# Almost all options means same as for install() cmake command. -# 'MODULE' refers to kernel module itself, SYMVERS refers symvers file. -# -# Unlike to standard install() command, there is no default destination -# directory neither for modules nor for symvers files. -# So, at least one rule should be defined, and 'DESTINATION' option -# should be set for every rule. -# -# TODO: support for 'EXPORT' mode. -function(kbuild_install type) - if(NOT type STREQUAL "TARGETS") - message(FATAL_ERROR "Only 'TARGETS' mode is currently supported") - endif(NOT type STREQUAL "TARGETS") - - parse_install_arguments(kbuild_install - "MODULE;SYMVERS" # Section types - "" "DESTINATION;COMPONENT" "CONFIGURATIONS" # Section keywords classification. - ${ARGN} - ) - - set(kbuild_install_TARGETS ${kbuild_install_GLOBAL_ARGUMENTS}) - if(NOT kbuild_install_TARGETS) - message(FATAL_ERROR "No targets given for kbuild_install() command") - endif(NOT kbuild_install_TARGETS) - - if(NOT kbuild_install_sections) - message(FATAL_ERROR "There is no default destination for install kernel module components, but no section described it is given.") - endif(NOT kbuild_install_sections) - - foreach(section_type ${kbuild_install_sections}) - if(NOT kbuild_install_${section_type}_DESTINATION) - message(FATAL_ERROR "DESTINATION is not defined for section ${section_type}") - endif(NOT kbuild_install_${section_type}_DESTINATION) - # All additional arguments for given section. - set(install_args_${section_type} - DESTINATION "${kbuild_install_${section_type}_DESTINATION}") - if(kbuild_install_${section_type}_CONFIGURATION) - list(APPEND install_args_${section_type} - CONFIGURATION "${kbuild_install_${section_type}_CONFIGURATION}" - ) - endif(kbuild_install_${section_type}_CONFIGURATION) - if(kbuild_install_${section_type}_COMPONENT) - list(APPEND install_args_${section_type} - COMPONENT "${kbuild_install_${section_type}_COMPONENT}" - ) - endif(kbuild_install_${section_type}_COMPONENT) - # Module installation. - if(section_type STREQUAL "MODULE" OR section_type STREQUAL "ALL") - # Combine locations for all modules in one list. - set(module_locations) - foreach(t ${kbuild_install_TARGETS}) - kbuild_get_module_location(module_location ${t}) - list(APPEND module_locations ${module_location}) - endforeach(t ${kbuild_install_TARGETS}) - # .. and install them at once. - install(FILES ${module_locations} ${install_args_${section_type}}) - endif(section_type STREQUAL "MODULE" OR section_type STREQUAL "ALL") - # Symvers installation. - if(section_type STREQUAL "SYMVERS" OR section_type STREQUAL "ALL") - # Because of renaming, symvers files should be installed separately. - foreach(t ${kbuild_install_TARGETS}) - kbuild_get_symvers_location(symvers_location ${t}) - get_property(module_name TARGET ${t} PROPERTY KMODULE_MODULE_NAME) - install(FILES ${symvers_location} - RENAME "${module_name}.symvers" - ${install_args_${section_type}} - ) - endforeach(t ${kbuild_install_TARGETS}) - endif(section_type STREQUAL "SYMVERS" OR section_type STREQUAL "ALL") - endforeach(section_type ${kbuild_install_sections}) -endfunction(kbuild_install type) - - -# kbuild_try_compile(RESULT_VAR bindir srcfile|SOURCES src ... -# [CMAKE_FLAGS ] -# [KBUILD_COMPILE_DEFINITIONS flags ...] -# [OUTPUT_VARIABLE var]) -# -# Similar to try_module in simplified form, but compile srcfile as -# kernel module, instead of user space program. -# -# KBUILD_COMPILE_DEFINITIONS contains compiler definition flags for -# build kernel module. -# -# Possible CMAKE_FLAGS which has special semantic: -# KBUILD_INCLUDE_DIRECTORIES - include directories for build kernel module -# KBUILD_LINK_MODULE - symvers file(s) for link module with other modules. -function(kbuild_try_compile RESULT_VAR bindir srcfile) - cmake_parse_arguments(kbuild_try_compile "" "OUTPUT_VARIABLE" "CMAKE_FLAGS;KBUILD_COMPILE_DEFINITIONS" ${ARGN}) - if(srcfile STREQUAL "SOURCES") - set(srcfiles ${kbuild_try_compile_UNPARSED_ARGUMENTS}) - else(srcfile STREQUAL "SOURCES") - set(srcfiles ${srcfile}) - endif(srcfile STREQUAL "SOURCES") - - # Inside try_compile project values of variables - # CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR - # differs from ones in current project. - # - # So make all paths absolute before pass them into try_compile project. - # Note, that copiing files into binary dir is nevertheless performed - # in the try_compile project, because it bases on real current binary dir. - to_abs_path(srcfiles_abs ${srcfiles}) - - # Collect parameters to try_compile() function - set(cmake_flags - "-DSOURCES:STRING=${srcfiles_abs}" # Source file(s) - "-DCMAKE_MODULE_PATH:PATH=${CMAKE_MODULE_PATH}" # Path for search include files. - ${kbuild_try_compile_CMAKE_FLAGS} - ) - - # Parameters for compiler - if(kbuild_try_compile_KBUILD_COMPILE_DEFINITIONS) - list(APPEND cmake_flags - "-DKBUILD_COMPILE_DEFINITIONS:STRING=${kbuild_try_compile_KBUILD_COMPILE_DEFINITIONS}" - ) - endif(kbuild_try_compile_KBUILD_COMPILE_DEFINITIONS) - - # Other user-defined cmake flags - if(kbuild_try_compile_CMAKE_FLAGS) - - endif(kbuild_try_compile_CMAKE_FLAGS) - - # Possible definition of output variable. - if(kbuild_try_compile_OUTPUT_VARIABLE) - set(output_variable_def "OUTPUT_VARIABLE" "output_tmp") - else(build_try_compile_OUTPUT_VARIABLE) - set(output_variable_def) - endif(kbuild_try_compile_OUTPUT_VARIABLE) - - - try_compile(result_tmp # Result variable(temporary) - "${bindir}" # Binary directory - "${kbuild_aux_dir}/try_compile_project" # Source directory - "kmodule" # Project name - CMAKE_FLAGS # Flags to CMake: - ${cmake_flags} - ${kbuild_try_compile_flags} - ${output_variable_def} - ) - - if(kbuild_try_compile_OUTPUT_VARIABLE) - # Set output variable for the caller - set("${kbuild_try_compile_OUTPUT_VARIABLE}" "${output_tmp}" PARENT_SCOPE) - endif(kbuild_try_compile_OUTPUT_VARIABLE) - # Set result variable for the caller - set("${RESULT_VAR}" "${result_tmp}" PARENT_SCOPE) -endfunction(kbuild_try_compile RESULT_VAR bindir srcfile) - -########### Auxiliary functions for internal use ####################### -# Per-directory tracking for kbuild compiler flags. -# -# Unlike to standard COMPILE_FLAGS, these flags do not include values -# set for parent directories. -# (There is no generic "fill-with-parent's values" mechanism exists -# in cmake). -# -# So, this property has a little sence for the user. -# It exists only for make effect of kbuild_add_definitions() to -# cross "function" scope. -define_property(DIRECTORY PROPERTY KBUILD_COMPILE_FLAGS - BRIEF_DOCS "Compiler flags used by Kbuild system added in this directory." - FULL_DOCS "Compiler flags used by Kbuild system added in this directory." -) - -# Per-directory tracking for kbuild include directories. -# -# Unlike to standard INCLUDE_DIRECTORIES, these ones do not include values -# set for parent directories. -# (There is no generic "fill-with-parent's values" mechanism exists -# in cmake). -# -# So, this property has a little sence for the user. -# It exists only for make effect of kbuild_include_directories() to -# cross "function" scope. -define_property(DIRECTORY PROPERTY KBUILD_INCLUDE_DIRECTORIES - BRIEF_DOCS "Include directories used by Kbuild system; added in this directory." - FULL_DOCS "Include directories used by Kbuild system; added in this directory." -) - - -# Parameters below are set externally only in try_compile() for subproject, -# which include this file. -# No need to cache them as try_compile() project is not configured by the user. - -# Real top-level source directory. -if(NOT KBUILD_REAL_SOURCE_DIR) - set(KBUILD_REAL_SOURCE_DIR ${CMAKE_SOURCE_DIR}) -endif(NOT KBUILD_REAL_SOURCE_DIR) -# Real top-level binary directory. -if(NOT KBUILD_REAL_BINARY_DIR) - set(KBUILD_REAL_BINARY_DIR ${CMAKE_BINARY_DIR}) -endif(NOT KBUILD_REAL_BINARY_DIR) - -# These flags are passed to the 'make' when compile kernel module. -set(kbuild_additional_make_flags) -# These CMake flags will be passed to try_compile() subproject. -set(kbuild_try_compile_flags - "-DKBUILD_REAL_SOURCE_DIR=${KBUILD_REAL_SOURCE_DIR}" - "-DKBUILD_REAL_BINARY_DIR=${KBUILD_REAL_BINARY_DIR}" -) - -# ARCH and CROSS_COMPILE are passed to submake only when non-empty. -if(ARCH) - list(APPEND kbuild_additional_make_flags "ARCH=${ARCH}") - list(APPEND kbuild_try_compile_flags "-DARCH=${ARCH}") -endif(ARCH) -if(CROSS_COMPILE) - list(APPEND kbuild_additional_make_flags "CROSS_COMPILE=${CROSS_COMPILE}") - list(APPEND kbuild_try_compile_flags "-DCROSS_COMPILE=${CROSS_COMPILE}") -endif(CROSS_COMPILE) - -# List of targets created with kmodule_add_module() without -# 'IMPORTED' option. -# -# This list is traversed in kmodule_finalize_linking() for -# create 'Kbuild' files. -# -# Note, that this list does not contain all defined kernel modules, -# so property normally shouldn't be used by outer code. -define_property(GLOBAL PROPERTY KMODULE_TARGETS - BRIEF_DOCS "List of kernel module targets configured for build" - FULL_DOCS "List of kernel module targets configured for build" -) - -# CMAKE_CURRENT_BINARY_DIR at the moment, when kbuild_add_module() is issued. -define_property(TARGET PROPERTY KMODULE_BINARY_DIR - BRIEF_DOCS "CMAKE_CURRENT_BINARY_DIR where module is built." - FULL_DOCS "CMAKE_CURRENT_BINARY_DIR where module is built." -) - -# List of object files, used for build kernel module. -# -# List may be empty in case of simple build, when target module has same -# name as its only source. -# -# This is internal property for configure 'Kbuild' file. -define_property(TARGET PROPERTY KMODULE_OBJ_SOURCES - BRIEF_DOCS "Object source files for build kernel module." - FULL_DOCS "Object source files for build kernel module." -) - -# Compiler flags, added with kbuild_add_definitions() -# -# Note, that KBUILD_C_FLAGS* are not included here. -# -# This is internal property for configure 'Kbuild' file. -define_property(TARGET PROPERTY KMODULE_COMPILE_FLAGS - BRIEF_DOCS "Additional compile flags for build kernel module." - FULL_DOCS "Additional compile flags for build kernel module." -) - -# Include directories, added with kbuild_include_directories(). -# -# This is internal property for configure 'Kbuild' file. -define_property(TARGET PROPERTY KMODULE_COMPILE_FLAGS - BRIEF_DOCS "Additional compile flags for build kernel module." - FULL_DOCS "Additional compile flags for build kernel module." -) - - -# List of symvers files, added by kbuild_link_module(). -# -# This is internal property for configure 'Kbuild' file. -define_property(TARGET PROPERTY KMODULE_DEPEND_SYMVERS - BRIEF_DOCS "Symvers files this module depends on." - FULL_DOCS "Symvers files this module depends on." -) - -# Constants for internal filenames. -set(_kbuild_symvers "Module.symvers") - -# copy_source_to_binary_dir( ) -# -# Helper for the building kernel module. -# -# Make sure that given source file is inside current binary dir. -# That place is writable and has some "uniqeness" garantee for generate -# auxiliary files during build process. -# -# If already inside current binary dir, do nothing and -# set variable to itself. -# -# Otherwise create rule for copy source into "nice" place inside -# current binary dir and set pointed to that place. -function(copy_source_to_binary_dir source new_source_var) - is_path_inside_dir(is_in_current_binary ${CMAKE_CURRENT_BINARY_DIR} "${source}") - if(is_in_current_binary) - # Source is already placed where we want. - set(${new_source_var} "${source}" PARENT_SCOPE) - else(is_in_current_binary) - # Base directory from which count relative source path. - # By default, it is root directory. - set(base_dir "/") - # Try "nicer" base directories. - foreach(d - ${KBUILD_REAL_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${KBUILD_REAL_SOURCE_DIR} - ) - is_path_inside_dir(is_in_d ${d} "${source}") - if(is_in_d) - set(base_dir ${d}) - break() - endif(is_in_d) - endforeach(d) - # Copy source into same relativ dir, but inside current binary one. - file(RELATIVE_PATH source_rel "${base_dir}" "${source}") - set(new_source "${CMAKE_CURRENT_BINARY_DIR}/${source_rel}") - get_filename_component(new_source_dir ${new_source} PATH) - file(MAKE_DIRECTORY "${new_source_dir}") - rule_copy_file("${new_source}" "${source}") - # Return path to the new source. - set(${new_source_var} "${new_source}" PARENT_SCOPE) - endif(is_in_current_binary) -endfunction(copy_source_to_binary_dir source new_source_var) - -# _get_per_build_var(RESULT_VAR variable) -# -# Return value of per-build variable. -macro(_get_per_build_var RESULT_VAR variable) - if(CMAKE_BUILD_TYPE) - string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type_uppercase) - set(${RESULT_VAR} "${${variable}_${_build_type_uppercase}}") - else(CMAKE_BUILD_TYPE) - set(${RESULT_VAR}) - endif(CMAKE_BUILD_TYPE) -endmacro(_get_per_build_var RESULT_VAR variable) - -# _string_join(sep RESULT_VAR str1 str2) -# -# Join strings and using as glue. -# -# Note, that precisely 2 string are joined, not a list of strings. -# This prevents automatic replacing of ';' inside strings while parsing arguments. -macro(_string_join sep RESULT_VAR str1 str2) - if("${str1}" STREQUAL "") - set("${RESULT_VAR}" "${str2}") - elseif("${str2}" STREQUAL "") - set("${RESULT_VAR}" "${str1}") - else("${str1}" STREQUAL "") - set("${RESULT_VAR}" "${str1}${sep}${str2}") - endif("${str1}" STREQUAL "") -endmacro(_string_join sep RESULT_VAR str1 str2) -# _build_get_directory_property_chained(RESULT_VAR []) -# -# Return list of all values for given property in the current directory -# and all parent directories. -# -# If is given, it is used as glue for join values. -# By default, cmake list separator (';') is used. -function(_get_directory_property_chained RESULT_VAR property_name) - set(sep ";") - foreach(arg ${ARGN}) - set(sep "${arg}") - endforeach(arg ${ARGN}) - set(result "") - set(d "${CMAKE_CURRENT_SOURCE_DIR}") - while(NOT "${d}" STREQUAL "") - get_property(p DIRECTORY "${d}" PROPERTY "${property_name}") - # debug - # message("Property ${property_name} for directory ${d}: '${p}'") - _string_join("${sep}" result "${p}" "${result}") - # message("Intermediate result: '${result}'") - get_property(d DIRECTORY "${d}" PROPERTY PARENT_DIRECTORY) - endwhile(NOT "${d}" STREQUAL "") - set("${RESULT_VAR}" "${result}" PARENT_SCOPE) -endfunction(_get_directory_property_chained RESULT_VAR property_name) - -# _kbuild_module_clean_files(module_name -# [C_SOURCE c_source_noext_abs ...] -# [ASM_SOURCE asm_source_noext_abs ...] -# [SHIPPED_SOURCE shipped_source_noext_abs ...]) -# -# Tell CMake that intermediate files, created by kbuild system, -# should be cleaned with 'make clean'. -function(_kbuild_module_clean_files module_name) - cmake_parse_arguments(kbuild_module_clean "" "" "C_SOURCE;ASM_SOURCE;SHIPPED_SOURCE" ${ARGN}) - if(kbuild_module_clean_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unparsed arguments") - endif(kbuild_module_clean_UNPARSED_ARGUMENTS) - - # List common files (names only) for cleaning - set(common_files_names - ".tmp_versions" # Directory - "modules.order" - "Module.markers" - ) - # List module name-depending files (extensions only) for cleaning - set(name_files_ext - ".o" - ".mod.c" - ".mod.o" - ) - # Same but for the files with names starting with a dot ('.'). - set(name_files_dot_ext - ".ko.cmd" - ".mod.o.cmd" - ".o.cmd" - ) - # List source name-depending files (extensions only) for cleaning - set(source_name_files_ext - ".o" - ) - # Same but for the files with names starting with a dot ('.') - set(source_name_files_dot_ext - ".o.cmd" - ".o.d" # This file is created in case of unsuccessfull build - ) - - # Now collect all sort of files into list - set(files_list) - - foreach(name ${common_files_names}) - list(APPEND files_list "${CMAKE_CURRENT_BINARY_DIR}/${name}") - endforeach(name ${common_files_names}) - - foreach(ext ${name_files_ext}) - list(APPEND files_list - "${CMAKE_CURRENT_BINARY_DIR}/${module_name}${ext}") - endforeach(ext ${name_files_ext}) - - foreach(ext ${name_files_dot_ext}) - list(APPEND files_list - "${CMAKE_CURRENT_BINARY_DIR}/.${module_name}${ext}") - endforeach(ext ${name_files_ext}) - - # All the types of sources are processed in a similar way - foreach(obj_source_noext_abs ${kbuild_module_clean_C_SOURCE} - ${kbuild_module_clean_ASM_SOURCE} ${kbuild_module_clean_SHIPPED_SOURCE}) - - get_filename_component(dir ${obj_source_noext_abs} PATH) - get_filename_component(name ${obj_source_noext_abs} NAME) - foreach(ext ${source_name_files_ext}) - list(APPEND files_list "${dir}/${name}${ext}") - endforeach(ext ${source_name_files_ext}) - foreach(ext ${source_name_files_dot_ext}) - list(APPEND files_list "${dir}/.${name}${ext}") - endforeach(ext ${source_name_files_ext}) - endforeach(obj_source_noext_abs) - # Tell CMake that given files should be cleaned. - set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${files_list}") -endfunction(_kbuild_module_clean_files module_name) - -# parse_install_arguments(prefix args..) -# -# Helper for parse arguments for install-like command. -# -# All arguments before the first keyword are classified as GLOBAL_ARGUMENTS -# and stored into list ${prefix}_GLOBAL_ARGUMENTS. -# -# describe possible keywords denoting section type. -# At most one section may exist for every type. -# Special type ALL means generic section. -# -# , and describe -# all keywords inside section. -# Values corresponded to these keywords are stored under -# __*. -# Note, that unlike to cmake_parse_arguments(), all arguments inside -# section definition should be either keyword or its value(s). -# Additionally, _ is set to TRUE for every -# encountered section and _sections contains list of such sections. -# -# If non section is currently active, the first section keyword starts -# special generic section. Definitions for this section are stored as -# for section of type ALL. -# If such section exists, it should be the only section. -# -# Function is generic, but currently is used only there. -function(parse_install_arguments prefix section_types options one_value_keywords multiple_value_keywords) - set(all_keywords ${section_types} ${options} ${one_value_keywords} ${multiple_value_keywords}) - # Type of the currently parsed section('ALL' for ALL section). - set(current_section_type) - # Active section-related keyword for current section. - set(current_section_keyword) - # Classification for @_current_section_keyword: - # 'OPTION', 'ONE' or 'MULTY'. - set(current_section_keyword_type) - - # Clean all previous keyword values. - set("pia_GLOBAL_ARGUMENTS") - foreach(section_type "ALL" ${section_types}) - set(pia_${section_type} "FALSE") - foreach(opt ${options}) - set("pia_${section_type}_${opt}" "FALSE") - endforeach(opt ${options}) - foreach(keyword ${one_value_keywords} ${multiple_value_keywords}) - set("pia_${section_type}_${keyword}") - endforeach(keyword ${one_value_keywords} ${multiple_value_keywords}) - endforeach(section_type "ALL" ${section_types}) - set("pia_sections") - - foreach(arg ${ARGN}) - list(FIND all_keywords ${arg} keyword_index) - if(keyword_index EQUAL "-1") - if(current_section_type) - if(NOT current_section_keyword) - message(FATAL_ERROR "In section ${current_section_type} argument ${arg} does not belong to any keyword.") - endif(NOT current_section_keyword) - if(current_section_keyword_type STREQUAL "ONE") - set("pia_${current_section_type}_${current_section_keyword}" "${arg}") - set(current_section_keyword "") - else(current_section_keyword_type STREQUAL "ONE") - list(APPEND "pia_${current_section_type}_${current_section_keyword}" "${arg}") - endif(current_section_keyword_type STREQUAL "ONE") - else(current_section_type) - list(APPEND "pia_GLOBAL_ARGUMENTS" "${arg}") - endif(current_section_type) - else(keyword_index EQUAL "-1") - if(current_section_type AND current_section_keyword AND current_section_keyword_type STREQUAL "ONE") - message(FATAL_ERROR "Keyword ${arg} found while value for one-value-option ${current_section_keyword} expected.") - endif(current_section_type AND current_section_keyword AND current_section_keyword_type STREQUAL "ONE") - list(FIND section_types "${arg}" section_type_index) - if(section_type_index EQUAL "-1") - if(NOT current_section_type) - set(current_section_type "ALL") - set("pia_${current_section_type}" "TRUE") - list(APPEND "pia_sections" "${current_section_type}") - endif(NOT current_section_type) - list(FIND options ${arg} option_index) - if(option_index EQUAL "-1") - set(current_section_keyword ${arg}) - list(FIND one_value_keywords ${current_section_keyword} one_index) - if(one_index EQUAL "-1") - set(current_section_keyword_type "MULTY") - else(one_index EQUAL "-1") - set(current_section_keyword_type "ONE") - endif(one_index EQUAL "-1") - set("pia_${current_section_type}_${current_section_keyword}") - else(option_index EQUAL "-1") - set("${prefix}_${current_section_type}_${current_section_keyword}" "TRUE") - set(current_section_keyword_type) - endif(option_index EQUAL "-1") - else(section_type_index EQUAL "-1") - if("pia_ALL") - message(FATAL_ERROR "Generic section should be the only section defined") - endif("pia_ALL") - set(current_section_type "${arg}") - if("pia_${current_section_type}") - message(FATAL_ERROR "Section ${current_section_type} is defined twice.") - endif("pia_${current_section_type}") - set("pia_${current_section_type}" "TRUE") - list(APPEND "pia_sections" "${current_section_type}") - # Current keyword is initially undefined for such section. - set(current_section_keyword) - endif(section_type_index EQUAL "-1") - endif(keyword_index EQUAL "-1") - endforeach(arg ${ARGN}) - - # propagate the result variables to the caller - set("${prefix}_GLOBAL_ARGUMENTS" ${pia_GLOBAL_ARGUMENTS} PARENT_SCOPE) - foreach(section_type "ALL" ${section_types}) - set(${prefix}_${section_type} ${pia_${section_type}} PARENT_SCOPE) - foreach(keyword ${options} ${one_value_keywords} ${multiple_value_keywords}) - set("${prefix}_${section_type}_${keyword}" ${pia_${section_type}_${keyword}} PARENT_SCOPE) - endforeach(keyword ${options} ${one_value_keywords} ${multiple_value_keywords}) - endforeach(section_type "ALL" ${section_types}) - set("${prefix}_sections" ${pia_sections} PARENT_SCOPE) -endfunction(parse_install_arguments prefix section_types options one_value_keywords multiple_value_keywords) diff --git a/cmake_targets/tools/MODULES/kmodule.cmake b/cmake_targets/tools/MODULES/kmodule.cmake deleted file mode 100755 index 2912ac115a..0000000000 --- a/cmake_targets/tools/MODULES/kmodule.cmake +++ /dev/null @@ -1,539 +0,0 @@ -set(kmodule_this_module_dir "${CMAKE_SOURCE_DIR}/cmake/modules/") -set(kmodule_test_sources_dir "${CMAKE_SOURCE_DIR}/cmake/kmodule_sources") - -set(kmodule_function_map_file "") -if (CMAKE_CROSSCOMPILING) - if (KEDR_SYSTEM_MAP_FILE) - set (kmodule_function_map_file "${KEDR_SYSTEM_MAP_FILE}") - else (KEDR_SYSTEM_MAP_FILE) -# KEDR_SYSTEM_MAP_FILE is not specified, construct the default path -# to the symbol map file. - set (kmodule_function_map_file - "${KEDR_ROOT_DIR}/boot/System.map-${KBUILD_VERSION_STRING}" - ) - endif (KEDR_SYSTEM_MAP_FILE) -endif (CMAKE_CROSSCOMPILING) - -# List of unreliable functions, that is, the functions that may be -# be exported and mentioned in System.map but still cannot be used -# because no header provides their declarations. -set(unreliable_functions_list - "__kmalloc_node" - "kmem_cache_alloc_node" - "kmem_cache_alloc_node_notrace" - "kmem_cache_alloc_node_trace" -) - -# kmodule_is_function_exist(function_name RESULT_VAR) -# Verify, whether given function exist in the kernel space on the current system. -# RESULT_VAR is TRUE, if function_name exist in the kernel space, FALSE otherwise. -# RESULT_VAR is cached. - -function(kmodule_is_function_exist function_name RESULT_VAR) - check_begin("Looking for ${function_name} in the kernel") - if(NOT DEFINED ${RESULT_VAR}) - check_try() - execute_process( - COMMAND sh "${kmodule_this_module_dir}/kmodule_files/scripts/lookup_kernel_function.sh" - ${function_name} ${kmodule_function_map_file} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - RESULT_VARIABLE kmodule_is_function_exist_result - OUTPUT_QUIET) - - if (kmodule_is_function_exist_result EQUAL 0) - list(FIND unreliable_functions_list ${function_name} unreliable_function_index) - if(unreliable_function_index GREATER -1) - # Additional verification for unreliable function - kbuild_try_compile(kmodule_function_is_exist_reliable - "${CMAKE_BINARY_DIR}/check_unreliable_functions/${function_name}" - "${kmodule_test_sources_dir}/check_unreliable_functions/${function_name}.c" - ) - if(NOT kmodule_function_is_exist_reliable) - set(kmodule_is_function_exist_result 1) - endif(NOT kmodule_function_is_exist_reliable) - endif(unreliable_function_index GREATER -1) - endif(kmodule_is_function_exist_result EQUAL 0) - - if (kmodule_is_function_exist_result EQUAL 0) - set(${RESULT_VAR} "TRUE" CACHE INTERNAL "Does ${function_name} exist in the kernel?") - elseif(kmodule_is_function_exist_result EQUAL 1) - set(${RESULT_VAR} "FALSE" CACHE INTERNAL "Does ${function_name} exist in the kernel?") - else(kmodule_is_function_exist_result EQUAL 0) - message(FATAL_ERROR -"Cannot determine whether function '${function_name}' exists in the kernel" - ) - endif(kmodule_is_function_exist_result EQUAL 0) - endif(NOT DEFINED ${RESULT_VAR}) - set_bool_string(check_result "found" "not found" ${${RESULT_VAR}}) - check_end(${check_result}) -endfunction(kmodule_is_function_exist function_name RESULT_VAR) - -# Creates the list of functions that actually exist on the -# current system. -# -# kmodule_configure_kernel_functions(output_list -# {[REQUIRED | OPTIONAL] {func | ONE_OF_LIST}} ...) -# -# ONE_OF_LIST := ONE_OF_BEGIN {func ...} ONE_OF_END -# -# There are 2 modes of function lookup: -# OPTIONAL - if the function doesn't exist, it is silently ignored. -# REQUIRED - if the function doesn't exist, FATAL_ERROR message is printed. -# -# Initial mode is REQUIRED, and it can be changed at any time by REQUIRED -# and OPTIONAL keywords. -# -# ONE_OF_BEGIN/ONE_OF_END determine a section for which no more than one -# function among all listed there should exist. FATAL_ERROR message is -# printed otherwise. When mode is REQUIRED, precisely one function must -# exist. -# Inside this section other keywords must not be used (even another -# ONE_OF_BEGIN). - -function(kmodule_configure_kernel_functions output_list) - set(kmodule_configure_kernel_functions_mode "REQUIRED") - set(kmodule_configure_kernel_functions_one_of_section "FALSE") - set(output_list_tmp) - set(${output_list}) - foreach(arg ${ARGN}) - if(arg STREQUAL "REQUIRED" OR arg STREQUAL "OPTIONAL") - if(kmodule_configure_kernel_functions_one_of_section) - message(FATAL_ERROR -"Inside ONE_OF_BEGIN/ONE_OF_END section, other keywords are not allowed." - ) - endif(kmodule_configure_kernel_functions_one_of_section) - set(kmodule_configure_kernel_functions_mode ${arg}) - elseif(arg STREQUAL "ONE_OF_BEGIN") - if(kmodule_configure_kernel_functions_one_of_section) - message(FATAL_ERROR "Nested ONE_OF_BEGIN/ONE_OF_END sections are not allowed.") - endif(kmodule_configure_kernel_functions_one_of_section) - set(kmodule_configure_kernel_functions_one_of_section "TRUE") - set(kmodule_configure_kernel_functions_one_of_section_function) - elseif(arg STREQUAL "ONE_OF_END") - if(NOT kmodule_configure_kernel_functions_one_of_section) - message(FATAL_ERROR "ONE_OF_END without ONE_OF_BEGIN is not allowed.") - endif(NOT kmodule_configure_kernel_functions_one_of_section) - if(kmodule_configure_kernel_functions_one_of_section_function) - list(APPEND output_list_tmp ${kmodule_configure_kernel_functions_one_of_section_function}) - else(kmodule_configure_kernel_functions_one_of_section_function) - if(kmodule_configure_kernel_functions_mode STREQUAL "REQUIRED") - message(FATAL_ERROR -"None of the functions listed in ONE_OF section exist in the kernel but it is required." - ) - endif(kmodule_configure_kernel_functions_mode STREQUAL "REQUIRED") - endif(kmodule_configure_kernel_functions_one_of_section_function) - set(kmodule_configure_kernel_functions_one_of_section "FALSE") - else(arg STREQUAL "REQUIRED" OR arg STREQUAL "OPTIONAL") - set(kmodule_func_varname _KMODULE_IS_${arg}_EXIST) - kedr_find_function(${arg} ${kmodule_func_varname}) - if(kmodule_configure_kernel_functions_one_of_section) - if(${kmodule_func_varname}) - if(kmodule_configure_kernel_functions_one_of_section_function) - message(FATAL_ERROR "Two functions from ONE_OF sections exist in the kernel.") - else(kmodule_configure_kernel_functions_one_of_section_function) - set(kmodule_configure_kernel_functions_one_of_section_function ${arg}) - endif(kmodule_configure_kernel_functions_one_of_section_function) - endif(${kmodule_func_varname}) - else(kmodule_configure_kernel_functions_one_of_section) - if(${kmodule_func_varname}) - list(APPEND output_list_tmp ${arg}) - else(${kmodule_func_varname}) - if(kmodule_configure_kernel_functions_mode STREQUAL "REQUIRED") - message(FATAL_ERROR "Function ${arg} is not found in the kernel but it is required.") - endif(kmodule_configure_kernel_functions_mode STREQUAL "REQUIRED") - endif(${kmodule_func_varname}) - endif(kmodule_configure_kernel_functions_one_of_section) - endif(arg STREQUAL "REQUIRED" OR arg STREQUAL "OPTIONAL") - endforeach(arg ${ARGN}) - if(kmodule_configure_kernel_functions_one_of_section) - message(FATAL_ERROR "Found ONE_OF_BEGIN without ONE_OF_END") - endif(kmodule_configure_kernel_functions_one_of_section) - set(${output_list} ${output_list_tmp} PARENT_SCOPE) -endfunction(kmodule_configure_kernel_functions output_list) - -############################################################################ -# Utility macros to check for particular features. If the particular feature -# is supported, the macros will set the corresponding variable to TRUE, -# otherwise - to FALSE (the name of variable is mentioned in the comments -# for the macro). -############################################################################ - -# Check if the system has everything necessary to build at least simple -# kernel modules. -# The macro sets variable 'MODULE_BUILD_SUPPORTED'. -macro(check_module_build) - check_begin("Checking if kernel modules can be built on this system") - if (NOT MODULE_BUILD_SUPPORTED) - check_try() - kbuild_try_compile(module_build_supported_impl - "${CMAKE_BINARY_DIR}/check_module_build" - "${kmodule_test_sources_dir}/check_module_build/module.c" - ) - set_bool_string(MODULE_BUILD_SUPPORTED "yes" "no" ${module_build_supported_impl} - CACHE INTERNAL "Can kernel modules be built on this system?" - ) - endif (NOT MODULE_BUILD_SUPPORTED) - - check_end("${MODULE_BUILD_SUPPORTED}") - - if (NOT MODULE_BUILD_SUPPORTED) - message(FATAL_ERROR -"There are problems with building kernel modules on this system. " -"Please check that the appropriate kernel headers and build tools " -"are installed." - ) - endif (NOT MODULE_BUILD_SUPPORTED) -endmacro(check_module_build) - -# Check if reliable stack trace information can be obtained. -# This is the case, for example, if the kernel is compiled with support -# for frame pointers and/or stack unwind on. -# The macro sets variable 'STACK_TRACE_RELIABLE'. -macro(check_stack_trace) - check_begin("Checking if stack trace information is reliable") - if (NOT DEFINED STACK_TRACE_RELIABLE) - check_try() - kbuild_try_compile(stack_trace_reliable_impl - "${CMAKE_BINARY_DIR}/check_stack_trace" - "${kmodule_test_sources_dir}/check_stack_trace/module.c" - ) - set_bool_string(STACK_TRACE_RELIABLE "yes" "no" ${stack_trace_reliable_impl} - CACHE INTERNAL "Are stack traces reliable on this system?") - endif (NOT DEFINED STACK_TRACE_RELIABLE) - check_end("${STACK_TRACE_RELIABLE}") - - if (NOT STACK_TRACE_RELIABLE) - message ("\n[WARNING]\n" - "It looks like reliable stack traces cannot be obtained on this system.\n" - "The output of KEDR-based tools like LeakCheck will be less detailed\n" - "(each stack trace shown will contain only one frame).\n" - "If this is not acceptable, you could rebuild the kernel with\n" - "CONFIG_FRAME_POINTER or CONFIG_STACK_UNWIND (if available) set to \"y\"\n" - "and then reconfigure and rebuild KEDR.\n") - endif (NOT STACK_TRACE_RELIABLE) -endmacro(check_stack_trace) - -# Check whether ring buffer is implemented by the kernel. -# Set cache variable RING_BUFFER_IMPLEMENTED according to this checking. -function(check_ring_buffer) - check_begin("Checking if ring buffer is implemented in the kernel") - - if (NOT DEFINED RING_BUFFER_IMPLEMENTED) - check_try() - kbuild_try_compile(ring_buffer_implemented_impl - "${CMAKE_BINARY_DIR}/check_ring_buffer" - "${kmodule_test_sources_dir}/check_ring_buffer/module.c" - ) - - set_bool_string(RING_BUFFER_IMPLEMENTED "yes" "no" "${ring_buffer_implemented_impl}" - CACHE INTERNAL "Whether ring buffer is implemented in the kernel" - ) - endif (NOT DEFINED RING_BUFFER_IMPLEMENTED) - check_end(${RING_BUFFER_IMPLEMENTED}) - - if (NOT RING_BUFFER_IMPLEMENTED) - message("\n[WARNING]\nRing buffer is not supported by the system.\n" - "The tracing facilities as well as call monitoring plugins will not be built.\n" - "If this is not acceptable, you could rebuild the kernel with\n" - "CONFIG_RING_BUFFER set to \"y\" and then reconfigure and rebuild KEDR.\n") - endif (NOT RING_BUFFER_IMPLEMENTED) -endfunction(check_ring_buffer) - -# Check which memory allocator is used by the kernel. -# Set KERNEL_MEMORY_ALLOCATOR to 'slab', 'slub', 'slob' or 'other'. -# -# Some functions in that allocators may have same names, but different signatures. -function(check_allocator) - check_begin("Checking which memory allocator is used by the kernel") - if(NOT DEFINED KERNEL_MEMORY_ALLOCATOR) - check_try() - # Use local variable for detect allocator, - # Cache one will be set at the end - set(allocator "") - if(allocator STREQUAL "") - kbuild_try_compile(is_allocator_slab - "${CMAKE_BINARY_DIR}/check_allocator_slab" - "${kmodule_test_sources_dir}/check_allocator/module.c" - KBUILD_COMPILE_DEFINITIONS "-DIS_ALLOCATOR_SLAB" - ) - if (is_allocator_slab) - set(allocator "slab") - endif (is_allocator_slab) - endif(allocator STREQUAL "") - - if(allocator STREQUAL "") - kbuild_try_compile(is_allocator_slub - "${CMAKE_BINARY_DIR}/check_allocator_slub" - "${kmodule_test_sources_dir}/check_allocator/module.c" - KBUILD_COMPILE_DEFINITIONS "-DIS_ALLOCATOR_SLUB" - ) - if (is_allocator_slub) - set(allocator "slub") - endif (is_allocator_slub) - endif(allocator STREQUAL "") - - if(allocator STREQUAL "") - kbuild_try_compile(is_allocator_slob - "${CMAKE_BINARY_DIR}/check_allocator_slob" - "${kmodule_test_sources_dir}/check_allocator/module.c" - KBUILD_COMPILE_DEFINITIONS "-DIS_ALLOCATOR_SLOB" - ) - if (is_allocator_slob) - set(allocator "slob") - endif (is_allocator_slub) - endif(allocator STREQUAL "") - - if(allocator STREQUAL "") - set(allocator "other") - endif(allocator STREQUAL "") - - set(KERNEL_MEMORY_ALLOCATOR "${allocator}" CACHE INTERNAL - "Memory allocator which is used by the kernel" - ) - endif (NOT DEFINED KERNEL_MEMORY_ALLOCATOR) - check_end("${KERNEL_MEMORY_ALLOCATOR}") -endfunction(check_allocator) - -# Check if 'kfree_rcu' is available in the kernel (it is likely to be -# a macro or an inline). If it is available, we should handle it as -# 'free' in LeakCheck. As KEDR cannot normally intercept kfree_rcu() -# itself, it needs to intercept call_rcu/call_rcu_sched and check their -# arguments. -# The macro sets variable 'HAVE_KFREE_RCU'. -macro(check_kfree_rcu) - check_begin("Checking if kfree_rcu() is available") - - if (NOT DEFINED HAVE_KFREE_RCU) - check_try() - kbuild_try_compile(have_kfree_rcu_impl - "${CMAKE_BINARY_DIR}/check_kfree_rcu" - "${kmodule_test_sources_dir}/check_kfree_rcu/module.c" - ) - - set_bool_string(HAVE_KFREE_RCU "yes" "no" ${have_kfree_rcu_impl} - CACHE INTERNAL "Is kfree_rcu() available?" - ) - endif () - check_end("${HAVE_KFREE_RCU}") -endmacro(check_kfree_rcu) -############################################################################ - -# Check if posix_acl_from_xattr() accepts struct user_namespace as the -# first argument. -# The macro sets variable 'POSIX_ACL_XATTR_HAS_USER_NS'. -macro(check_xattr_user_ns) - check_begin("Checking if posix_acl_from_xattr() has struct user_namespace * argument") - if (NOT DEFINED POSIX_ACL_XATTR_HAS_USER_NS) - check_try() - kbuild_try_compile(have_xattr_user_ns_impl - "${CMAKE_BINARY_DIR}/check_xattr_user_ns" - "${kmodule_test_sources_dir}/check_xattr_user_ns/module.c" - ) - - set_bool_string(POSIX_ACL_XATTR_HAS_USER_NS "yes" "no" ${have_xattr_user_ns_impl} - CACHE INTERNAL "Does posix_acl_from_xattr() have struct user_namespace argument?" - ) - endif () - check_end("${POSIX_ACL_XATTR_HAS_USER_NS}") -endmacro(check_xattr_user_ns) -############################################################################ - -# Check if hlist_for_each_entry*() macros accept only 'type *pos' argument -# rather than both 'type *tpos' and 'hlist_node *pos' as the loop cursors. -# The macro sets variable 'HLIST_FOR_EACH_ENTRY_POS_ONLY'. -macro(check_hlist_for_each_entry) - check_begin("Checking the signatures of hlist_for_each_entry*() macros") - if (NOT DEFINED HLIST_FOR_EACH_ENTRY_POS_ONLY) - check_try() - kbuild_try_compile(pos_only_impl - "${CMAKE_BINARY_DIR}/check_hlist_for_each_entry" - "${kmodule_test_sources_dir}/check_hlist_for_each_entry/module.c" - ) - set_bool_string(HLIST_FOR_EACH_ENTRY_POS_ONLY "yes" "no" ${pos_only_impl} - CACHE INTERNAL - "Do hlist_for_each_entry*() macros have only 'type *pos' to use as a loop cursor?" - ) - endif () - set_bool_string(check_result "do not use additional loop cursor" "use additional loop cursor" - ${HLIST_FOR_EACH_ENTRY_POS_ONLY}) - check_end(${check_result}) -endmacro(check_hlist_for_each_entry) -############################################################################ - -# Check if 'random32' is available in the kernel. -# The macro sets variable 'KEDR_HAVE_RANDOM32'. -macro(check_random32) - check_begin("Checking if random32() is available") - if (NOT DEFINED KEDR_HAVE_RANDOM32) - check_try() - kbuild_try_compile(have_random32_impl - "${CMAKE_BINARY_DIR}/check_random32" - "${kmodule_test_sources_dir}/check_random32/module.c" - ) - set_bool_string(KEDR_HAVE_RANDOM32 "yes" "no" "${have_random32_impl}" - CACHE INTERNAL "Is random32() available?" - ) - endif () - check_end("${KEDR_HAVE_RANDOM32}") -endmacro(check_random32) -############################################################################ - -# The payload modules should call kedr_find_function() for each kernel -# function they would like to process. If the function is not known -# to KEDR (absent from the "function database", see "functions/" directory), -# kedr_find_function() issues a fatal error. -# -# 'func_name' is the name of the function, -# 'found_var' is the name of the output variable (the variable will evaluate -# as "true" if found, as "false" otherwise). -function (kedr_find_function func_name found_var) - if (NOT DEFINED KEDR_DATA_FILE_${func_name}) - message(FATAL_ERROR "Unsupported function: ${func_name}") - endif (NOT DEFINED KEDR_DATA_FILE_${func_name}) - - # OK, known function. - kmodule_is_function_exist(${func_name} ${found_var}) - if (${found_var}) - # Mark the function as used by at least one payload module. - # This can be used when preparing the tests for call interception. - # The actual value does not matter, it only matters that this - # variable is defined. - set(KEDR_FUNC_USED_${func_name} "yes" CACHE INTERNAL - "Is kernel function ${func_name} used by any payload module?") - set(${found_var} "${${found_var}}" PARENT_SCOPE) - else () - set(${found_var} "NO" PARENT_SCOPE) - endif (${found_var}) -endfunction (kedr_find_function func_name found_var) - -# Returns (in ${path_var}) the path to the .data file for the function -# ${func_name}. The function must be present in the system and -# kedr_find_function() must be called for it before kedr_get_data_for_func(). -function (kedr_get_data_for_func func_name path_var) - if (NOT DEFINED KEDR_FUNC_USED_${func_name}) - message(FATAL_ERROR -"Attempt to lookup data for a function that is not marked as used: " - "${func_name}") - endif (NOT DEFINED KEDR_FUNC_USED_${func_name}) - - set(${path_var} "${KEDR_DATA_FILE_${func_name}}" PARENT_SCOPE) -endfunction (kedr_get_data_for_func func_name path_var) - -# kedr_get_header_data_list(list_var func1 [func2 func3 ...]) -# Get the list of paths to the 'header.data' files for the given functions. -# The functions must be present in the system and # kedr_find_function() -# must be called for each of them before kedr_get_header_data_list() is -# called. -# The resulting list is returned in ${list_var}. It will contain at least -# one item. It will not contain duplicates. -function (kedr_get_header_data_list list_var) - set(hdata_list) - foreach (func ${ARGN}) - if (NOT DEFINED KEDR_FUNC_USED_${func}) - message(FATAL_ERROR -"Attempt to lookup header data for a function that is not marked as used: " - "${func}") - endif (NOT DEFINED KEDR_FUNC_USED_${func}) - list(APPEND hdata_list "${KEDR_HEADER_DATA_FILE_${func}}") - endforeach () - list(REMOVE_DUPLICATES hdata_list) - - list(LENGTH hdata_list hdata_list_len) - if (NOT hdata_list_len) - message(FATAL_ERROR - "kedr_get_header_data_list(): BUG: the output list is empty.") - endif (NOT hdata_list_len) - - set(${list_var} ${hdata_list} PARENT_SCOPE) -endfunction (kedr_get_header_data_list list_var) - -# kedr_create_header_rules( -# header_impl_file header_data_file func1 [func2 ...]) -# Create the rules to generate ${header_impl_file} from -# ${header_data_file} and the specific header files for the given -# functions. -function(kedr_create_header_rules header_impl_file header_data_file - functions) - set(hlist_data) - kedr_get_header_data_list(hlist_data ${functions} ${ARGN}) - to_abs_path(hdata_files_abs ${header_data_file} ${hlist_data}) - add_custom_command(OUTPUT ${header_impl_file} - COMMAND cat ${hdata_files_abs} > ${header_impl_file} - DEPENDS ${hdata_files_abs} - ) -endfunction(kedr_create_header_rules header_impl_file header_data_file - functions) - -# kedr_create_data_rules(func) -# Create the rules to generate ${func}_impl.data from -# ${func}.data (contains processing instructions specific to the current -# payload) and the .data file from "func_db" (contains function signature). -function(kedr_create_data_rules func) - set(func_db_data_file) - kedr_get_data_for_func(${func} func_db_data_file) - - set(func_proc_data_file) - to_abs_path(func_proc_data_file ${func}.data) - - add_custom_command(OUTPUT "${func}_impl.data" - COMMAND printf "\"[group]\\n\"" > "${func}_impl.data" - COMMAND grep -E -v '\\[group\\]' - "${func_db_data_file}" >> "${func}_impl.data" - COMMAND printf "\"\\n\"" >> "${func}_impl.data" - COMMAND grep -E -v '\\[group\\]' - "${func_proc_data_file}" >> "${func}_impl.data" - DEPENDS - "${func_proc_data_file}" - "${func_db_data_file}" - ) -endfunction(kedr_create_data_rules func) - -# kedr_create_payload_module(module_name payload_data_file template_dir) -# Create the rules to build a payload module with the given name. -# 'payload_data_file' defines how the kernel functions are to be processed -# by this payload module. -# 'template_dir' - path to the direcotry containing the templates to be used -# to prepare the source code of the module from 'payload_data_file'. -function(kedr_create_payload_module module_name payload_data_file - template_dir) - - # Rules to build the module - kbuild_add_module(${module_name} - "payload.c" - "functions_support.c" - ) - kbuild_link_module(${module_name} kedr) - - # Rules to obtain the source files of the module - kedr_generate("payload.c" ${payload_data_file} "${template_dir}") - kedr_generate("functions_support.c" ${payload_data_file} "${KEDR_GEN_TEMPLATES_DIR}/functions_support.c") -endfunction(kedr_create_payload_module module_name payload_data_file - template_dir) - -# kedr_create_payload_data(payload_data_file func1 [func2 ...]) -# Create .data files and the header data files for the functions and prepare -# the main .data file (${payload_data_file}) for the payload module. -function(kedr_create_payload_data header_data_file payload_data_file - functions) - set(header_impl_file "header_impl.data") - set(functions_data) - - foreach(func ${functions} ${ARGN}) - list(APPEND functions_data "${func}_impl.data") - kedr_create_data_rules(${func}) - endforeach(func ${functions} ${ARGN}) - - kedr_create_header_rules(${header_impl_file} ${header_data_file} - ${functions} ${ARGN}) - - to_abs_path(payload_data_file_abs ${payload_data_file}) - to_abs_path(source_files_abs ${header_impl_file} ${functions_data}) - - set(payload_data_file_abs "${CMAKE_CURRENT_BINARY_DIR}/${payload_data_file}") - add_custom_command(OUTPUT ${payload_data_file_abs} - COMMAND cat ${source_files_abs} > ${payload_data_file_abs} - DEPENDS ${source_files_abs} - ) -endfunction(kedr_create_payload_data header_data_file payload_data_file - functions) -############################################################################ From 6313c71b83da0e978cb895800d37b1562d078ec4 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 22 Dec 2025 19:44:42 +0100 Subject: [PATCH 18/23] remove unused iq_txshift and iq_rxrescale variables improve comments in usrp_lib.cpp --- openair1/PHY/TOOLS/calibration_test.c | 4 ---- radio/BLADERF/bladerf_lib.c | 4 ---- radio/COMMON/common_lib.h | 4 ---- radio/ETHERNET/ethernet_lib.c | 34 --------------------------- radio/USRP/usrp_lib.cpp | 27 ++++++++++----------- 5 files changed, 12 insertions(+), 61 deletions(-) diff --git a/openair1/PHY/TOOLS/calibration_test.c b/openair1/PHY/TOOLS/calibration_test.c index 4d88ab542a..8007040499 100644 --- a/openair1/PHY/TOOLS/calibration_test.c +++ b/openair1/PHY/TOOLS/calibration_test.c @@ -108,10 +108,6 @@ int main(int argc, char **argv) { .sdr_addrs=usrp_addrs, //! Auto calibration flag .autocal={0}, - //! rf devices work with x bits iqs when oai have its own iq format - //! the two following parameters are used to convert iqs - .iq_txshift=0, - .iq_rxrescale=0, //! Configuration file for LMS7002M .configFilename="", //! remote IP/MAC addr for Ethernet interface diff --git a/radio/BLADERF/bladerf_lib.c b/radio/BLADERF/bladerf_lib.c index 178fc2fff2..bff12bee8e 100644 --- a/radio/BLADERF/bladerf_lib.c +++ b/radio/BLADERF/bladerf_lib.c @@ -957,8 +957,6 @@ int device_init(openair0_device *device, /* device specific */ //openair0_cfg->txlaunch_wait = 1;//manage when TX processing is triggered //openair0_cfg->txlaunch_wait_slotcount = 1; //manage when TX processing is triggered - openair0_cfg->iq_txshift = 0;// shift - openair0_cfg->iq_rxrescale = 15;//rescale iqs // init required params switch ((int)openair0_cfg->sample_rate) { @@ -983,8 +981,6 @@ int device_init(openair0_device *device, exit(-1); break; } - openair0_cfg->iq_txshift= 0; - openair0_cfg->iq_rxrescale = 15; /*not sure*/ openair0_cfg->rx_gain_calib_table = calib_table_fx4; // The number of buffers to use in the underlying data stream diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index b87a6a8abf..5da2b13c84 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -274,10 +274,6 @@ typedef struct openair0_config { char *sdr_addrs; //! Auto calibration flag int autocal[8]; - //! rf devices work with x bits iqs when oai have its own iq format - //! the two following parameters are used to convert iqs - int iq_txshift; - int iq_rxrescale; //! Configuration file for LMS7002M char *configFilename; //! remote IP/MAC addr for Ethernet interface diff --git a/radio/ETHERNET/ethernet_lib.c b/radio/ETHERNET/ethernet_lib.c index 2a4d021afc..038246c998 100644 --- a/radio/ETHERNET/ethernet_lib.c +++ b/radio/ETHERNET/ethernet_lib.c @@ -540,40 +540,6 @@ int transport_init(openair0_device *device, eth->if_name = eth_params->local_if_name; device->priv = eth; - - - /* device specific */ - // openair0_cfg[0].iq_rxrescale = 15;//rescale iqs - // openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift - // openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance; - - /* RRU does not have any information to make this configuration atm */ - /* - if (device->host_type == RAU_HOST) { - - switch ((int)openair0_cfg[0].sample_rate) { - case 30720000: - openair0_cfg[0].samples_per_packet = 3840; - break; - case 23040000: - openair0_cfg[0].samples_per_packet = 2880; - break; - case 15360000: - openair0_cfg[0].samples_per_packet = 1920; - break; - case 7680000: - openair0_cfg[0].samples_per_packet = 960; - break; - case 1920000: - openair0_cfg[0].samples_per_packet = 240; - break; - default: - printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate); - exit(-1); - break; - } - }*/ - device->openair0_cfg=&openair0_cfg[0]; return 0; } diff --git a/radio/USRP/usrp_lib.cpp b/radio/USRP/usrp_lib.cpp index 923e938869..dabc6f8947 100644 --- a/radio/USRP/usrp_lib.cpp +++ b/radio/USRP/usrp_lib.cpp @@ -478,14 +478,15 @@ static int trx_usrp_write(openair0_device *device, nsamps2 = (nsamps+7)>>3; simde__m256i buff_tx[cc < 2 ? 2 : cc][nsamps2]; - // bring RX data into 12 LSBs for softmodem RX + // bring TX data into 16 MSBs, assuming it is on the 12 LSB after OAI computation + const int shift = 4; for (int i = 0; i < cc; i++) { for (int j = 0; j < nsamps2; j++) { if ((((uintptr_t)buff[i]) & 0x1F) == 0) { - buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[i])[j], 4); + buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[i])[j], shift); } else { simde__m256i tmp = simde_mm256_loadu_si256(((simde__m256i *)buff[i]) + j); - buff_tx[i][j] = simde_mm256_slli_epi16(tmp, 4); + buff_tx[i][j] = simde_mm256_slli_epi16(tmp, shift); } } } @@ -605,17 +606,17 @@ void *trx_usrp_write_thread(void * arg){ nsamps2 = (nsamps+7)>>3; simde__m256i buff_tx[cc < 2 ? 2 : cc][nsamps2]; - - // bring RX data into 12 LSBs for softmodem RX + // bring TX data into 16 MSBs, assuming it is on the 12 LSB after OAI computation + const int shift = 4; for (int i = 0; i < cc; i++) { for (int j = 0; j < nsamps2; j++) { if ((((uintptr_t) buff[i])&0x1F)==0) { - buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[i])[j], 4); + buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[i])[j], shift); } else { simde__m256i tmp = simde_mm256_loadu_si256(((simde__m256i *)buff[i]) + j); - buff_tx[i][j] = simde_mm256_slli_epi16(tmp,4); + buff_tx[i][j] = simde_mm256_slli_epi16(tmp, shift); } } } @@ -752,11 +753,13 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp // bring RX data into 12 LSBs for softmodem RX for (int i=0; i Date: Tue, 6 Jan 2026 09:28:16 +0100 Subject: [PATCH 19/23] fix(documentation): issue 1042 A merge conflict header was left by mistake in `openair1/PHY/CODING/DOC/LDPCImplementation.md` --- openair1/PHY/CODING/DOC/LDPCImplementation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index 0e4a630f4b..56e7570de4 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -40,7 +40,6 @@ Linking C shared module libldpc_aal.so At runtime, to successfully use LDPC accelerators (e.g., Xilinx T2/Intel ACCs), you will need to install the corresponding drivers and tools. Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_OFFLOAD_SETUP.md). -<<<<<<< HEAD ``` ./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _aal --nrLDPC_coding_aal.dpdk_dev 01:00.0 --nrLDPC_coding_aal.dpdk_core_list 0-1 ``` From e7bcca88bb30e65b8b5c324a8a9008eaaf000834 Mon Sep 17 00:00:00 2001 From: Rakesh B B Date: Tue, 6 Jan 2026 15:01:17 +0530 Subject: [PATCH 20/23] Add time units for better clarity and readability. --- openair2/COMMON/ngap_messages_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 76abc3783d..3bfd2e20a2 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -473,7 +473,7 @@ typedef struct { cell_id_t id; // Cell type uint8_t type; - // Time UE Stayed in Cell + // Time UE Stayed in Cell (seconds) uint16_t time_in_cell; // Cause ngap_cause_t *cause; From c382d78051f9123610761d433ba0e53d5a4df128 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Mon, 5 Jan 2026 21:07:00 +0100 Subject: [PATCH 21/23] CI: include VNF container in xNB log analysis --- ci-scripts/cls_containerize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 71e437676c..be42790a5c 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -209,7 +209,7 @@ def CheckLogs(self, filename, HTML, RAN): elif 'nv-cubb' in name: msg = 'Undeploy PNF/Nvidia CUBB' HTML.CreateHtmlTestRow(msg, 'OK', CONST.ALL_PROCESSES_OK) - elif (any(sub in name for sub in ['enb','rru','rcc','cu','du','gnb'])): + elif (any(sub in name for sub in ['enb','rru','rcc','cu','du','gnb','vnf'])): logging.debug(f'\u001B[1m Analyzing XnB logfile {filename}\u001B[0m') logStatus = RAN.AnalyzeLogFile_eNB(filename, HTML, self.ran_checkers) opt = f"xNB log analysis ({name})" From 1ff1cb12001e673c900e6e9db3b9dd0081f59071 Mon Sep 17 00:00:00 2001 From: Jaroslava Fiedlerova Date: Wed, 7 Jan 2026 12:08:21 +0100 Subject: [PATCH 22/23] Adjustment of nr_ulsim timing report --- openair1/SIMULATION/NR_PHY/ulsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index f2452221d6..18f6f3f605 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1742,7 +1742,7 @@ int main(int argc, char *argv[]) printStatIndent2(&gNB->rx_pusch_symbol_processing_stats, "RX PUSCH Symbol Processing time"); printStatIndent(&gNB->ulsch_decoding_stats,"ULSCH total decoding time"); printStatIndent2(&gNB->ts_deinterleave, "ULSCH segment deinterleaving time"); - printStatIndent2(&gNB->ts_rate_unmatch, "ULSCH segment rate recovery time"); + printStatIndent2(&gNB->ts_rate_unmatch, "ULSCH segment rate matching time"); printStatIndent2(&gNB->ts_ldpc_decode, "ULSCH segments decoding time"); printStatIndent(&gNB->rx_srs_stats,"RX SRS time"); printStatIndent2(&gNB->generate_srs_stats,"Generate SRS sequence time"); From 0ddf21db1209aee3c88d1bfbe10fb95eeb0d7c5b Mon Sep 17 00:00:00 2001 From: Mario Joa-Ng Date: Wed, 7 Jan 2026 21:31:48 +0100 Subject: [PATCH 23/23] support prach with non-zero start symbol. --- radio/fhi_72/oaioran.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/fhi_72/oaioran.c b/radio/fhi_72/oaioran.c index b614ae99b5..b0ed41decb 100644 --- a/radio/fhi_72/oaioran.c +++ b/radio/fhi_72/oaioran.c @@ -173,6 +173,9 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) struct xran_fh_init *fh_init = get_xran_fh_init(); struct xran_fh_config *fh_cfg = get_xran_fh_config(0); nr_prach_info_t prach_info = get_prach_info(0); + + int prach_start_sym = prach_info.start_symbol; + int prach_end_sym = prach_info.N_dur + prach_start_sym; struct xran_ru_config *ru_conf = &fh_cfg->ru_conf; int slots_per_frame = 10 << fh_cfg->frame_conf.nNumerology; int slots_per_subframe = 1 << fh_cfg->frame_conf.nNumerology; @@ -190,7 +193,7 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) LOG_W(HW, "we get rach data from ru, but it is not scheduled %d.%d\n", frame, slot); return -1; } - for (sym_idx = 0; sym_idx < prach_info.N_dur; sym_idx++) { + for (sym_idx = prach_start_sym; sym_idx < prach_end_sym; sym_idx++) { for (int aa = 0; aa < ru->nb_rx; aa++) { int16_t *dst, *src; int idx = 0; @@ -200,7 +203,7 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) src = (int16_t *)bufs->prachdstdecomp[aa % nb_rx_per_ru][tti % XRAN_N_FE_BUF_LEN].pBuffers[sym_idx].pData; /* convert Network order to host order */ if (ru_conf->compMeth_PRACH == XRAN_COMPMETHOD_NONE) { - if (sym_idx == 0) { + if (sym_idx == prach_start_sym) { for (idx = 0; idx < 139 * 2; idx++) { dst[idx] = ((int16_t)ntohs(src[idx + g_kbar])); } @@ -233,7 +236,7 @@ static int read_prach_data(ru_info_t *ru, int frame, int slot) AssertFatal(1 == 0, "BFP decompression not supported on this architecture"); #endif // note: this is hardwired for 139 point PRACH sequence, kbar=2 - if (sym_idx == 0) // + if (sym_idx == prach_start_sym) for (idx = 0; idx < (139 * 2); idx++) dst[idx] = local_dst[idx + g_kbar]; else