Skip to content

Framebuffer Overview

Can Selcik edited this page Apr 25, 2018 · 9 revisions

Current framebuffer can be dumped into a PNG with:

ssh [email protected] "cat /dev/fb0" | \
   convert -depth 16 -size 1408x1872+0 gray:- png:/tmp/frame.png

Looks like the latest version of ImageMagick has changed the command line parameters for the gray16_le pixel format so here is the alternative for the command above using ffmpeg instead of convert:

ssh [email protected] "cat /dev/fb0" | \
  ffmpeg -vcodec rawvideo \ 
         -f rawvideo \
         -pix_fmt gray16le \
         -s 1408,1872 \
         -i - \
         -vframes 1 
         -f image2
         -vcodec mjpeg /tmp/frame.jpg

The pixel format for the framebuffer is rgb565_le due to the following:

    red     : offset = 11,  length =5,      msb_right = 0
    green   : offset = 5,   length =6,      msb_right = 0
    blue    : offset = 0,   length =5,      msb_right = 0
    transp  : offset = 0,   length =0,      msb_right = 0

However, given the nature of the display, it can be thought as gray16_le.

This is what it looks like when interpreted as gray16_le: gray16le.bmp

And as rgb565_le: rgb565le.bmp

Remarkable Paper Tablet has an undocumented API for partial refreshes on its eInk display, which is what's behind its magic that disappears when custom Qt applications are used to draw on the screen, even using the toolchain provided by Remarkable.

The xochitl program opens /dev/fb0, which always ends up being the FD=3. It then writes to this FD when it wants to update the screen and uses primarily the following ioctl call in order to perform its partial updates when the user draws on the device (0x4048462e is the PARTIAL_UPDATE_MAGIC, and the next argument is a pointer to mxcfb_update_data):

What is particularly interesting here is that 0x4048462e also happens to be Kindle's MXCFB_SEND_UPDATE magic. Something to keep in mind.

typedef struct {
  uint32_t top;    // 0x0000
  uint32_t left;   // 0x0000 
  uint32_t width;  // 0x0258 = 600
  uint32_t height; // 0x0320 = 800
} mxcfb_rect;

typedef struct {
  mxcfb_rect update_region;

  uint32_t waveform_mode;  // 0x0002 = WAVEFORM_MODE_GC16 
  uint32_t update_mode;    // 0x0000 = UPDATE_MODE_PARTIAL 
  uint32_t update_marker;  // 0x002a 
  
  int temp;   // 0x1001 = TEMP_USE_PAPYRUS
  uint flags; // 0x0000 
  
  void* alt_buffer_data; // must not used when flags is 0
  ...
} mxcfb_update_data;

ioctl(3, 0x4048462e, 0x7ea2d290{
   updateRegion: x: 0
                 y: 0
                 width: 1404
                 height: 1872
   waveformMode: 3,
   updateMode:   0
   updateMarker: 46
   temp: 4096
   flags: 0000
   alt_buffer_data: 0x300f30
   ...
}) == 0

The xochitl program is statically linked with the QsgEpaperPlugin which can be found in this repository with the filename libqsgepaper.a. These implementations contained within that library, however, are not used in the PoC as they are not yet fully explored and entirely undocumented. What is used instead is skipping what libqsgepaper can achieve with its undocumented portions listed here and instead gaining lower level access to the hardware.

However, looking at the function signatures and the analysis so far, it looks like the PoC actually has gotten them right (EPFrameBuffer::WaveformMode, EPFrameBuffer::UpdateMode in EPFramebuffer::sendUpdate, returning a uint32_t refresh_marker that is referred to as an updateCounter in epframebuffer.o). The list of prototypes can be found at the end of this page.

Additionally take a look at the /usr/bin/remarkable-test program. It definitely offers interesting insights into the device.