A Toradex proporciona aos seus clientes todo um ecossistema já muito bem estruturado para que sejam criadas imagens Linux e aplicações embarcadas de uma maneira relativamente fácil. Uma das ferramentas que compõem esse ecossistema é o Toradex Easy Installer, sendo a principal forma de instalar imagens Linux nos computadores em módulo da Toradex, tanto imagens customizadas via Yocto como imagens padrão já pré geradas, e também imagens de demonstração de parceiros como Qt.
Neste artigo veremos como gerar uma imagem Linux utilizando o Yocto e BSP da Toradex. Essa imagem será compatível com a ferramenta Toradex Easy Installer e terá uma identidade visual do Embarcados que será exibida na ferramenta como ícone, banners e nome customizado da imagem. Isso é útil quando temos uma aplicação ou imagem de demonstração utilizando algum SoM da Toradex ou quando queremos melhorar a experiência de alguém que irá instalar nossa imagem no SoM, por exemplo alguém do time de produção ou testes.
Configurando o Ambiente Yocto
Primeiramente precisamos montar um ambiente Yocto para geração da nossa imagem customizada. Para isso basta seguir o guia completo no site de desenvolvedores da Toradex.
https://developer.toradex.com/knowledge-base/board-support-package/openembedded-core
Basicamente você deve seguir os seguintes passos:
1 – Instalar os pacotes de pré requisitos do Yocto no Ubuntu.
$ sudo apt-get install gawk wget git-core diffstat unzip texinfo gcc-multilib \
build-essential chrpath socat cpio python3 python3-pip python3-pexpect \
xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
pylint3 xterm
2 – Instalar o Repo
$ mkdir ~/bin
$ export PATH=~/bin:$PATH
$ curl https://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
3 – Instalar e configurar o Git
$ sudo apt install git
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
4 – Iniciar o ambiente e baixar os componentes dos fontes da Toradex
$ mkdir ${HOME}/oe-core
$ cd ${HOME}/oe-core
$ repo init -u https://git.toradex.com/toradex-manifest.git -b dunfell-5.x.y -m tdxref/default.xml
$ repo sync
5 – Executar o comando abaixo para configurar o ambiente de build
$ . export
6 – Editar o arquivo build/conf/local.conf
No nosso caso iremos gerar uma imagem para o módulo Colibri iMX6 por isso configuramos as flags MACHINE e ACCEPT_FSL_EULA no arquivo local.conf
MACHINE ?= "colibri-imx6"
ACCEPT_FSL_EULA = "1"
Aqui nesse ponto já poderíamos gerar uma imagem padrão da toradex tdx-reference-minimal-image, mas iremos mais adiante com a customização da imagem.
Criando e Customizando o Layer meta-embarcados
Dentro da pasta oe-core/layers é possível ver todos os layers que o BSP da Toradex utiliza para geração de imagens.
Iremos tomar como base o que a Toradex já faz para colocar identidade visual na imagem padrão Linux para ser exibida no Toradex Easy Installer, basicamente reproduzindo os arquivos. Esses arquivos são encontrados no seguinte diretório:
meta-toradex-bsp-common/recipes-bsp/tezi-metadata/
Dentro temos o arquivo da receita tezi-metadata_0.3.bb
DESCRIPTION = "Toradex Easy Installer Metadata"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = " \
file://prepare.sh \
file://wrapup.sh \
file://toradexlinux.png \
file://marketing.tar;unpack=false \
file://LA_OPT_NXP_SW.html \
"
# We want to always check the latest EULA file in image_type_tezi.bbclass
# So we put LA_OPT_NXP_SW.html to sstate duplicate whitelist, this
# ensures it could be deployed to ${DEPLOY_DIR_IMAGE} as a backup even
# it's aleady existed.
SSTATE_DUPWHITELIST_prepend = "${DEPLOY_DIR_IMAGE}/LA_OPT_NXP_SW.html "
inherit deploy nopackages
do_deploy () {
install -m 644 ${WORKDIR}/prepare.sh ${DEPLOYDIR}
install -m 644 ${WORKDIR}/wrapup.sh ${DEPLOYDIR}
install -m 644 ${WORKDIR}/toradexlinux.png ${DEPLOYDIR}
install -m 644 ${WORKDIR}/marketing.tar ${DEPLOYDIR}
install -m 644 ${WORKDIR}/LA_OPT_NXP_SW.html ${DEPLOYDIR}
}
addtask deploy before do_build after do_install
PACKAGE_ARCH = "${MACHINE_ARCH}"
E também a pasta files que contém os arquivos referenciados pela receita: prepare.sh, wrapup.sh, toradexlinux.png, marketing.tar
Logo mais à frente explicaremos um pouco da estrutura dessa receita e o que são todos esses arquivos.
Layer meta-embarcados
Quando se trabalha com Yocto é costume criar layers, que nada mais são do que pastas contendo outras pastas e arquivos onde irão residir as customizações referentes à sua imagem, aplicações específicas de sua empresa e outras customizações. Por isso criaremos um layer chamado meta-embarcados.
A estrutura mais básica de um layer é a pasta principal com prefixo “meta-” e dentro, outra pasta chamada conf. Dentro de conf é necessário criar um arquivo chamado layer.conf com o seguinte conteúdo:
# We have a conf and classes directory, add to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-embarcados"
BBFILE_PATTERN_meta-embarcados = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-embarcados = "10"
LAYERSERIES_COMPAT_meta-embarcados = "dunfell"
Note as referências a meta-embarcados dentro do arquivo.
Yocto recipes
As recipes são, como o nome já diz, receitas que tem todas as informações de como nossa aplicação irá ser construída e integrada na imagem Linux. Ou também pode conter outras instruções sobre a geração da imagem. No nosso caso iremos criar uma receita que irá indicar onde serão colocados os arquivos da identidade visual na imagem final.
Dentro da pasta meta-embarcados iremos criar uma pasta chamada recipes-bsp. E dentro desta, criaremos uma pasta chamada embarcados-tezi-metadata.
Dentro de embarcados-tezi-metada, teremos outra pasta com mesmo nome e também o arquivo com a receita embarcados-tezi-metadata_1.0.bb
A estrutura de pastas fica da seguinte maneira:
O conteúdo do arquivo da receita embarcados-tezi-metadata_1.0.bb fica da seguinte maneira:
DESCRIPTION = "Embarcados Easy Installer Metadata"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = " \
file://prepare_emb.sh \
file://wrapup_emb.sh \
file://logo_embarcados.png \
file://marketing_embarcados.tar;unpack=false \
"
inherit deploy nopackages
do_deploy() {
install -m 644 ${WORKDIR}/prepare_emb.sh ${DEPLOYDIR}
install -m 644 ${WORKDIR}/wrapup_emb.sh ${DEPLOYDIR}
install -m 644 ${WORKDIR}/logo_embarcados.png ${DEPLOYDIR}
install -m 644 ${WORKDIR}/marketing_embarcados.tar ${DEPLOYDIR}
}
addtask deploy before do_build after do_install
PACKAGE_ARCH = "${MACHINE_ARCH}"
Os componentes mais importantes da receita acima são:
SRC_URI indica onde se encontram os arquivos que usaremos naquela receita e como estão nomeados.
do_deploy indica onde serão instalados os arquivos indicados acima. No caso, ao gerar a imagem, eles se encontrarão na pasta de deploy que mostraremos mais adiante.
Arquivos da Identidade Visual para Toradex Easy Installer
Mas afinal quais são os arquivos da identidade visual e como fazemos a customização dos mesmos?
O primeiro arquivo é o logo_embarcados.png. Esse é o ícone que irá aparecer na listagem das imagens no Toradex Easy Installer. Esse ícone tem as dimensões de 40×40 pixels e está no formato png.
O outro arquivo, marketing_embarcados.tar, é na verdade uma pasta compactada no formato tar e dentro dela temos a pasta slides_vga. Dentro da pasta slides_vga temos 3 imagens slide1.jpg, slide2.jpg e slide3.jpg. Essas serão as imagens que aparecerão no carrossel enquanto a imagem Linux é gravada no módulo pelo Toradex Easy Installer. Cada imagem tem o tamanho de 640×400 pixels e está no formato jpg. Por exemplo:
Os outros arquivos prepare_emb.sh e wrapup_emb.sh são scripts que podem ser customizados para executar comandos antes e depois da instalação da imagem pelo Toradex Easy Installer. Note que colocamos o sufixo emb para diferenciar dos arquivos originais.
Para criar essas artes é possível usar o Gimp, Inkscape ou outra ferramenta de edição de imagem.
Yocto classes
Ainda resta um outro arquivo para indicar ao Yocto para fazer uso da nossa receita customizada. Esse arquivo é uma classe e no Yocto é basicamente utilizado para abstrair funcionalidades comuns entre receitas.
Nosso arquivo classe é basicamente uma cópia do arquivo classe image_type_tezi.bbclass dentro do diretório meta-toradex-bsp-common/classes. O arquivo foi nomeado image_type_tezi_embarcados.bbclass e fica dentro de uma pasta classes dentro do layer meta-embarcados.
# This class implements Toradex Easy Installer image type
# It allows to use OpenEmbedded to build images which can be consumed
# by the Toradex Easy Installer.
#
# Since it also generates the image.json description file it is rather
# interwind with the boot flow which is U-Boot target specific.
WKS_FILE_DEPENDS_append = " embarcados-tezi-metadata virtual/dtb "
DEPENDS += "${WKS_FILE_DEPENDS}"
RM_WORK_EXCLUDE += "${PN}"
TEZI_VERSION ?= "${DISTRO_VERSION}"
TEZI_DATE ?= "${TDX_MATRIX_BUILD_TIME}"
TEZI_IMAGE_NAME ?= "${IMAGE_NAME}"
TEZI_ROOT_FSTYPE ??= "ext4"
TEZI_ROOT_LABEL ??= "RFS"
TEZI_ROOT_NAME ??= "rootfs"
TEZI_ROOT_SUFFIX ??= "tar.xz"
TEZI_USE_BOOTFILES ??= "true"
TEZI_BOOT_SUFFIX ??= "${@'bootfs.tar.xz' if oe.types.boolean('${TEZI_USE_BOOTFILES}') else ''}"
TEZI_CONFIG_FORMAT ??= "2"
# Require newer Tezi for mx8 Socs with the u-boot environment bugfix
TEZI_CONFIG_FORMAT_mx8 ??= "4"
TEZI_EXTERNAL_KERNEL_DEVICETREE ??= ""
TEZI_EXTERNAL_KERNEL_DEVICETREE_BOOT ??= ""
TORADEX_FLASH_TYPE ??= "emmc"
UBOOT_BINARY_TEZI_EMMC ?= "${UBOOT_BINARY}"
UBOOT_BINARY_TEZI_RAWNAND ?= "${UBOOT_BINARY}"
UBOOT_ENV_TEZI ?= "${@ 'u-boot-initial-env-%s' % d.getVar('UBOOT_CONFIG') if d.getVar('UBOOT_CONFIG') else 'u-boot-initial-env'}"
UBOOT_ENV_TEZI_EMMC ?= "${UBOOT_ENV_TEZI}"
UBOOT_ENV_TEZI_RAWNAND ?= "${UBOOT_ENV_TEZI}"
# use DISTRO_FLAVOUR to append to the image name displayed in TEZI
DISTRO_FLAVOUR ??= ""
SUMMARY_append = "${DISTRO_FLAVOUR}"
# Append tar command to store uncompressed image size to ${T}.
# If a custom rootfs type is used make sure this file is created
# before compression.
IMAGE_CMD_tar_append = "; du -ks ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.tar | cut -f 1 > ${T}/image-size${IMAGE_NAME_SUFFIX}"
CONVERSION_CMD_tar_append = "; du -ks ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}.tar | cut -f 1 > ${T}/image-size.${type}"
CONVERSION_CMD_tar = "touch ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}; ${IMAGE_CMD_TAR} --numeric-owner -cf ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}.tar -C ${TAR_IMAGE_ROOTFS} . || [ $? -eq 1 ]"
CONVERSIONTYPES_append = " tar"
def get_uncompressed_size(d, type):
path = os.path.join(d.getVar('T'), "image-size.%s" % type)
if not os.path.exists(path):
return 0
with open(path, "r") as f:
size = f.read().strip()
return float(size) / 1024
# Make an educated guess of the needed boot partition size
# max(16MB, 3x the size of the payload rounded up to the next 2^x number)
def get_bootfs_part_size(d):
from math import log
part_size = 3 * 2 ** (1 + int(log(get_uncompressed_size(d, 'bootfs'), 2)))
return max(16, part_size)
# Whitespace separated list of files declared by 'deploy_var' variable
# from 'source_dir' (DEPLOY_DIR_IMAGE by default) to place in 'deploy_dir'.
# Entries will be installed under a same name as the source file. To change
# the destination file name, pass a desired name after a semicolon
# (eg. u-boot.img;uboot). Exactly same rules with how IMAGE_BOOT_FILES being
# handled by wic.
def tezi_deploy_files(d, deploy_var, deploy_dir, source_dir=None):
import os, re, glob, subprocess
src_files = d.getVar(deploy_var) or ""
src_dir = source_dir or d.getVar('DEPLOY_DIR_IMAGE')
dst_dir = deploy_dir
# list of tuples (src_name, dst_name)
deploy_files = []
for src_entry in re.findall(r'[\w;\-\./\*]+', src_files):
if ';' in src_entry:
dst_entry = tuple(src_entry.split(';'))
if not dst_entry[0] or not dst_entry[1]:
bb.fatal('Malformed file entry: %s' % src_entry)
else:
dst_entry = (src_entry, src_entry)
deploy_files.append(dst_entry)
# list of tuples (src_path, dst_path)
install_task = []
for deploy_entry in deploy_files:
src, dst = deploy_entry
if '*' in src:
# by default install files under their basename
entry_name_fn = os.path.basename
if dst != src:
# unless a target name was given, then treat name
# as a directory and append a basename
entry_name_fn = lambda name: \
os.path.join(dst,
os.path.basename(name))
srcs = glob.glob(os.path.join(src_dir, src))
for entry in srcs:
src = os.path.relpath(entry, src_dir)
entry_dst_name = entry_name_fn(entry)
install_task.append((src, entry_dst_name))
else:
install_task.append((src, dst))
# install src_path to dst_path
for task in install_task:
src_path, dst_path = task
install_cmd = "install -m 0644 -D %s %s" \
% (os.path.join(src_dir, src_path),
os.path.join(dst_dir, dst_path))
try:
subprocess.check_output(install_cmd, stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError as e:
bb.fatal("Command '%s' returned %d:\n%s" % (e.cmd, e.returncode, e.output))
def rootfs_tezi_emmc(d, use_bootfiles):
from collections import OrderedDict
offset_bootrom = d.getVar('OFFSET_BOOTROM_PAYLOAD')
offset_spl = d.getVar('OFFSET_SPL_PAYLOAD')
imagename = d.getVar('IMAGE_LINK_NAME')
bootpart_rawfiles = []
filesystem_partitions = []
if offset_spl:
bootpart_rawfiles.append(
{
"filename": d.getVar('SPL_BINARY'),
"dd_options": "seek=" + offset_bootrom
})
bootpart_rawfiles.append(
{
"filename": d.getVar('UBOOT_BINARY_TEZI_EMMC'),
"dd_options": "seek=" + (offset_spl if offset_spl else offset_bootrom)
})
if use_bootfiles:
filesystem_partitions.append(
{
"partition_size_nominal": get_bootfs_part_size(d),
"want_maximised": False,
"content": {
"label": "BOOT",
"filesystem_type": "FAT",
"mkfs_options": "",
"filename": imagename + "." + d.getVar('TEZI_BOOT_SUFFIX'),
"uncompressed_size": get_uncompressed_size(d, 'bootfs')
}
})
filesystem_partitions.append(
{
"partition_size_nominal": 512,
"want_maximised": True,
"content": {
"label": d.getVar('TEZI_ROOT_LABEL'),
"filesystem_type": d.getVar('TEZI_ROOT_FSTYPE'),
"mkfs_options": "-E nodiscard",
"filename": imagename + "." + d.getVar('TEZI_ROOT_SUFFIX'),
"uncompressed_size": get_uncompressed_size(d, d.getVar('TEZI_ROOT_NAME'))
}
})
return [
OrderedDict({
"name": "mmcblk0",
"partitions": filesystem_partitions
}),
OrderedDict({
"name": "mmcblk0boot0",
"erase": True,
"content": {
"filesystem_type": "raw",
"rawfiles": bootpart_rawfiles
}
})]
def rootfs_tezi_rawnand(d):
from collections import OrderedDict
imagename = d.getVar('IMAGE_LINK_NAME')
uboot1 = OrderedDict({
"name": "u-boot1",
"content": {
"rawfile": {
"filename": d.getVar('UBOOT_BINARY_TEZI_RAWNAND'),
"size": 1
}
},
})
uboot2 = OrderedDict({
"name": "u-boot2",
"content": {
"rawfile": {
"filename": d.getVar('UBOOT_BINARY_TEZI_RAWNAND'),
"size": 1
}
}
})
env = OrderedDict({
"name": "u-boot-env",
"erase": True,
"content": {}
})
rootfs = {
"name": "rootfs",
"content": {
"filesystem_type": "ubifs",
"filename": imagename + "." + d.getVar('TEZI_ROOT_SUFFIX'),
"uncompressed_size": get_uncompressed_size(d, d.getVar('TEZI_ROOT_NAME'))
}
}
kernel = {
"name": "kernel",
"size_kib": 8192,
"type": "static",
"content": {
"rawfile": {
"filename": d.getVar('KERNEL_IMAGETYPE'),
"size": 5
}
}
}
# Use device tree mapping to create product id <-> device tree relationship
dtmapping = d.getVarFlags('TORADEX_PRODUCT_IDS')
dtfiles = []
for f, v in dtmapping.items():
dtfiles.append({ "filename": v, "product_ids": f })
dtb = {
"name": "dtb",
"content": {
"rawfiles": dtfiles
},
"size_kib": 128,
"type": "static"
}
m4firmware = {
"name": "m4firmware",
"size_kib": 896,
"type": "static"
}
ubi = OrderedDict({
"name": "ubi",
"ubivolumes": [kernel, dtb, m4firmware, rootfs]
})
return [uboot1, uboot2, env, ubi]
def rootfs_tezi_json(d, flash_type, flash_data, json_file, uenv_file):
import json
from collections import OrderedDict
from datetime import datetime
deploydir = d.getVar('DEPLOY_DIR_IMAGE')
data = OrderedDict({ "config_format": d.getVar('TEZI_CONFIG_FORMAT'), "autoinstall": False })
# Use image recipes SUMMARY/DESCRIPTION...
data["name"] = '[Embarcados ' + d.getVar('EMBARCADOS_RELEASE_VERSION') + '] Custom Linux Image'
data["description"] = d.getVar('DESCRIPTION')
data["version"] = d.getVar('TEZI_VERSION')
data["release_date"] = datetime.strptime(d.getVar('TEZI_DATE'), '%Y%m%d%H%M%S').date().isoformat()
data["u_boot_env"] = uenv_file
if os.path.exists(os.path.join(deploydir, "prepare_emb.sh")):
data["prepare_script"] = "prepare_emb.sh"
if os.path.exists(os.path.join(deploydir, "wrapup_emb.sh")):
data["wrapup_script"] = "wrapup_emb.sh"
if os.path.exists(os.path.join(deploydir, "marketing_embarcados.tar")):
data["marketing"] = "marketing_embarcados.tar"
if os.path.exists(os.path.join(deploydir, "logo_embarcados.png")):
data["icon"] = "logo_embarcados.png"
product_ids = d.getVar('TORADEX_PRODUCT_IDS')
if product_ids is None:
bb.fatal("Supported Toradex product ids missing, assign TORADEX_PRODUCT_IDS with a list of product ids.")
dtmapping = d.getVarFlags('TORADEX_PRODUCT_IDS')
data["supported_product_ids"] = []
# If no varflags are set, we assume all product ids supported with single image/U-Boot
if dtmapping is not None:
for f, v in dtmapping.items():
dtbflashtypearr = v.split(',')
if len(dtbflashtypearr) < 2 or dtbflashtypearr[1] == flash_type:
data["supported_product_ids"].append(f)
else:
data["supported_product_ids"].extend(product_ids.split())
if flash_type == "rawnand":
data["mtddevs"] = flash_data
elif flash_type == "emmc":
data["blockdevs"] = flash_data
with open(os.path.join(d.getVar('IMGDEPLOYDIR'), json_file), 'w') as outfile:
json.dump(data, outfile, indent=4)
bb.note("Toradex Easy Installer metadata file {0} written.".format(json_file))
python rootfs_tezi_run_json() {
artifacts = "%s/%s.%s" % (d.getVar('IMGDEPLOYDIR'), d.getVar('IMAGE_LINK_NAME'), d.getVar('TEZI_ROOT_SUFFIX'))
flash_type = d.getVar('TORADEX_FLASH_TYPE')
if len(flash_type.split()) > 1:
bb.fatal("This class only supports a single flash type.")
if flash_type == "rawnand":
flash_data = rootfs_tezi_rawnand(d)
uenv_file = d.getVar('UBOOT_ENV_TEZI_RAWNAND')
uboot_file = d.getVar('UBOOT_BINARY_TEZI_RAWNAND')
artifacts += " " + d.getVar('KERNEL_IMAGETYPE') + " " + d.getVar('KERNEL_DEVICETREE')
elif flash_type == "emmc":
use_bootfiles = oe.types.boolean(d.getVar('TEZI_USE_BOOTFILES'))
flash_data = rootfs_tezi_emmc(d, use_bootfiles)
uenv_file = d.getVar('UBOOT_ENV_TEZI_EMMC')
uboot_file = d.getVar('UBOOT_BINARY_TEZI_EMMC')
# TODO: Multi image/raw NAND with SPL currently not supported
uboot_file += " " + d.getVar('SPL_BINARY') if d.getVar('OFFSET_SPL_PAYLOAD') else ""
artifacts += " " + "%s/%s.%s" % (d.getVar('IMGDEPLOYDIR'), d.getVar('IMAGE_LINK_NAME'), d.getVar('TEZI_BOOT_SUFFIX')) if use_bootfiles else ""
else:
bb.fatal("Toradex flash type unknown")
artifacts += " " + uenv_file + " " + uboot_file
d.setVar("TEZI_ARTIFACTS", artifacts)
rootfs_tezi_json(d, flash_type, flash_data, "image-%s.json" % d.getVar('IMAGE_BASENAME'), uenv_file)
}
python tezi_deploy_bootfs_files() {
tezi_deploy_files(d, 'IMAGE_BOOT_FILES', os.path.join(d.getVar('WORKDIR'), 'bootfs'))
}
tezi_deploy_bootfs_files[dirs] =+ "${WORKDIR}/bootfs"
tezi_deploy_bootfs_files[cleandirs] += "${WORKDIR}/bootfs"
MACHINE_PREFIX = "${MACHINE}"
MACHINE_PREFIX_apalis-imx8x-v11a = "apalis-imx8x"
MACHINE_PREFIX_colibri-imx8x-v10b = "colibri-imx8x"
MACHINE_PREFIX_colibri-imx7-emmc = "colibri-imx7"
tezi_deploy_dt_overlays() {
deploy_dt_dir=${DEPLOY_DIR_IMAGE}/devicetree/
dtbos=
if [ -z "${TEZI_EXTERNAL_KERNEL_DEVICETREE}" -a -d "$deploy_dt_dir" ] ; then
machine_dtbos=`cd $deploy_dt_dir && ls ${MACHINE_PREFIX}*.dtbo 2>/dev/null || true`
common_dtbos=`cd $deploy_dt_dir && ls *.dtbo 2>/dev/null | grep -v -e 'imx[6-8]' -e 'tk1' | xargs || true`
dtbos="$machine_dtbos $common_dtbos"
else
dtbos="${TEZI_EXTERNAL_KERNEL_DEVICETREE}"
fi
# overlays to copy to bootfs/overlays
mkdir -p ${WORKDIR}/bootfs/overlays/
for dtbo in $dtbos; do
cp $deploy_dt_dir/$dtbo ${WORKDIR}/bootfs/overlays/
done
# overlays that we want to be applied during boot time
overlays=
for dtbo in ${TEZI_EXTERNAL_KERNEL_DEVICETREE_BOOT}; do
if [ ! -e ${WORKDIR}/bootfs/overlays/$dtbo ]; then
bbfatal "$dtbo is not installed in your boot filesystem, please make sure it's in TEZI_EXTERNAL_KERNEL_DEVICETREE or being provided by virtual/dtb."
fi
overlays="$overlays $dtbo"
done
echo "fdt_overlays=$(echo $overlays)" > ${WORKDIR}/bootfs/overlays.txt
}
TAR_IMAGE_ROOTFS_task-image-bootfs = "${WORKDIR}/bootfs"
IMAGE_CMD_bootfs () {
:
}
TEZI_IMAGE_BOOTFS_PREFUNCS ??= "tezi_deploy_bootfs_files tezi_deploy_dt_overlays"
do_image_bootfs[prefuncs] += "${TEZI_IMAGE_BOOTFS_PREFUNCS}"
TEZI_IMAGE_TEZIIMG_PREFUNCS ??= "rootfs_tezi_run_json"
IMAGE_TYPEDEP_teziimg += "${TEZI_BOOT_SUFFIX} ${TEZI_ROOT_SUFFIX}"
IMAGE_CMD_teziimg () {
bbnote "Create Toradex Easy Installer tarball"
# Copy image json file to ${WORKDIR}/image-json
cp ${IMGDEPLOYDIR}/image*.json ${WORKDIR}/image-json/image.json
# The first transform strips all folders from the files to tar, the
# second transform "moves" them in a subfolder ${TEZI_IMAGE_NAME}-Tezi_${TEZI_VERSION}.
${IMAGE_CMD_TAR} \
--transform='s/.*\///' \
--transform 's,^,${TEZI_IMAGE_NAME}-Tezi_${TEZI_VERSION}-Embarcados_${@d.getVar('EMBARCADOS_RELEASE_VERSION')}/,' \
-chf ${IMGDEPLOYDIR}/${TEZI_IMAGE_NAME}-Tezi_${TEZI_VERSION}-Embarcados_${@d.getVar('EMBARCADOS_RELEASE_VERSION')}.tar \
logo_embarcados.png marketing_embarcados.tar prepare_emb.sh wrapup_emb.sh \
${WORKDIR}/image-json/image.json ${TEZI_ARTIFACTS}
}
do_image_teziimg[dirs] += "${WORKDIR}/image-json ${DEPLOY_DIR_IMAGE}"
do_image_teziimg[cleandirs] += "${WORKDIR}/image-json"
do_image_teziimg[prefuncs] += "${TEZI_IMAGE_TEZIIMG_PREFUNCS}"
do_image_teziimg[recrdeptask] += "do_deploy"
do_image_teziimg[vardepsexclude] = "TEZI_VERSION TEZI_DATE"
Note que mudamos todas as referências aos arquivos de identidade visual como logo_embarcados.png, marketing_embarcados.tar e também prepare_emb.sh e wrapup_emb.sh. Note também que foi mudada a string que será utilizada no nome da imagem. Pesquise pela palavra “embarcados” no arquivo e veja todas as modificações. Veja também que adicionamos uma variável EMBARCADOS_RELEASE_VERSION que será utilizada mais a frente.
Outra variável importante que indica dependência da nossa receita embarcados-te-metadata.bb é a seguinte:
WKS_FILE_DEPENDS_append = " embarcados-tezi-metadata virtual/dtb "
A estrutura final do layer meta-toradex ficou da seguinte maneira:
Geração da Imagem com Yocto
Ainda um último passo antes de gerar a imagem é adicionar algumas variáveis de ambiente no arquivo local.conf. São elas:
IMAGE_CLASSES_append = " image_type_tezi_embarcados"
EMBARCADOS_RELEASE_VERSION = "v1.0"
A primeira variável indica a nossa classe image_type_tezi_embarcados e a segunda variável é uma string de versionamento para ser exibida no Toradex Easy Installer.
Quando o layer e seus arquivos estiverem prontos, podemos iniciar o processo de geração da imagem com o comando abaixo:
$ bitbake tdx-reference-minimal-image
Dependendo das configurações de processador e memória RAM do seu computador o processo pode levar algumas horas ou até mais. É preciso ter paciência nesse primeiro build.
Ao final do processo a imagem gerada deve se encontrar na pasta build/deploy/images/colibri-imx6
Note que nossa string Embarcados_v1.0 apareceu no nome do arquivo.
Descompactando essa imagem podemos ver o seu conteúdo. Logo notamos que os arquivos da identidade visual foram colocados nos lugares corretos como também as referências no arquivo image.json.
Servidor de Imagens Toradex Easy Installer com Zeroconf
Para instalar essa imagem existem algumas formas. Utilizar um cartão SD ou servir a imagem localmente através da rede. A Toradex tem um guia mostrando como realizar esse setup utilizando Zeroconf no seguinte link:
Carregando o Easy Installer no Módulo
Os módulos da Toradex devem vir com a imagem Toradex Easy Installer já pré gravada, mas caso não venha existe um guia para carregar a imagem Toradex Easy Installer nos módulos:
Resultado Final e Instalação da Imagem
Ao carregar a interface do Toradex Easy Installer via VNC, será possível ver nossa imagem customizada listada. Note o logo do Embarcados e o nome customizado da imagem.
Ao iniciar a gravação será possível ver os banners sendo exibidos. Como a imagem é muito pequena e o processo de gravação é rápido, podemos ver apenas 2 dos banners, mas caso a imagem fosse maior poderíamos ver todos os 3.
Conclusão
Vimos neste artigo como utilizar as ferramentas da Toradex para gerar uma imagem compatível com o Toradex Easy Installer incluindo a identidade visual do Portal Embarcados. Isso é útil quando temos uma aplicação ou imagem de demonstração utilizando algum SoM da Toradex ou quando queremos melhorar a experiência de alguém que irá instalar nossa imagem no SoM, por exemplo alguém do time de produção ou testes.
Existe ainda uma maneira mais fácil de mudar os ícones mas talvez com menos possibilidade de customização. Ao invés de criar uma receita e classe, é possível utilizar apenas um bbappend como no exemplo abaixo:
Se quiser baixar o layer completo e já sair gerando a imagem segue o link do meu GitHub:
https://github.com/giobauermeister/meta-embarcados
E você já utilizou esse processo? Sua empresa utiliza módulos da Toradex? Como foi sua experiência com o Toradex Easy Installer? Comente abaixo suas experiências e dúvidas.
Saiba mais
https://embarcados.com.br/yocto-project-para-toradex-colibri-i-mx7/






