Skip to content

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KoboEink

Elixir library that initializes the e-ink display on Kobo e-readers running Nerves, so that fbink_nif can drive the screen.

Kobo devices (tested on the Clara Colour, MT8113 SoC) ship with proprietary userspace daemons that must be running before the kernel's HWTCON framebuffer is fully operational. Because these binaries live on the stock Kobo root partition and cannot be redistributed, they are extracted from the device's own eMMC at build time by kobo_firmware's one-time mix kobo.setup step and baked into the firmware image. This library then starts the required services under OTP supervision at boot.

What it does

  1. Starts display services in order:

    • Mounts the init_bin partition which contains the waveform LUT and CFA LUT files needed by the hwtcon kernel driver
    • Creates REGAL waveform device nodes in /dev
    • Starts mdpd (MediaTek Display Processing Daemon, supervised via MuonTrap.Daemon)
    • Starts nvram_daemon (NVRAM calibration/config service, supervised via MuonTrap.Daemon)
  2. Broadcasts events so your application knows exactly when /dev/fb0 is ready for FBInk.

Installation

Add kobo_eink to your dependencies in mix.exs:

def deps do
  [
    {:kobo_eink, github: "Spin42/ex_kobo_eink"}
  ]
end

kobo_eink pulls in kobo_firmware (for the device manifest) and muontrap automatically.

Usage

The application starts automatically. Subscribe to get notified when the display is ready:

defmodule MyApp.Display do
  use GenServer

  def start_link(_), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)

  @impl true
  def init(nil) do
    KoboEink.subscribe()
    {:ok, :waiting}
  end

  @impl true
  def handle_info({:kobo_eink, :ready}, _state) do
    {:ok, fd} = FBInk.open()
    FBInk.init(fd, %FBInk.Config{})
    FBInk.print(fd, "Hello from Nerves!", %FBInk.Config{})
    {:noreply, {:ready, fd}}
  end

  def handle_info({:kobo_eink, {:error, reason}}, _state) do
    require Logger
    Logger.error("E-ink init failed: #{inspect(reason)}")
    {:noreply, {:error, reason}}
  end

  def handle_info({:kobo_eink, phase}, state) do
    require Logger
    Logger.info("E-ink init phase: #{phase}")
    {:noreply, state}
  end
end

Events

Subscribers receive {:kobo_eink, event} messages:

Event Meaning
:starting_services Starting mdpd, nvram_daemon
:services_started All services running
:ready /dev/fb0 is ready for fbink_nif
{:error, reason} Initialization failed

If you subscribe after initialization has already completed (or failed), you immediately receive the current terminal state.

One-off status check

KoboEink.status()
# :ready | :initializing | :starting_services | {:error, reason}

Stopping services

KoboEink.stop_services()

Configuration

Device-specific paths (init_bin partition, mount point, daemon binary paths) are provided by KoboFirmware.Manifest, keyed by device codename. Configure the codename in your Nerves project:

config :kobo_firmware, codename: "spa_colour"

See KoboFirmware.Manifest for the per-device values and how to override them.

How it works

This library is the Elixir/Nerves equivalent of the S20kobo-eink init script from buildroot-kobo. The Kobo Clara Colour uses a MediaTek MT8113 SoC with a kernel-level HWTCON driver that provides /dev/fb0. However, the framebuffer isn't usable until several proprietary userspace daemons are running.

Because the device uses Secure Boot, the proprietary daemons can't be freely redistributed. Instead, mix kobo.setup (from kobo_firmware) extracts them from the stock Kobo root filesystem on the device's eMMC and bakes them into the firmware image's read-only squashfs overlay, so they are already on the filesystem when this library starts.

Daemons are started as MuonTrap.Daemon processes under KoboEink.DaemonSupervisor (a DynamicSupervisor), giving proper OTP supervision, automatic restart on crash, and clean shutdown without manual PID tracking.

The initialization sequence:

Kernel (HWTCON + PMIC drivers built-in)
  |
  +-- /dev/fb0 exists (waveform loaded lazily on first update)
  |
KoboEink.Init GenServer starts
  |
  +-- Mount init_bin partition  (waveform + CFA LUTs)
  +-- REGAL waveform device node setup
  +-- mdpd -f                  (MuonTrap.Daemon)
  +-- nvram_daemon              (MuonTrap.Daemon)
  |
  +-- :ready -- /dev/fb0 is now usable by FBInk/fbink_nif

License

MIT License. See LICENSE for details.

About

Kobo eink initialization for Nerves

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages