{
  config
, pkgs
, lib
, ...
}:
let
  inherit (lib) mkOption types concatStringsSep;
  inherit (config.boot) tftp;
in {
  options.system.outputs = {
    firmware = mkOption {
      type = types.package;
      internal = true;          # component of mtdimage
      description = ''
        Binary image (combining kernel, FDT, rootfs, initramfs
        if needed, etc) for the target device.
      '';
    };
    flash-scr = mkOption {
      type = types.package;
      internal = true;          # component of mtdimage
      description = ''
        Copy-pastable U-Boot commands to TFTP download the
        image and write it to flash
      '';
    };
    mtdimage = mkOption {
      type = types.package;
      description = ''
        mtdimage
        **********

        This creates an image called :file:`firmware.bin` suitable for
        squashfs or jffs2 systems. It can be flashed from U-Boot (if
        you have a serial console connection), or on some devices from
        the vendor firmware, or from a Liminix kexecboot system.

        If you are flashing from U-Boot, the file
        :file:`flash.scr` is a sequence of commands
        which you can paste at the U-Boot prompt to
        to transfer the firmware file from a TFTP server and
        write it to flash. **Please read the script before
        running it: flash operations carry the potential to
        brick your device**

        .. NOTE::

           TTL serial connections typically have no form of flow
           control and so don't always like having massive chunks of
           text pasted into them - and U-Boot may drop characters
           while it's busy. So don't necessarily expect to copy-paste
           the whole of :file:`flash.scr` into a terminal emulator and
           have it work just like that. You may need to paste each
           line one at a time, or even retype it.
      '';
    };
  };

  config = {
    kernel = {
      config = {
        MTD_SPLIT_UIMAGE_FW = "y";
      } // lib.optionalAttrs (pkgs.stdenv.isMips) {
        # https://stackoverflow.com/questions/26466470/can-the-logical-erase-block-size-of-an-mtd-device-be-increased
        MTD_SPI_NOR_USE_4K_SECTORS = "n";
      };
    };

    programs.busybox.applets = [
      "flashcp"
    ];

    system.outputs = {
      firmware =
        let
          o = config.system.outputs;
          bs = toString config.hardware.flash.eraseBlockSize;
        in pkgs.runCommand "firmware" {} ''
          dd if=${o.uimage} of=$out bs=${bs} conv=sync
          dd if=${o.rootfs} of=$out bs=${bs} conv=sync,nocreat,notrunc oflag=append
        '';
      mtdimage =
        let o = config.system.outputs; in
        # could use trivial-builders.linkFarmFromDrvs here?
        pkgs.runCommand "mtdimage" {} ''
          mkdir $out
          cd $out
          ln -s ${o.firmware} firmware.bin
          ln -s ${o.rootfs} rootfs
          ln -s ${o.kernel} vmlinux
          ln -s ${o.manifest} manifest
          ln -s ${o.kernel.headers} build
          ln -s ${o.uimage} uimage
          ln -s ${o.dtb} dtb
          ln -s ${o.flash-scr} flash.scr
       '';

      flash-scr =
        let
          inherit (pkgs.lib.trivial) toHexString;
          inherit (config.hardware) flash;
        in
          pkgs.buildPackages.runCommand "" {} ''
            imageSize=$(stat -L -c %s ${config.system.outputs.firmware})
            cat > $out << EOF
            setenv serverip ${tftp.serverip}
            setenv ipaddr ${tftp.ipaddr}
            tftp 0x${toHexString tftp.loadAddress} result/firmware.bin
            erase 0x${toHexString flash.address} +0x${toHexString flash.size}
            cp.b 0x${toHexString tftp.loadAddress} 0x${toHexString flash.address} \''${filesize}
            echo command line was ${builtins.toJSON (concatStringsSep " " config.boot.commandLine)}
            EOF
          '';
    };
  };
}