From 9d4e427099eb422e1632d4323b753357c8032e54 Mon Sep 17 00:00:00 2001 From: Sreenivasa Reddy Date: Sun, 24 Nov 2024 18:26:47 +0000 Subject: [PATCH] gh #44 adding cec commands and responses --- host/.lastlogin | 0 host/activate_venv.sh | 35 +++ host/install.sh | 170 +++++++++++ host/tests/classes/hdmiCEC.py | 272 ++++++++++++++++++ host/tests/classes/hdmiCEC_testConfig.yml | 66 +++++ host/tests/configs/deviceConfig.yml | 42 +++ host/tests/configs/example_rack_config.yml | 96 +++++++ .../hdmiCEC_L3_Tests/hdmiCECHelperClass.py | 149 ++++++++++ .../hdmiCEC_L3_Tests/hdmiCEC_L3_testSetup.yml | 35 +++ ...nsmitSingleStandbyCommandandValidateAck.py | 113 ++++++++ src/test_l3_hdmi_cec_sink_driver.c | 226 ++++++++++++++- 11 files changed, 1193 insertions(+), 11 deletions(-) create mode 100644 host/.lastlogin create mode 100644 host/activate_venv.sh create mode 100755 host/install.sh create mode 100644 host/tests/classes/hdmiCEC.py create mode 100644 host/tests/classes/hdmiCEC_testConfig.yml create mode 100644 host/tests/configs/deviceConfig.yml create mode 100644 host/tests/configs/example_rack_config.yml create mode 100755 host/tests/hdmiCEC_L3_Tests/hdmiCECHelperClass.py create mode 100755 host/tests/hdmiCEC_L3_Tests/hdmiCEC_L3_testSetup.yml create mode 100755 host/tests/hdmiCEC_L3_Tests/hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck.py diff --git a/host/.lastlogin b/host/.lastlogin new file mode 100644 index 0000000..e69de29 diff --git a/host/activate_venv.sh b/host/activate_venv.sh new file mode 100644 index 0000000..d911224 --- /dev/null +++ b/host/activate_venv.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2023 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** +MY_PATH="$(realpath ${BASH_SOURCE[0]})" +MY_DIR="$(dirname ${MY_PATH})" +VENV_DIR="${MY_DIR}/python_venv" # Default virtual environment directory name + +# Check if the script is sourced +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + source "$VENV_DIR"/bin/activate + echo "Virtual environment '$VENV_DIR' activated." +else + echo "The script must be sourced. 'source ./activate_venv.sh' or '. ./activate_venv.sh'" + echo "Once activated you can deactivate with 'deactivate' command" +fi + diff --git a/host/install.sh b/host/install.sh new file mode 100755 index 0000000..9a5ff8a --- /dev/null +++ b/host/install.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2023 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** +MY_PATH="$(realpath ${BASH_SOURCE[0]})" +MY_DIR="$(dirname ${MY_PATH})" +TESTS_DIR="${MY_DIR}/tests" +RAFT_DIR="${TESTS_DIR}/raft" +PLUGINS_FRAMEWORK_DIR="${RAFT_DIR}/framework/plugins" +VENV_NAME="python_venv" +VENV_DIR="${MY_DIR}/${VENV_NAME}" # Default virtual environment directory name + +mkdir -p ${TESTS_DIR} + +NO_COLOR="\e[0m" +RED="\e[0;31m" +CYAN="\e[0;36m" +YELLOW="\e[1;33m" +GREEN="\e[0;32m" +RED_BOLD="\e[1;31m" +BLUE_BOLD="\e[1;34m" +YELLOW_BOLD="\e[1;33m" + +set -e # return on errors + +function DUMP_VAR() +{ + local var_name="$1" + local var_content="${!var_name}" + echo -e ${CYAN}$var_name:[${var_content}]${NO_COLOR} +} +function ECHO() +{ + echo -e "$*" +} + +function DEBUG() +{ + # if set -x is in use debug messages are useless as whole stript will be shown + if [[ "$-" =~ "x" ]]; then + return + fi + if [[ "${DEBUG_FLAG}" == "1" ]];then + ECHO "${BLUE_BOLD}DEBUG: ${CYAN}$*${NO_COLOR}" > /dev/stderr + fi +} + +function INFO() +{ + ECHO "${GREEN}$*${NO_COLOR}" +} + +function WARNING() +{ + ECHO "${YELLOW_BOLD}Warning: ${YELLOW}$*${NO_COLOR}" > /dev/stderr +} + +function ERROR() +{ + ECHO "${RED_BOLD}ERROR: ${RED}$*${NO_COLOR}" + exit 1 +} + +function install_pip_requirements() +{ + local requirements_file="$1" + + if [ ! -f ${requirements_file} ]; then + WARNING "No ${requirements_file} found" + return # Exit the function if the file exists + fi + + INFO "install_pip_requirements( ${requirements_file} ):" + if pip install -r "$requirements_file" >/dev/null 2>&1; then + INFO "pip install completed" + else + ERROR "process_and_update_sha(): pip install failed." + return 1 # Exit the function with error code + fi +} + +function clone_repo() +{ + # Requirment it to clone only if not present. + local repo_url="$1" + local path="$2" + local version="$3" + local message="$4" + + if [[ -z "${repo_url}" ]]; then + ERROR "clone_repo:A url for a repository must be passed to the clone repo function" + fi + if [[ -z "${version}" ]]; then + ERROR "clone_repo:Version not specified" + fi + if [[ ! -z "${path}" ]]; then + if [[ ! -d "${path}" ]]; then + INFO "git clone ${repo_url} @ ${version} ${CYAN}${message}${NO_COLOR}" + git clone ${repo_url} "${path}" > /dev/null 2>&1 + cd ${path} + #INFO "git checkout ${version}" + git checkout ${version} > /dev/null 2>&1 + cd - > /dev/null + fi + fi +} + +function setup_and_enable_venv() +{ + # Check if virtual environment directory exists, create if not + if [[ ! -d "$VENV_DIR" ]]; then + ECHO "Creating Virtual environment ${YELLOW}'$VENV_NAME'${NO_COLOR}" + python3 -m venv "$VENV_DIR" + ECHO "Virtual environment created." + fi + + # Request that the user re-run this script from the vendor + # Check if already inside a virtual environment + if [[ ! -n "$VIRTUAL_ENV" ]]; then + # Activate virtual environment + ECHO "please source & re-run ${YELLOW}install.sh${NO_COLOR} to ensure setup:" + ECHO ${YELLOW}"source ./activate_venv.sh"${NO_COLOR} + exit 1 # Exit the function if already in a venv + fi + + if [ -f "${VENV_DIR}/.installed" ]; then + return + fi + + # Upgrade pip + #python3 -m pip install --upgrade pip + #echo "pip upgraded within the virtual environment." + + touch ${VENV_DIR}/.installed +} + + +## Setup and start venv +setup_and_enable_venv + +### Clone required repos ### +# Setup raft +clone_repo git@github.com:rdkcentral/python_raft.git "${RAFT_DIR}" "1.1.1" "in ./raft" +install_pip_requirements "${RAFT_DIR}"/requirements.txt + +# Setup ut-raft +clone_repo git@github.com:rdkcentral/ut-raft.git "${PLUGINS_FRAMEWORK_DIR}/ut_raft" 2.0.0 "in ./raft/framework/plugins" +install_pip_requirements "${PLUGINS_FRAMEWORK_DIR}"/ut_raft/requirements.txt + +## Install your own sub git repo's in here as required +# +INFO "Install Complete" diff --git a/host/tests/classes/hdmiCEC.py b/host/tests/classes/hdmiCEC.py new file mode 100644 index 0000000..b61f3ee --- /dev/null +++ b/host/tests/classes/hdmiCEC.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2024 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + +import subprocess +import os +import sys +from enum import Enum, auto +import re +import yaml + +# Add parent directory to the system path for module imports +dir_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(dir_path, "../")) + +from raft.framework.plugins.ut_raft.configRead import ConfigRead +from raft.framework.plugins.ut_raft.utSuiteNavigator import UTSuiteNavigatorClass +from raft.framework.plugins.ut_raft.interactiveShell import InteractiveShell +from raft.framework.plugins.ut_raft.utBaseUtils import utBaseUtils + +class hdmiCECClass(): + """ + HDMI CEC Class. + + This module provides common functionalities and extensions for the HDMI CEC Module. + """ + + def __init__(self, moduleConfigProfileFile :str, session=None, targetWorkspace="/tmp"): + """ + Initializes the HDMI CEC Class instance with configuration settings. + + Args: + moduleConfigProfileFile (str): Path to the device profile configuration file. + session: Optional; session object for the user interface. + + Returns: + None + """ + self.moduleName = "hdmiCEC" + self.testConfigFile = os:.path.join(dir_path, "hdmiCEC_testConfig.yml") + self.testSuite = "L3 hdmicec - Sink" + + # Load configurations for device profile and menu + self.moduleConfigProfile = ConfigRead( moduleConfigProfileFile , self.moduleName) + self.testConfig = ConfigRead(self.testConfigFile, self.moduleName) + self.testConfig.test.execute = os.path.join(targetWorkspace, self.testConfig.test.execute) + self.utMenu = UTSuiteNavigatorClass(self.testConfig, None, session) + self.testSession = session + self.utils = utBaseUtils() + self.ports = self.moduleConfigProfile.fields.get("Ports") + + for artifact in self.testConfig.test.artifacts: + filesPath = os.path.join(dir_path, artifact) + self.utils.rsync(self.testSession, filesPath, targetWorkspace) + + # Start the user interface menu + self.utMenu.start() + + def searchPattern(self, haystack, pattern): + """ + Searches for the first occurrence of a specified pattern in the provided string. + + Args: + haystack (str): The string to be searched. + pattern (str): The regular expression pattern to search for. + + Returns: + str: The first capturing group of the match if found; otherwise, None. + + Notes: + - The pattern should contain at least one capturing group (parentheses). + - If no match is found, None is returned. + """ + match = re.search(pattern, haystack) + if match: + return match.group(1) + return None + + def initialise(self): + """ + Initializes the HDMI CEC module for sink. + + Args: + None. + + Returns: + None + """ + result = self.utMenu.select( self.testSuite, "L3_Init_HdmiCec") + + def terminate(self): + """ + Terminates the hdmi cec module + + Args: + None + + Returns: + None + """ + result = self.utMenu.select(self.testSuite, "L3_Close_HdmiCec_Sink") + + def addLogicalAddress(self, logicalAddress:int): + """ + Adding the logical address of a specific device. + For now Sink to support only the logical address 0. + + Args: + logicalAddress (int): The Logical address of the DUT. This will be fixed to zero for a sink device for now. + + Returns: + None + """ + promptWithAnswers = [ + { + "query_type": "direct", + "query": "Enter Logical Address:", + "input": str(logicalAddress) + } + ] + + + result = self.utMenu.select(self.testSuite, "L3_AddLogicalAddress", promptWithAnswers) + + def removeLogicalAddress(self, logicalAddress:int): + """ + Remove logical address. + + Args: + logicalAddress (int): The Logical address of the DUT that should be removed. + + Returns: + None + """ + promptWithAnswers = [ + { + "query_type": "direct", + "query": "Enter Logical Address to Remove:", + "input": str(logicalAddress) + } + ] + + result = self.utMenu.select(self.testSuite, "L3_RemoveLogicalAddressHdmiCec_Sink", promptWithAnswers) + + + + def getLogicalAddress(self): + """ + Retrieves the Logical Address of the DUT. + + Args: + None. + + Returns: + int: Logical address of the device. + """ + result = self.utMenu.select( self.testSuite, "L3_GetLogicalAddress") + connectionStatusPattern = r"HdmiCecGetLogicalAddress\(IN: handle: [.*\], OUT: logicalAddress: [.*\]), status: [.*\])" + logicalAddress = self.searchPattern(result, connectionStatusPattern) + + return logicalAddress + + + def getPhysicalAddress(self): + """ + Retrieve the Physical Address of the DUT. + + Args: + None. + + Returns: + int: Physical Address of the DUT. + """ + + + result = self.utMenu.select( self.testSuite, "L3_GetPhyiscalAddress") + typeStatusPattern = r"HdmiCecGetPhysicalAddress\(IN: handle: [.*\], physicalAddress: [.*\]), status:[.*\]" + physicalAddress = self.searchPattern(result, typeStatusPattern) + + return physicalAddress + + def cecTransmitCmd(self, sourceLogicalAddress:int, destLogicalAddress:int, cecCommand:int, cecData:list=None): + """ + Transmit/Broadcast the CEC command and data to the respective destination. + + Args: + None. + + Returns: + None + """ + + promptWithAnswers = [ + { + "query_type": "direct", + "query": "Enter a valid Source Logical Address:", + "input": str(sourceLogicalAddress) + }, + { + "query_type": "direct", + "query": "Enter a valid Destination Logical Address:", + "input": str(destLogicalAddress) + }, + { + "query_type": "direct", + "query": "Enter CEC Command:", + "input": str(cecCommand) + }, + ] + + if cecData: + for byte in cecData: + promptWithAnswers.append( + { + "query_type": "direct", + "query": "Enter Databyte", + "input": str(byte) + }) + + result = self.utMenu.select( self.testSuite, "L3_TransmitCecCommand",promptWithAnswers) + + + def __del__(self): + """ + Cleans up and de-initializes the hdmi cec helper by stopping the test menu. + + Args: + None. + + Returns: + None + """ + self.utMenu.stop() + +# Test and example usage code +if __name__ == '__main__': + + shell = InteractiveShell() + shell.open() + + platformProfile = dir_path + "/../../../profiles/sink/Sink_AudioSettings.yaml" + # test the class assuming that it's optional + test = dsAudioClass(platformProfile, shell) + + test.initialise() + ports = test.getSupportedPorts() + + test.enablePort(ports[0][0], ports[0][1]) + test.disablePort(ports[0][0], ports[0][1]) + + test.terminate() + + shell.close() diff --git a/host/tests/classes/hdmiCEC_testConfig.yml b/host/tests/classes/hdmiCEC_testConfig.yml new file mode 100644 index 0000000..baa8d23 --- /dev/null +++ b/host/tests/classes/hdmiCEC_testConfig.yml @@ -0,0 +1,66 @@ +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2024 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** +hdmiCEC: + description: "hdmi CEC testing profile / menu system for UT" + test: + artifacts: + #List of artifacts folders, test class copies the content of folder to the target device workspace + - "../../../bin/" + # exectute command, this will appended with the target device workspace path + execute: "run.sh" + type: UT-C # C (UT-C Cunit) / C++ (UT-G (g++ ut-core gtest backend)) + suites: + 0: + name: "L1 hdmicec" + tests: + - "open_Positive" + - "open_negative" + - "close_Positive" + - "close_negative" + - "getPhysicalAddress_Positive" + - "getPhysicalAddress_negative" + - "setRxCallback_Positive" + - "setRxCallback_negative" + - "addLogicalAddress_Positive" + - "addLogicalAddress_negative" + - "removeLogicalAddress_Positive" + - "removeLogicalAddress_negative" + 1: + name: "L2 hdmicec - Sink" + tests: + - "L2_GetDefaultLogicalAddress_Sink" + - "L2_AddAndGetLogicalAddress_Sink" + - "L2_RemoveLogicalAddress_Sink" + - "L2_BroadcastHdmiCecCommand_Sink" + - "L2_VerifyPhysicalAddress_Sink" + - "L2_TransmitCECCommand_Sink" + 2: + name: "L3 hdmicec - Sink" + tests: + - "L3_Init_HdmiCec" + - "L3_AddLogicalAddress" + - "L3_GetLogicalAddress" + - "L3_TransmitCecCommand" + - "L3_ReceiveCecCommand" + - "L3_GetPhyiscalAddress" + - "L3_RemoveLogicalAddressHdmiCec_Sink" + - "L3_Close_HdmiCec_Sink" diff --git a/host/tests/configs/deviceConfig.yml b/host/tests/configs/deviceConfig.yml new file mode 100644 index 0000000..b7dafee --- /dev/null +++ b/host/tests/configs/deviceConfig.yml @@ -0,0 +1,42 @@ +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2023 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + +# The information defined in this config is consistent across all devices of a type. + +deviceConfig: + cpe1: + platform: "element" + model: "uk" + soc_vendor: "amlogic" + target_directory: "/tmp/" # Target Directory on device + prompt: "" # Prompt string on console + test: + #TODO: Use the single profile file which contains all details (ds, hdmi, etc) + profile: "../../../../profiles/sink/sink_hdmiCEC_test.yaml" + streams_download_url: "" #URL path from which the streams are downloaded to the device + cpe2: + platform: "test" + model: "test" + vendor: "test" + target_directory: "/tmp" + profile: "" + test: diff --git a/host/tests/configs/example_rack_config.yml b/host/tests/configs/example_rack_config.yml new file mode 100644 index 0000000..815a440 --- /dev/null +++ b/host/tests/configs/example_rack_config.yml @@ -0,0 +1,96 @@ +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2023 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + +# This config file creates your environment, defaults are setup internally unless otherwise overridden. +# you will need at least 1 rack and 1 slot to operate. +# each module e.g. console options, will be off by default unless otherwise stated + +# Depreciated +# slotx: address: IP of device while running locally, replaced with slotx: ip +# slotx: deviceConsole: IP of device while running locally, replaced with slotx: devices + +# This example is taken from https://github.com/rdkcentral/python_raft/blob/master/examples/configs/example_rack_config.yml +# For information on python_raft please refer to : https://github.com/rdkcentral/python_raft/blob/master/README.md + +# Data that is global to all tests. +globalConfig: + includes: + # [ includes: ] + # [ deviceConfig: "required.yml file" ] + deviceConfig: "example_device_config.yml" + capture: + # [capture: optional] + # [ocrEnginePath: "/usr/bin/tesseract"] # "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (For Windows) - tesseract binary + # [resolution: "1080p"] - Capture resolution + # [input: 0] - which input is connected to the video path + # Note: Video capture will not be installed unless screenRegions: is defined in deviceConfig: + ocrEnginePath: "/usr/bin/tesseract" # "C:\\Program Files\\Tesseract-OCR\\tesseract.exe" (For Windows) + resolution: "1080p" + input: 0 + cec-adaptor: + type: cec-client + adaptor: /dev/ttyACM0 + local: + log: # log for each slot + directory: "./logs" + delimiter: "/" + +# Define racks, their slots and the devices in them. These are not always physical racks and slots. +rackConfig: + rack1: + name: "rack1" + description: "example config at my desk" + slot1: + # [ name: "required", description: "optional"] + name: "slot1" + devices: + # [ devices: ] + # [ type: "serial": port: "COM7" baudRate: "(default)115200" dataBits: "optional(8)" stopBits: "optional(1)" parity: "optional(None)" FlowControl: "optional(None)" ] + # [ type: "ssh": port: 22 username: "test" password: "test" ] + # [ type: "telnet": port: 23 username: "test" password: "test" ] + - dut: + ip: "127.0.0.1" # IP Address of the ADA Hub + description: "local PC" + platform: "PC" + consoles: + - default: + type: "ssh" + port: 22 + username: "srr07" + ip: "127.0.0.1" #IP address + password: '1234' + - ssh_player: + type: "ssh" + port: 22 + username: "srr07" + ip: "127.0.0.1" #IP address + password: '1234' + - ssh_hal_test: + type: "ssh" + port: 22 + username: "srr07" + ip: "127.0.0.1" #IP address + password: '1234' + outbound: + download_url: "http://localhost:8000/" # download location for the CPE device + httpProxy: # Local Proxy if required + workspaceDirectory: './logs/workspace' # Local working directory diff --git a/host/tests/hdmiCEC_L3_Tests/hdmiCECHelperClass.py b/host/tests/hdmiCEC_L3_Tests/hdmiCECHelperClass.py new file mode 100755 index 0000000..a95ded8 --- /dev/null +++ b/host/tests/hdmiCEC_L3_Tests/hdmiCECHelperClass.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2024 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + +import os +import sys + +dir_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(dir_path, "../../")) + +from raft.framework.plugins.ut_raft.configRead import ConfigRead +from raft.framework.plugins.ut_raft.utUserResponse import utUserResponse +from raft.framework.plugins.ut_raft import utHelperClass +from raft.framework.core.logModule import logModule +from classes.hdmCEC import hdmiCECClass + +class hdmiCECHelperClass(utHelperClass): + + def __init__(self, testName:str, qcId:str, log:logModule=None ): + """ + Initializes the test class with test name, setup configuration, and sessions for the device. + + Args: + testName (str) : name of the test + qcId (str): QC ID of the test. + log (class, optional): Parent log class. Defaults to None. + """ + self.testName = "" + self.testSetupPath = os.path.join(dir_path, "hdmiCEC__L3_testSetup.yml") + self.moduleName = "hdmiCEC" + self.rackDevice = "dut" + + super().__init__(testName, qcId, log) + + # Load test setup configuration + self.testSetup = ConfigRead(self.testSetupPath, self.moduleName) + + # Open Sessions hal test + self.hal_session = self.dut.getConsoleSession("ssh_hal_test") + + deviceTestSetup = self.cpe.get("test") + + # Create user response Class + self.testUserResponse = utUserResponse() + + # Get path to device profile file + self.moduleConfigProfileFile = os.path.join(dir_path, deviceTestSetup.get("profile")) + + self.targetWorkspace = self.cpe.get("target_directory") + self.targetWorkspace = os.path.join(self.targetWorkspace, self.moduleName) + +# def testDownloadAssets(self): +# """ +# Downloads the test artifacts listed in the test setup configuration. +# +# This function retrieves the necessary files and saves them on the DUT. +# +# Args: +# None +# """ +# +# # List of streams with path +# self.testStreams = [] +# url = [] +# +# streamPaths = self.testSetup.get("assets").get("device").get(self.testName).get("streams") +# +# # Download test streams to device +# if streamPaths and self.streamDownloadURL: +# for streamPath in streamPaths: +# url.append(os.path.join(self.streamDownloadURL, streamPath)) +# self.testStreams.append(os.path.join(self.targetWorkspace, os.path.basename(streamPath))) +# self.downloadToDevice(url, self.targetWorkspace, self.rackDevice) + +# def testCleanAssets(self): +# """ +# Removes the downloaded assets and test streams from the DUT after test execution. +# +# Args: +# None +# """ +# self.deleteFromDevice(self.testStreams) +# + def testRunPrerequisites(self): + """ + Executes prerequisite commands listed in the test setup configuration file on the DUT. + + Args: + None + """ + + # Run commands as part of test prerequisites + test = self.testSetup.get("assets").get("device").get(self.testName) + cmds = test.get("execute") + if cmds is not None: + for cmd in cmds: + self.writeCommands(cmd) + + def testPrepareFunction(self): + """ + Prepares the environment and assets required for the test. + + This function: + - Downloads the required assets. + - Runs the prerequisite commands. + - Creates hdmiCEC + + Returns: + bool + """ + + + # Run Prerequisites listed in the test setup configuration file + self.testRunPrerequisites() + + # Create the hdmiCEC class + self.testhdmiCEC = hdmiCECClass(self.moduleConfigProfileFile, self.hal_session, self.targetWorkspace) + + return True + + def testEndFunction(self, powerOff=True): + # Clean the assets downloaded to the device + self.testCleanAssets() + + # Clean up the hdmiCEC instance + del self.testhdmiCEC + + def testExceptionCleanUp (self): + # Clean the assets downloaded to the device + self.testCleanAssets() diff --git a/host/tests/hdmiCEC_L3_Tests/hdmiCEC_L3_testSetup.yml b/host/tests/hdmiCEC_L3_Tests/hdmiCEC_L3_testSetup.yml new file mode 100755 index 0000000..bf903d3 --- /dev/null +++ b/host/tests/hdmiCEC_L3_Tests/hdmiCEC_L3_testSetup.yml @@ -0,0 +1,35 @@ +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2024 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + + +hdmiCEC: # Prefix must always exist + description: "hdmiCEC test setup" + assets: + device: + test01_TransmitSingleStandbyCommandandValidateAck: + <<: *defaults + test02_BroadcastStandbyCommand: + <<: *defaults + test03_TransmitOSDCommandandValidateAck: + <<: *defaults + test04_TransmitLargerOSDCommandRepeatedlyandValidate: + <<: *defaults diff --git a/host/tests/hdmiCEC_L3_Tests/hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck.py b/host/tests/hdmiCEC_L3_Tests/hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck.py new file mode 100755 index 0000000..ae2a09d --- /dev/null +++ b/host/tests/hdmiCEC_L3_Tests/hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +#** ***************************************************************************** +# * +# * If not stated otherwise in this file or this component's LICENSE file the +# * following copyright and licenses apply: +# * +# * Copyright 2024 RDK Management +# * +# * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# * +# * 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. +# * +#* ****************************************************************************** + +import os +import sys + +dir_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(dir_path)) +sys.path.append(os.path.join(dir_path, "../")) + +from hdmiCECHelperClass import hdmiCECHelperClass +from raft.framework.core.logModule import logModule + +class hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck(hdmiCECHelperClass): + """ + Test class to enable, disable, and verify the status of audio ports on a device. + + This class uses the `dsAudioClass` to interact with the device's audio ports, + downloading necessary test assets, playing audio streams, enabling and disabling + audio ports, and performing verification of audio output. + """ + def __init__(self, log:logModule=None): + """ + Initializes the test class with test name, setup configuration, and sessions for the device. + + Args: + None + """ + # Class variables + self.testName = "test01_TransmitSingleStanbyCommandandValidateAck" + self.qcID = '1' + + super().__init__(self.testName, self.qcID, log) + + #TODO: Current version supports only manual verification. + def testVerifyStanbyStatus(self, ack, manual=False): + """ + Verifies the CEC Transmit Status through the result and also through the manual input. + + For manual verification, it prompts the user to confirm if CEC data is received by the destination + device and the action has been performed. + + Args: + ack(str): Ack result as printed on the test. Need to check that proper ACK is received. + (bool, optional): Flag to indicate if manual verification should be used. + Defaults to False for automation, True for manual. + + Returns: + bool: True if ACK verification succeeds, False otherwise.Also True if Manual test is Y and Flase if N + """ + if manual == True: + return self.testUserResponse.getUserYN(f"Is Stanby Command honored {port}? (Y/N):") + else : + #TODO: Add automation verification methods + return False + + def testFunction(self): + """ + The main test function that Transmits the Stanby Command and checks the ACK and validates it. + + This function: + - Send a standby command to a device that is connected on the CEC network and get the ack. + - User to confirm whether the targetted device had recieved this command or not. + + Returns: + bool: Final result of the test. + """ + + # Initialize the hdmiCEC module + self.testhdmiCEC.initialise() + + # Add the logical Address + self.testhdmiCEC.addLogicalAddress(0) + + # Transmit Standby command to a specific destination address + self.testhdmiCEC.cecTransmitCmd(0,3,65,[0]) + + #Verify the test result + result = self.testVerifyStandbyStatus(ack, True) + + # Remove the Logical Address + self.testhdmiCEC.removeLogicalAddress(0) + + # Terminate dsAudio Module + self.testdsAudio.terminate() + + return result + +if __name__ == '__main__': + summerLogName = os.path.splitext(os.path.basename(__file__))[0] + "_summery" + summeryLog = logModule(summerLogName, level=logModule.INFO) + test = hdmiCEC_test01_TransmitSingleStandbyCommandandValidateAck(summeryLog) + test.run(False) diff --git a/src/test_l3_hdmi_cec_sink_driver.c b/src/test_l3_hdmi_cec_sink_driver.c index bd825be..50ee614 100644 --- a/src/test_l3_hdmi_cec_sink_driver.c +++ b/src/test_l3_hdmi_cec_sink_driver.c @@ -180,6 +180,7 @@ int getCecCommandInfo(unsigned char cecCommand, const char** commandName, int* d */ /* Callback function */ +#if 0 void onRxDataReceived(int handle, void *callbackData, unsigned char *buf, int len) { UT_LOG_INFO("In %s(IN: handle: [%d], IN: callbackData: [%p], IN: buf: [%p], IN: len: [%d])\n", __FUNCTION__, handle, callbackData, buf, len); @@ -211,6 +212,211 @@ void onRxDataReceived(int handle, void *callbackData, unsigned char *buf, int le cbFlag = 1; UT_LOG_INFO("Out %s(OUT: cbFlag set to [1])\n", __FUNCTION__); } +#endif + +void displayOsdMessage(const char *message) { + UT_LOG_INFO("Displaying OSD message: \"%s\" on the device.\n", message); + + // Implement device-specific logic here, e.g., + // - Update a graphical display + // - Notify the user via LEDs or indicators + // - Trigger an external display mechanism + + // For example: + printf("OSD Message: %s\n", message); + + UT_LOG_INFO("OSD message display completed.\n"); +} + +void handleImageViewOn(int handle, unsigned char initiator, unsigned char destination) { + UT_LOG_INFO("Image View On command received.\n"); + // Perform any device-specific action for "Image View On" if needed. + UT_LOG_INFO("Image View On processed.\n"); +} + +void handleActiveSource(int handle, unsigned char initiator, unsigned char destination, unsigned char *buf, int len) { + if (len >= 4) { + unsigned short physicalAddress = (buf[2] << 8) | buf[3]; // Combine bytes to form the physical address + UT_LOG_INFO("Active Source command received. Physical Address: [0x%04X]\n", physicalAddress); + + // Process Active Source as needed. + } else { + UT_LOG_ERROR("Active Source command received with insufficient data.\n"); + } +} + +void handleGivePhysicalAddress(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[5] = { (destination << 4) | initiator, 0x84, 0x10, 0x00, 0x02 }; // Physical Address = 1.0.0.0, Device Type = TV + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Reported Physical Address response sent with result: %d\n", result); +} + +void handleDeviceVendorID(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[4] = { (destination << 4) | initiator, 0x87, 0x00, 0x00, 0x01 }; // Example Vendor ID: 0x000001 + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Device Vendor ID response sent with result: %d\n", result); +} + +void handleReportPhysicalAddress(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[5] = { (destination << 4) | initiator, 0x84, 0x10, 0x00, 0x02 }; // Physical Address = 1.0.0.0, Device Type = TV + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Reported Physical Address response sent with result: %d\n", result); +} + +void handleReportPowerStatus(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[3] = { (destination << 4) | initiator, 0x90, 0x00 }; // Power Status: On (0x00) + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Reported Power Status response sent with result: %d\n", result); +} + +void handleGiveCECVersion(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[3] = { (destination << 4) | initiator, 0x9E, 0x05 }; // CEC Version: 1.4 (0x05) + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("CEC Version response sent with result: %d\n", result); +} + +void handleGiveDeviceVendorID(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[4] = { (destination << 4) | initiator, 0x87, 0x00, 0x00, 0x01 }; // Vendor ID: 0x000001 + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Device Vendor ID response sent with result: %d\n", result); +} + +void handleStandby(int handle) { + UT_LOG_INFO("Standby command received. Initiating standby actions.\n"); + // Implement device-specific standby actions here, such as turning off the display. +} + +void handleGiveDeviceInfo(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[12] = { (destination << 4) | initiator, 0xA1, 'V', 'T', 'S', ' ', 'D', 'e', 'v', 'i', 'c', 'e' }; // Device Info: "VTS Device" + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Device Info response sent with result: %d\n", result); +} + +void handleOsdDisplay(int handle, unsigned char initiator, unsigned char destination, unsigned char *buf, int len) { + if (len > 2) { + UT_LOG_INFO("OSD Display message received: "); + for (int i = 2; i < len; i++) { + UT_LOG_INFO("%c", buf[i]); + } + UT_LOG_INFO("\n"); + } else { + UT_LOG_ERROR("OSD Display message received with insufficient data.\n"); + } +} + +void handleGetOsdName(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[11] = { (destination << 4) | initiator, 0x47, 'V', 'T', 'S', ' ', 'D', 'e', 'v', 'i', 'c', 'e' }; // OSD Name: "VTS Device" + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("OSD Name response sent with result: %d sourc/destination:0x%2x\n", result,response[0]); +} + +void handleGetPowerStatus(int handle, unsigned char initiator, unsigned char destination) { + unsigned char response[3] = { (destination << 4) | initiator, 0x90, 0x00 }; // Power Status: On (0x00) + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_INFO("Power Status response sent with result: %d\n", result); +} + +void handleFeatureAbort(int handle, unsigned char initiator, unsigned char destination, unsigned char opcode, unsigned char reason) { + unsigned char response[4] = { (destination << 4) | initiator, 0x00, opcode, reason }; // Abort with reason + int result; + HdmiCecTx(handle, response, sizeof(response), &result); + UT_LOG_WARNING("Feature Abort sent for opcode: 0x%02X with reason: 0x%02X, result: %d\n", opcode, reason, result); +} + + +/** + * @brief This function clears the stdin buffer. + * + * This function clears the stdin buffer. + */ +static void readAndDiscardRestOfLine(FILE *in) +{ + int c; + while ((c = fgetc(in)) != EOF && c != '\n'); +} + +static void readInt(int32_t *value) +{ + scanf("%d", value); + readAndDiscardRestOfLine(stdin); +} + +static void readHex(int32_t *value) +{ + scanf("%x", value); + readAndDiscardRestOfLine(stdin); +} +void onRxDataReceived(int handle, void *callbackData, unsigned char *buf, int len) { + UT_LOG_INFO("In %s(IN: handle: [%d], IN: callbackData: [%p], IN: buf: [%p], IN: len: [%d])\n", __FUNCTION__, handle, callbackData, buf, len); + + if ((handle != 0) && (callbackData != NULL) && (len > 0)) { + UT_LOG_INFO("CEC Data Received\n"); + + // Log each byte received in the buffer + for (int index = 0; index < len; index++) { + UT_LOG_INFO("Buffer at index [%d]: [0x%02X]\n", index, buf[index]); + } + + // Parse the command + unsigned char initiator = (buf[0] >> 4) & 0xF; // Extract initiator address + unsigned char destination = buf[0] & 0xF; // Extract destination address + unsigned char opcode = buf[1]; // Command opcode + + UT_LOG_INFO("Initiator: [0x%X], Destination: [0x%X], Opcode: [0x%02X]\n", initiator, destination, opcode); + + // Handle each opcode with its corresponding function + switch (opcode) { + case 0x04: handleImageViewOn(handle, initiator, destination); break; + case 0x82: handleActiveSource(handle, initiator, destination, buf, len); break; + case 0x8E: handleGivePhysicalAddress(handle, initiator, destination); break; + case 0x87: handleDeviceVendorID(handle, initiator, destination); break; + case 0x84: handleReportPhysicalAddress(handle, initiator, destination); break; + case 0x90: handleReportPowerStatus(handle, initiator, destination); break; + case 0x9F: handleGiveCECVersion(handle, initiator, destination); break; + case 0x8C: handleGiveDeviceVendorID(handle, initiator, destination); break; + case 0x36: handleStandby(handle); break; + case 0xA0: handleGiveDeviceInfo(handle, initiator, destination); break; + case 0x64: handleOsdDisplay(handle, initiator, destination, buf, len); break; + case 0x46: handleGetOsdName(handle, initiator, destination); break; + case 0x8F: handleGetPowerStatus(handle, initiator, destination); break; + case 0x00: handleFeatureAbort(handle, initiator, destination, opcode, 0x04); break; + default: + UT_LOG_WARNING("Unhandled opcode: [0x%02X]\n", opcode); + //handleFeatureAbort(handle, initiator, destination, opcode, 0x04); // Feature Abort: Unrecognized opcode + break; + } + + // Clear the buffer after processing + memset(buf, 0, len); + UT_LOG_INFO("Buffer cleared after processing.\n"); + + } else { + // Log specific errors based on failed conditions + if (handle == 0) { + UT_LOG_ERROR("Error: Invalid handle.\n"); + } + if (callbackData == NULL) { + UT_LOG_ERROR("Error: Null callback data.\n"); + } + if (len <= 0) { + UT_LOG_ERROR("Error: Invalid length.\n"); + } + } + + cbFlag = 1; + UT_LOG_INFO("Out %s(OUT: cbFlag set to [1])\n", __FUNCTION__); +} + + /** * @brief Initialization of the HAL CEC Module @@ -287,13 +493,13 @@ void test_l3_hdmi_cec_sink_hal_AddLogicalAddress(void) UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tEnter Logical Address: "); + readHex(&logicalAddress;) UT_LOG_MENU_INFO("----------------------------------------------------------"); - scanf("%d", &logicalAddress); /* Check that logical address should be valid one */ UT_LOG_INFO("Calling HdmiCecAddLogicalAddress(IN:handle:[0x%0X], IN:logicalAddress:[%d]",gHandle,logicalAddress); status = HdmiCecAddLogicalAddress(gHandle,logicalAddress ); - UT_LOG_INFO("Failed HdmiCecAddLogicalAddress (IN:handle:[0x%0X], IN:logicalAddress:[%d]), status[%d]",gHandle,logicalAddress,UT_Control_GetMapString(cecError_mapTable,status)); + UT_LOG_INFO("HdmiCecAddLogicalAddress (IN:handle:[0x%0X], IN:logicalAddress:[%d]), status[%d]",gHandle,logicalAddress,UT_Control_GetMapString(cecError_mapTable,status)); assert(status == HDMI_CEC_IO_SUCCESS); UT_LOG_INFO("Out %s\n", __FUNCTION__); @@ -372,19 +578,19 @@ void test_l3_hdmi_cec_sink_hal_TransmitHdmiCecCommand(void) { // Reading inputs from the user or test framework UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tEnter a valid Source Logical Address:"); + readHex(&sourceLogicalAddress); UT_LOG_MENU_INFO("----------------------------------------------------------"); - scanf("%d", &sourceLogicalAddress); UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tEnter a valid Destination Logical Address: "); - scanf("%d", &destinationLogicalAddress); + readHex(&destinationLogicalAddress); UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tEnter CEC Command (in hex): "); + readHex(&cecCommand); UT_LOG_MENU_INFO("----------------------------------------------------------"); - scanf("%x", &cecCommand); // Validate the CEC command and get the expected data length if (getCecCommandInfo(cecCommand, &commandName, &expectedDataLength) != 0) { @@ -393,9 +599,8 @@ void test_l3_hdmi_cec_sink_hal_TransmitHdmiCecCommand(void) { UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tPlease enter the number of data bytes for the CEC command: "); + readInt(&expectedDataLength); UT_LOG_MENU_INFO("----------------------------------------------------------"); - scanf("%d", &expectedDataLength); - commandName = "Vendor Specific Command"; } else { UT_LOG_INFO("CEC Command: %s (0x%02X), expects %d data byte(s)", commandName, cecCommand, expectedDataLength); @@ -405,7 +610,7 @@ void test_l3_hdmi_cec_sink_hal_TransmitHdmiCecCommand(void) { if (expectedDataLength > 0) { for (int i = 0; i < expectedDataLength; i++) { UT_LOG_MENU_INFO("\t \tEnter Databyte[%d] (in hex):", i); - scanf("%x", &buf[i + 2]); // +2 to account for the first two bytes + readHex(&buf[i + 2]); // +2 to account for the first two bytes } } @@ -425,7 +630,7 @@ void test_l3_hdmi_cec_sink_hal_TransmitHdmiCecCommand(void) { int status = HdmiCecTx(gHandle, buf, len, &result); UT_LOG_INFO("HdmiCecTx(IN: handle: [0x%0X], IN: length: [%d], result: [%d], status:[%d])", gHandle, len, result, UT_Control_GetMapString(cecError_mapTable,status)); - assert((result == HDMI_CEC_IO_SENT_BUT_NOT_ACKD) && (status == HDMI_CEC_IO_SUCCESS)); + //assert((result == HDMI_CEC_IO_SENT_BUT_NOT_ACKD) && (status == HDMI_CEC_IO_SUCCESS)); // Optional delay after sending the command sleep(5); @@ -545,10 +750,9 @@ void test_l3_hdmi_cec_sink_hal_RemoveLogicalAddress(void) UT_LOG_MENU_INFO("----------------------------------------------------------"); UT_LOG_MENU_INFO("\t \tEnter Logical Address to Remove: "); + readHex(&logicalAddress); UT_LOG_MENU_INFO("----------------------------------------------------------"); - scanf("%d", &logicalAddress); - UT_LOG_INFO("Calling HdmiCecRemoveLogicalAddress(IN: handle: [0x%0X], IN: logicalAddress: [%d])", gHandle, logicalAddress); // Invoke the API HdmiCecRemoveLogicalAddress