Nerves.Firmware v0.4.0 Nerves.Firmware

API for upgrading and managing firmware on a Nerves device.

Handles firmware for a single block device (like /dev/mmcblk0). Delegates a lot to Frank Hunleth’s excellent fwup.

Provides:

  • Firmware upgrades
  • Firmware status
  • Firmware-related activities (shutdown, reboot, halt)

Looking for over-the-network firmware updates? see nerves_firmware_http, which provides an HTTP micro-service providing over-network firmware management.

Installation

  1. Add nerves_firmware to your list of dependencies in mix.exs:

    def deps do [{:nerves_firmware, “~> 0.4.0”}] end

  2. Ensure nerves_firmware is started before your application:

    def application do [applications: [:nerves_firmware]] end

Configuration

In your app’s config.exs, you can configure the block device for your target that is managed by setting the device key as follows:

  config :nerves_firmware, device: "dev/mmcblk0"

Summary

Types

Arguments to be passed to FWUP

Functions

Returns true if new firmware can currently be installed

Applies a firmware file to the device media

Applies /tmp/finalize.fw if with on-reboot task if exists,

Forces device to halt (meaning hang, not power off, nor reboot)

Forces device to power off (without reboot)

Reboot the device

Reboot the device gracefully

Application start callback

Return a map of information about the current firmware

Apply a 1 or 2-phase nerves update

Types

args()
args() :: [binary]

Arguments to be passed to FWUP.

reason()
reason() :: atom

Functions

allow_upgrade?()
allow_upgrade?() :: true | false

Returns true if new firmware can currently be installed.

The firmware module usually allows new firmware to be installed, but there are situations where installing new firmware is dangerous. Currently if the device has had an update applied without being restarted, this returns false, and update apis will return errors, to prevent bricking.

apply(firmware, action, args \\ [])
apply(String.t, atom, args) :: :ok | {:error, reason}

Applies a firmware file to the device media.

This mostly just passes information through to Nerves.Firmware.Fwup.apply(..) which is a very thin wrapper around fwup, but it also sets the firwmare state based on the action to reflect the update, and prevent multiple updates from overwriting known good firmware.

  • action can be one of :upgrade or :complete
  • args is a list of extra arguments to be passed to fwup.

Returns {:error, :await_restart} if the upgrade is requested after already updating an image without a reboot in-between.

finalize(args \\ [])
finalize(args) :: :ok | {:error, reason}

Applies /tmp/finalize.fw if with on-reboot task if exists,

  • args is a list of extra arguments to be passed to fwup, but is currently ignored for this function.

Returns {:error, :await_restart} if the finalize is requested after already updating an image without a reboot in-between.

halt()
halt() :: :ok

Forces device to halt (meaning hang, not power off, nor reboot).

Note: this is different than :erlang.halt(), which exists BEAM, and may end up rebooting the device if erlinit.conf settings allow reboot on exit.

poweroff()
poweroff() :: :ok

Forces device to power off (without reboot).

reboot()
reboot() :: :ok

Reboot the device.

Issues the os-level reboot command, which reboots the device, even if erlinit.conf specifies not to reboot on exit of the Erlang VM.

reboot(atom)
reboot(atom) :: :ok

Reboot the device gracefully

Issues :init.stop command to gracefully shutdown all applications in the Erlang VM. All code is unloaded and ports closed before the system terminates by calling halt(Status). erlinit.config must be set to reboot on exit(default) for a graceful reboot to work.

start(type, args)
start(atom, term) :: {:ok, pid} | {:error, String.t}

Application start callback.

state()
state() :: Map.t

Return a map of information about the current firmware.

This currently contains values showing the state of the firmware installation, as well as the key/value pairs encoded in the firmware itself.

status:

:active - Currently running the latest firmware received. Firmware must be in this state to be updated.

:await_restart - Firmware has been updated since restart, and a restart is needed to start running from the new firmware.

device:

The device file that holds the firmware, e.g. /dev/mmcblk0

upgrade_and_finalize(firmware, args \\ [])
upgrade_and_finalize(String.t, args) :: :ok | {:error, reason}

Apply a 1 or 2-phase nerves update

Applies firmware using upgrade task, then, if /tmp/finalize.fw exists, apply that file with on-reboot task. Supports @fhunleth 2-phase format.

  • args is a list of extra arguments to be passed to fwup.

Returns {:error, :await_restart} if the upgrade is requested after already updating an image without a reboot in-between.