blob: de8c5eb485b9eb0f4de4ce18c1ba82cc3c84900c [file]
# Copyright (c) 2022-2026 Project CHIP Authors
#
# 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 logging
import os
import shlex
from enum import Enum, auto
from runner.runner import Runner
from .builder import Builder, BuilderOutput, OutDirLock, lock_output_dir
log = logging.getLogger(__name__)
class TelinkLogLevel(Enum):
DEFAULT = auto() # default everything
ALL = auto() # enable all logging
PROGRESS = auto() # progress and above
ERROR = auto() # error and above
NONE = auto() # no chip_logging at all
class TelinkApp(Enum):
AIR_QUALITY_SENSOR = auto()
ALL_CLUSTERS = auto()
ALL_CLUSTERS_MINIMAL = auto()
ALL_DEVICES = auto()
BRIDGE = auto()
CONTACT_SENSOR = auto()
LIGHT = auto()
SWITCH = auto()
LOCK = auto()
OTA_REQUESTOR = auto()
PUMP = auto()
PUMP_CONTROLLER = auto()
SHELL = auto()
SMOKE_CO_ALARM = auto()
TEMPERATURE_MEASUREMENT = auto()
THERMOSTAT = auto()
WINDOW_COVERING = auto()
def ExampleName(self):
if self == TelinkApp.AIR_QUALITY_SENSOR:
return 'air-quality-sensor-app'
if self == TelinkApp.ALL_CLUSTERS:
return 'all-clusters-app'
if self == TelinkApp.ALL_CLUSTERS_MINIMAL:
return 'all-clusters-minimal-app'
if self == TelinkApp.ALL_DEVICES:
return 'all-devices-app'
if self == TelinkApp.BRIDGE:
return 'bridge-app'
if self == TelinkApp.CONTACT_SENSOR:
return 'contact-sensor-app'
if self == TelinkApp.LIGHT:
return 'lighting-app'
if self == TelinkApp.SWITCH:
return 'light-switch-app'
if self == TelinkApp.LOCK:
return 'lock-app'
if self == TelinkApp.OTA_REQUESTOR:
return 'ota-requestor-app'
if self == TelinkApp.PUMP:
return 'pump-app'
if self == TelinkApp.PUMP_CONTROLLER:
return 'pump-controller-app'
if self == TelinkApp.SHELL:
return 'shell'
if self == TelinkApp.SMOKE_CO_ALARM:
return 'smoke-co-alarm-app'
if self == TelinkApp.TEMPERATURE_MEASUREMENT:
return 'temperature-measurement-app'
if self == TelinkApp.THERMOSTAT:
return 'thermostat'
if self == TelinkApp.WINDOW_COVERING:
return 'window-app'
raise Exception('Unknown app type: %r' % self)
def AppNamePrefix(self):
if self == TelinkApp.AIR_QUALITY_SENSOR:
return 'chip-telink-air-quality-sensor-example'
if self == TelinkApp.ALL_CLUSTERS:
return 'chip-telink-all-clusters-example'
if self == TelinkApp.ALL_CLUSTERS_MINIMAL:
return 'chip-telink-all-clusters-minimal-example'
if self == TelinkApp.ALL_DEVICES:
return 'all-devices-app'
if self == TelinkApp.BRIDGE:
return 'chip-telink-bridge-example'
if self == TelinkApp.CONTACT_SENSOR:
return 'chip-telink-contact-sensor-example'
if self == TelinkApp.LIGHT:
return 'chip-telink-lighting-example'
if self == TelinkApp.SWITCH:
return 'chip-telink-light-switch-example'
if self == TelinkApp.LOCK:
return 'chip-telink-lock-example'
if self == TelinkApp.OTA_REQUESTOR:
return 'chip-telink-ota-requestor-example'
if self == TelinkApp.PUMP:
return 'chip-telink-pump-example'
if self == TelinkApp.PUMP_CONTROLLER:
return 'chip-telink-pump-controller-example'
if self == TelinkApp.RESOURCE_MONITORING:
return 'chip-telink-resource-monitoring-example'
if self == TelinkApp.SHELL:
return 'chip-telink-shell-example'
if self == TelinkApp.SMOKE_CO_ALARM:
return 'chip-telink-smoke-co-alarm-example'
if self == TelinkApp.TEMPERATURE_MEASUREMENT:
return 'chip-telink-temperature-measurement-example'
if self == TelinkApp.THERMOSTAT:
return 'chip-telink-thermostat-example'
if self == TelinkApp.WINDOW_COVERING:
return 'chip-telink-window-example'
raise Exception('Unknown app type: %r' % self)
class TelinkBoard(Enum):
TLRS9118BDK40D = auto()
TLSR9518ADK80D = auto()
TLSR9528A = auto()
TLSR9528A_RETENTION = auto()
TL3218X = auto()
TL3218X_ML3M = auto()
TL3218X_RETENTION = auto()
TL7218X = auto()
TL7218X_ML7G = auto()
TL7218X_ML7M = auto()
TL7218X_RETENTION = auto()
def GnArgName(self):
if self == TelinkBoard.TLRS9118BDK40D:
return 'tlsr9118bdk40d'
if self == TelinkBoard.TLSR9518ADK80D:
return 'tlsr9518adk80d'
if self == TelinkBoard.TLSR9528A:
return 'tlsr9528a'
if self == TelinkBoard.TLSR9528A_RETENTION:
return 'tlsr9528a_retention'
if self == TelinkBoard.TL3218X:
return 'tl3218x'
if self == TelinkBoard.TL3218X_ML3M:
return 'tl3218x_ml3m'
if self == TelinkBoard.TL3218X_RETENTION:
return 'tl3218x_retention'
if self == TelinkBoard.TL7218X:
return 'tl7218x'
if self == TelinkBoard.TL7218X_ML7G:
return 'tl7218x_ml7g'
if self == TelinkBoard.TL7218X_ML7M:
return 'tl7218x_ml7m'
if self == TelinkBoard.TL7218X_RETENTION:
return 'tl7218x_retention'
raise Exception('Unknown board type: %r' % self)
class TelinkBuilder(Builder):
def __init__(self,
root: str,
runner: Runner,
output_dir_lock: OutDirLock,
app: TelinkApp = TelinkApp,
board: TelinkBoard = TelinkBoard,
enable_ota: bool = False,
enable_dfu_smp: bool = False,
enable_shell: bool = False,
enable_rpcs: bool = False,
enable_factory_data: bool = False,
enable_4mb_flash: bool = False,
mars_board_config: bool = False,
usb_board_config: bool = False,
compress_lzma_config: bool = False,
thread_analyzer_config: bool = False,
precompiled_ot_config: bool = False,
tflm_config: bool = False,
chip_enable_nfc_onboarding_payload: bool = False,
log_level: TelinkLogLevel = TelinkLogLevel.DEFAULT,
all_devices_enabled_devices=None,
):
super(TelinkBuilder, self).__init__(root, runner, output_dir_lock)
self.app = app
self.board = board
self.enable_ota = enable_ota
self.enable_dfu_smp = enable_dfu_smp
self.enable_shell = enable_shell
self.enable_rpcs = enable_rpcs
self.enable_factory_data = enable_factory_data
self.enable_4mb_flash = enable_4mb_flash
self.mars_board_config = mars_board_config
self.usb_board_config = usb_board_config
self.compress_lzma_config = compress_lzma_config
self.thread_analyzer_config = thread_analyzer_config
self.precompiled_ot_config = precompiled_ot_config
self.tflm_config = tflm_config
self.chip_enable_nfc_onboarding_payload = chip_enable_nfc_onboarding_payload
self.log_level = log_level
self.all_devices_enabled_devices = all_devices_enabled_devices or []
def get_cmd_prefixes(self):
if not self._runner.dry_run:
# Zephyr base
if 'TELINK_ZEPHYR_BASE' not in os.environ:
raise Exception("Telink builds require TELINK_ZEPHYR_BASE")
cmd = 'export ZEPHYR_TOOLCHAIN_VARIANT=zephyr\n'
cmd += 'export ZEPHYR_BASE="$TELINK_ZEPHYR_BASE"\n'
if 'TELINK_ZEPHYR_SDK_DIR' in os.environ:
cmd += 'export ZEPHYR_SDK_INSTALL_DIR="$TELINK_ZEPHYR_SDK_DIR"\n'
return cmd
@lock_output_dir
def generate(self):
os.makedirs(self.output_dir, exist_ok=True)
flags = []
if self.enable_ota:
flags.append("-DCONFIG_CHIP_OTA_REQUESTOR=y")
if self.enable_dfu_smp:
flags.append("-DCONFIG_CHIP_DFU_OVER_BT_SMP=y -DCONFIG_CHIP_DFU_OVER_BT_SMP_BUILD=y")
if self.enable_shell:
flags.append("-DCONFIG_CHIP_LIB_SHELL=y")
if self.enable_rpcs:
flags.append("-DOVERLAY_CONFIG=rpc.overlay")
if self.enable_factory_data:
flags.append("-DCONFIG_CHIP_FACTORY_DATA=y -DCONFIG_CHIP_FACTORY_DATA_BUILD=y -DCONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE=y")
if self.enable_4mb_flash:
flags.append("-DFLASH_SIZE=4m")
if self.mars_board_config:
flags.append("-DTLNK_MARS_BOARD=y")
if self.usb_board_config:
flags.append("-DTLNK_USB_DONGLE=y")
if self.compress_lzma_config:
flags.append("-DCONFIG_COMPRESS_LZMA=y")
if self.chip_enable_nfc_onboarding_payload:
flags.append("-DCONFIG_CHIP_NFC_ONBOARDING_PAYLOAD=y")
if self.thread_analyzer_config:
flags.append("-DCONFIG_THREAD_ANALYZER=y")
if self.precompiled_ot_config:
flags.append("-DCONFIG_OPENTHREAD_TELINK_LIBRARY=y -DCONFIG_LOG_MODE_DEFERRED=y")
if self.tflm_config:
flags.append("-DCONFIG_TFLM_FEATURE=y")
if self.options.pregen_dir:
flags.append(f"-DCHIP_CODEGEN_PREGEN_DIR={shlex.quote(self.options.pregen_dir)}")
if self.all_devices_enabled_devices:
flags.append(f"-DALL_DEVICES_ENABLED_DEVICES={shlex.quote(';'.join(self.all_devices_enabled_devices))}")
if self.log_level == TelinkLogLevel.DEFAULT:
pass
elif self.log_level == TelinkLogLevel.ALL:
flags.append("-DTLNK_LOG_LEVEL=all")
elif self.log_level == TelinkLogLevel.PROGRESS:
flags.append("-DTLNK_LOG_LEVEL=progress")
elif self.log_level == TelinkLogLevel.ERROR:
flags.append("-DTLNK_LOG_LEVEL=error")
elif self.log_level == TelinkLogLevel.NONE:
flags.append("-DTLNK_LOG_LEVEL=none")
else:
raise Exception("Unknown log level: %r" % self.log_level)
build_flags = " -- " + " ".join(flags) if len(flags) > 0 else ""
cmd = self.get_cmd_prefixes()
cmd += '\nsource "$ZEPHYR_BASE/zephyr-env.sh";'
cmd += '\nwest build --cmake-only -d {outdir} -b {board} {sourcedir}{build_flags}\n'.format(
outdir=shlex.quote(self.output_dir),
board=self.board.GnArgName(),
sourcedir=shlex.quote(os.path.join(self.root, 'examples', self.app.ExampleName(), 'telink')),
build_flags=build_flags).strip()
self._Execute(['bash', '-c', cmd],
title='Generating ' + self.identifier)
@lock_output_dir
def _build(self):
log.info('Compiling Telink at %s', self.output_dir)
cmd = self.get_cmd_prefixes() + ("ninja -C %s" % self.output_dir)
if self.ninja_jobs is not None:
cmd += " -j%s" % str(self.ninja_jobs)
self._Execute(['bash', '-c', cmd], title='Building ' + self.identifier)
def _AllDevicesOutputName(self):
"""Return the binary base name produced by the all-devices-app build."""
if self.all_devices_enabled_devices:
return 'example-device-app'
return 'all-devices-app'
@lock_output_dir
def build_outputs(self):
app_name = self._AllDevicesOutputName() if self.app == TelinkApp.ALL_DEVICES else self.app.AppNamePrefix()
yield BuilderOutput(
os.path.join(self.output_dir, 'zephyr', 'zephyr.elf'),
f'{app_name}.elf')
if self.options.enable_link_map_file:
yield BuilderOutput(
os.path.join(self.output_dir, 'zephyr', 'zephyr.map'),
f'{app_name}.map')