Toradex Easy Installer Com a Identidade Visual de Sua Empresa

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.

https://www.yoctoproject.org/docs/3.1.1/mega-manual/mega-manual.html#required-packages-for-the-build-host

$ 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.

layers-oecore
Layers Yocto

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.

Exemplo ícone para Toradex Easy Installer
Exemplo de Ícone

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:

Exemplo banner 1 para Toradex Easy Installer
Exemplo de Banner 1
Exemplo banner 2 para Toradex Easy Installer
Exemplo de Banner 2

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:

Estrutura final layer meta-embarcados

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

Arquivo final da imagem gerada

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.

Arquivos extraídos da imagem final

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:

https://developer.toradex.com/knowledge-base/toradex-easy-installer-detailed-manual#From_a_Local_Web_Server_Announced_via_Zeroconf

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:

https://developer.toradex.com/knowledge-base/load-toradex-easy-installer#Loading_Toradex_Easy_Installer

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.

Tela inicial Toradex Easy Installer

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.

Banner 1
Banner 2

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:

https://github.com/toradex/meta-pasta-demo/blob/master/recipes-bsp/tezi-metadata/tezi-metadata_%25.bbappend

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/

https://embarcados.com.br/torizon-toradex/

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
0 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Linux Embarcado » Toradex Easy Installer Com a Identidade Visual de Sua Empresa

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: