root@mine:~# adb devices List of devices attached 0123456789ABCDEF device
Sure, why would you ever connect more than one android device... At least rooting is easy (or maybe someone else had already done it).
root@mine:~# adb -s 0123456789ABCDEF shell # id uid=0(root) gid=0(root)
# cat /proc/version Linux version 2.6.35.7-tcc (keyu@emdoor-r710) (gcc version 4.4.3 (GCC) ) #51 Wed Oct 26 10:10:41 HKT 2011
Let's first try to just sync the debian-jessie from another tables and hope for it to work on a 2.6 kernel.
Mostly it did, except that Android's mount needed an explicit source even for remounts:
nova / % cat start.sh setprop ctl.stop media setprop ctl.stop zygote sleep 1 setprop ctl.stop bootanim sleep 1 setprop ctl.stop bootanim mount -o remount,rw,suid,dev /dev/block/mtdblock5 /data mount -o remount,rw /dev/block/mtdblock2 /system mount -t proc proc /data/debian-jessie/proc mount -t sysfs sysfs /data/debian-jessie/sys mount -t devpts devpts /data/debian-jessie/dev/pts mount -o bind /mnt/sdcard /data/debian-jessie/sdcard
So, will X just start?
(++) using VT number 1 (EE) Fatal server error: (EE) xf86OpenConsole: Cannot open virtual console 1 (No such device or address) (EE) (EE) Please consult the The X.Org Foundation support at http://wiki.x.org for help. (EE) Please also check the log file at "/var/log/Xorg.0.log" for additional information.
... of course not.
After some spelunking, it seems that I can re-use /dev/tcc-uart0 as a valid terminal for X to "run on".
However, starting Xorg would now reboot the system.
On the other hand, a simple
nova /dev % dd if=urandom of=fb0
would produce random dots quite fine (and not crash).
So probably the Xorg server is setting some not-so-good video mode.
nova / % gdb Xorg (gdb) break ioctl Breakpoint 1 at 0x4035610c: file ../sysdeps/unix/syscall-template.S, line 82. (gdb) run -sharevts -noreset -retro -verbose vt1 (II) FBDEV: driver for framebuffer: fbdev (++) using VT number 1 Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 ../sysdeps/unix/syscall-template.S: No such file or directory. (gdb) print /x $r1 $1 = 0x5603 (gdb) cont Continuing. [tcsetpgrp failed in terminal_inferior: Operation not permitted] (WW) xf86OpenConsole: VT_GETSTATE failed: Invalid argument (WW) Falling back to old probe method for fbdev (II) Loading /usr/lib/xorg/modules/libfbdevhw.so (II) Module fbdevhw: vendor="X.Org Foundation" compiled for 1.16.4, module version = 0.0.2 (II) FBDEV(0): using /dev/fb0 (WW) VGA arbiter: cannot open kernel arbiter, no multi-card support Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $2 = 0x4602 (gdb) cont Continuing. Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $3 = 0x4600 (gdb) print /x $r2 $3 = 0x2a199ab0 (gdb) step Single stepping until exit from function fbdevHWInit, which has no line number information. Warning: Cannot insert breakpoint 0. Cannot access memory at address 0x320 0x408df1d6 in ?? () from /usr/lib/xorg/modules/drivers/fbdev_drv.so (gdb) x /20lx 0x2a199ab0 0x2a199ab0: 0x00000320 0x00000258 0x00000320 0x000004b0 0x2a199ac0: 0x00000000 0x00000000 0x00000020 0x00000000 0x2a199ad0: 0x00000010 0x00000008 0x00000000 0x00000008 0x2a199ae0: 0x00000008 0x00000000 0x00000000 0x00000008 0x2a199af0: 0x00000000 0x00000018 0x00000008 0x00000000 (gdb) 0x2a199b00: 0x00000000 0x00000010 0x0000005f 0x0000007f 0x2a199b10: 0x00000000 0x00000000 0x00000000 0x00000000 0x2a199b20: 0x00000000 0x00000000 0x00000000 0x00000000 0x2a199b30: 0x00000000 0x00000000 0x00000000 0x00000000 0x2a199b40: 0x00000000 0x00000000 0x00000000 0x00000000
Comparing struct fb_var_creeninfo, this looks pretty sane.
(gdb) cont Continuing. (==) FBDEV(0): Depth 24, (==) framebuffer bpp 32 (==) FBDEV(0): RGB weight 888 (==) FBDEV(0): Default visual is TrueColor (==) FBDEV(0): Using gamma correction (1.0, 1.0, 1.0) (II) FBDEV(0): hardware: tccfb (video memory: 3750kB) (II) FBDEV(0): checking modes against framebuffer device... Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $5 = 0x4601 (gdb) set $r1 = 0x4600 (gdb) cont Continuing. (II) FBDEV(0): mode "800x600" ok (II) FBDEV(0): checking modes against monitor... (--) FBDEV(0): Virtual size is 800x600 (pitch 800) (**) FBDEV(0): Built-in mode "current" (==) FBDEV(0): DPI set to (96, 96) (II) Loading /usr/lib/xorg/modules/libfb.so (II) Module fb: vendor="X.Org Foundation" compiled for 1.16.4, module version = 1.0.0 (**) FBDEV(0): using shadow framebuffer (II) Loading /usr/lib/xorg/modules/libshadow.so (II) Module shadow: vendor="X.Org Foundation" compiled for 1.16.4, module version = 1.1.0 fbdev: PreInit done (==) Depth 24 pixmap format is 32 bpp fbdev: FBDevScreenInit 0 Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $6 = 0x4600 (gdb) cont Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $7 = 0x4601 (gdb) set $r1 = 0x4600 (gdb) cont Continuing. Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $8 = 0x4602 (gdb) cont Continuing. Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $9 = 0x4600 (gdb) cont Continuing. Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $10 = 0x4611 (gdb) cont Continuing. Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S (gdb) print /x $r1 $11 = 0x4606 (gdb) cont Continuing.
... and hard reset. So either it's something else entirely or the FBIOPAN_DISPLAY ioctly was a bad idea.
Let's see.
.... Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S 1: /x $r1 = 0x4606 (gdb) set $r1 = 0x4600 (gdb) cont Continuing.
... and hard reset.
Hm, maybe something is wrong with the memory layout as returned by FBIOGET_FSCREENINFO?
Commands such as
nova /dev % while sleep 0.025; do echo -ne '\xff\0\x00\xff'; done > /dev/fb0
and a keen eye (in particular on the timing of wrapping) tell us that it is 8bit blue, 8bit green, 8bit red, 8bit alpha, no extra padding per row.
Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S 1: /x $r1 = 0x4602 (gdb) print /x $r2 $1 = 0x2a199a6c (gdb) step 0x4093091e in fbdevHWInit () from /usr/lib/xorg/modules/libfbdevhw.so 1: /x $r1 = 0x4602 (gdb) x /20lx 0x2a199a6c 0x2a199a6c: 0x66636374 0x00000062 0x00000000 0x00000000 0x2a199a7c: 0x58000000 0x003a9800 0x00000000 0x00000000 0x2a199a8c: 0x00000002 0x00010000 0x00000000 0x00000c80 0x2a199a9c: 0x00000000 0x00000000 0x00000000 0x00000000 0x2a199aac: 0x00000000 0x00000000 0x00000000 0x00000000
But even with careful (or not so much) manipulation of the returned fb_fix_screeninfo I could not get it to stay alive.
Maybe it's dying on some other syscall? Time for another debugging tool.
nova / % apt-get install strace nova / % strace -ttf Xorg -sharevts -noreset -retro -verbose vt1 ... 14:13:27.695206 clock_gettime(CLOCK_MONOTONIC, {241, 976042669}) = 0 14:13:27.695332 write(0, "[ 241.976] ", 13) = 13 14:13:27.695471 write(0, "fbdev: FBDevScreenInit 0\n", 25) = 25 14:13:27.695681 mmap2(NULL, 3842048, PROT_READ|PROT_WRITE, MAP_SHARED, 13, 0) = 0x40980000 14:13:27.696053 ioctl(13, FBIOGET_VSCREENINFO, 0x2a199b50) = 0 14:13:27.696191 ioctl(13, FBIOPUT_VSCREENINFO, 0xbe92984c) = 0 14:13:27.698429 ioctl(13, FBIOGET_FSCREENINFO, 0x2a199a6c) = 0 14:13:27.698526 ioctl(13, FBIOGET_VSCREENINFO, 0x2a199ab0) = 0 14:13:27.698648 ioctl(13, FBIOBLANK, 0x1) = 0 14:13:27.698765 ioctl(13, FBIOPAN_DISPLAY, 0x2a199ab0) = 0 14:13:27.709122 mmap2(NULL, 15364096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40d2a000 14:13:27.738673 write(2, "(==) FBDEV(0): Backing store ena"..., 37(==) FBDEV(0): Backing store enabled ) = 37 14:13:27.738927 clock_gettime(CLOCK_MONOTONIC, {242, 19778169}) = 0 14:13:27.739091 write(0, "[ 242.019] ", 13root@mine:~#
Hm, so maybe it's actually dying on memory access to the FB ram?
Breakpoint 1, ioctl () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S 1: /x $r1 = 0x4606 (gdb) display /i $pc 2: x/i $pc => 0x4035610c <ioctl+12>: it cc (gdb) stepi 0x4035610e 82 in ../sysdeps/unix/syscall-template.S 2: x/i $pc => 0x4035610e <ioctl+14>: bxcc lr 1: /x $r1 = 0x4606 (gdb) undisplay 1 (gdb) break mmap Breakpoint 2 at 0x40358b52: file ../ports/sysdeps/unix/sysv/linux/arm/mmap.S, line 29. (gdb) cont Continuing. Breakpoint 2, mmap () at ../ports/sysdeps/unix/sysv/linux/arm/mmap.S:29 29 ../ports/sysdeps/unix/sysv/linux/arm/mmap.S: No such file or directory. 2: x/i $pc => 0x40358b52 <mmap+2>: ldr r5, [sp, #8] (gdb) stepi ... (for a very long time) ... (gdb) bt #0 0x4095011e in fbInitVisuals () from /usr/lib/xorg/modules/libfb.so #1 0x40955186 in fbFinishScreenInit () from /usr/lib/xorg/modules/libfb.so #2 0x409552fc in fbScreenInit () from /usr/lib/xorg/modules/libfb.so #3 0x408ded98 in ?? () from /usr/lib/xorg/modules/drivers/fbdev_drv.so (gdb) stepi ... Program received signal SIGSEGV, Segmentation fault. 0x408dedb8 in ?? () from /usr/lib/xorg/modules/drivers/fbdev_drv.so
:( By observing Xorg, we changed its behavior. Not cool.
At this point I checked if the touchscreen is worth spending more work on,
nova / % xxd /dev/input/event2
shows quite responsive events even with a stylus. So yeah, as slow as the tablet seemed when running Android, this is pretty snappy.
So let's do some experiments:
nova ~ % cat test.c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> int main(void) { int fb = open("/dev/fb0", O_RDWR); unsigned int *pixels = mmap((void *)0, 800 * 600 * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0); for(int x = 0; x < 800; ++x) { for(int y = 0; y < 600; ++y) { pixels[y * 800 + x] = (x + y) | 0xff000000; } } return 0; } nova ~ % gcc test.c -o test test.c: In function 'main': test.c:10:2: error: 'for' loop initial declarations are only allowed in C99 or C11 mode for(int x = 0; x < 800; ++x) { ^ test.c:10:2: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code test.c:11:3: error: 'for' loop initial declarations are only allowed in C99 or C11 mode for(int y = 0; y < 600; ++y) { ^
Riiiiight...
nova ~ % gcc -std=c99 test.c -o test
Works like a charm. So clearly, just mapping the fb and drawing to it works as expected. Even writing to the full advertised virtual y resolution of 1200 works. So does writing to the mapping with byte-oriented access, so it's not an obvious alignment issue either.
But even when disabling all of FBIOPUT_VSCREENINFO, FBIOPAN_DISPLAY and FBIOBLANK the system would still hard-reset on starting Xorg.
Given the aim here is not to produce a high quality system, but just a worksforme, how about making a fbdev-dummy hybrid driver instead of trying to debug whatever fbdev is actuall doing?
nova ~ % git clone https://gitlab.freedesktop.org/xorg/driver/xf86-video-dummy ... after some failing compiles ... nove ~ % git checkout 8706f60ab457867c120dd44e812b8fadc2be7179 nova ~/xf86-video-dummy % make gcc -std=c99 \ -DMULTITOUCH -DPACKAGE_VERSION_MAJOR=2 -DPACKAGE_VERSION_MINOR=9 -DPACKAGE_VERSION_PATCHLEVEL=2 \ -I src -I include -I /usr/include/libevdev-1.0 -I /usr/include/xorg -I /usr/include/pixman-1 \ -fPIC -shared -o yolo.so src/*.c src/dummy_dga.c: In function 'DUMMYDGAInit': src/dummy_dga.c:103:4: warning: implicit declaration of function 'DGAInit' [-Wimplicit-function-declaration] return DGAInit(pScreen, &DUMMYDGAFuncs, modes, num); ^ In file included from src/dummy_driver.c:28:0: /usr/include/xorg/fb.h:94:2: error: #error "GLYPHPADBYTES must be 4" #error "GLYPHPADBYTES must be 4" ^ Makefile:2: recipe for target 'yolo.so' failed make: *** [yolo.so] Error 1
Given that it needs to be 4, well let's make it 4. Also we apparently need XORG_VERSION_CURRENT, so
nova ~/xf86-video-dummy % git diff diff --git a/src/dummy_driver.c b/src/dummy_driver.c index 9d4d5bf..9593307 100644 --- a/src/dummy_driver.c +++ b/src/dummy_driver.c @@ -14,6 +14,8 @@ /* All drivers initialising the SW cursor need this */ #include "mipointer.h" +#include "xorg/xorg-server.h" + /* All drivers using the mi colormap manipulation need this */ #include "micmap.h" nova ~/xf86-video-dummy % make install gcc -std=c99 \ -DMULTITOUCH -DPACKAGE_VERSION_MAJOR=2 -DPACKAGE_VERSION_MINOR=9 -DPACKAGE_VERSION_PATCHLEVEL=2 -DGLYPHPADBYTES=4 -DXFree86LOADER \ -I src -I include -I /usr/include/libevdev-1.0 -I /usr/include/xorg -I /usr/include/pixman-1 \ -fPIC -shared -o yolo.so src/*.c src/dummy_dga.c: In function 'DUMMYDGAInit': src/dummy_dga.c:103:4: warning: implicit declaration of function 'DGAInit' [-Wimplicit-function-declaration] return DGAInit(pScreen, &DUMMYDGAFuncs, modes, num); ^ cp yolo.so /usr/lib/xorg/modules/drivers/dummy_drv.so
Now changing our video driver to "dummy" and we can start Xorg (but don't see a thing yet). Which is expected. We still need to move our pixmap data onto the actual /dev/fb0.
After some experiments it became apparent that mmap(2)-ing /dev/fb0 would not work reliably, but normal file I/O to it would always work. So I created the least quality code which would still do the blit for me:
nova ~/xf86-video-dummy % git diff diff --git a/src/dummy_driver.c b/src/dummy_driver.c index 9d4d5bf..4ae0113 100644 --- a/src/dummy_driver.c +++ b/src/dummy_driver.c @@ -3,6 +3,14 @@ * Copyright 2002, SuSE Linux AG, Author: Egbert Eich */ +#define _GNU_SOURCE +#include <sched.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -14,6 +22,8 @@ /* All drivers initialising the SW cursor need this */ #include "mipointer.h" +#include "xorg/xorg-server.h" + /* All drivers using the mi colormap manipulation need this */ #include "micmap.h" @@ -363,15 +373,9 @@ DUMMYPreInit(ScrnInfoPtr pScrn, int flags) xf86GetOptValBool(dPtr->Options, OPTION_SW_CURSOR,&dPtr->swCursor); - if (device->videoRam != 0) { - pScrn->videoRam = device->videoRam; - xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "VideoRAM: %d kByte\n", - pScrn->videoRam); - } else { - pScrn->videoRam = 4096; - xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "VideoRAM: %d kByte\n", - pScrn->videoRam); - } + pScrn->videoRam = 800 * 600 * 4 / 1024; + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "VideoRAM: %d kByte\n", + pScrn->videoRam); if (device->dacSpeeds[0] != 0) { maxClock = device->dacSpeeds[0]; @@ -515,6 +519,26 @@ DUMMYLoadPalette( static ScrnInfoPtr DUMMYScrn; /* static-globalize it */ +static int blitter(void *xorgVideoVoid) { + unsigned int *xorgVideo = xorgVideoVoid; + + unsigned int *interim = malloc(800 * 600 * 4); + + while(1) { + usleep(20000); + for(int y = 0; y < 600; ++y) { + for(int x = 0; x < 800; ++x) { + interim[800 * (599 - y) + (799 - x)] = xorgVideo[800 * y + x] | 0xff000000ul; + } + } + + int fb = open("/dev/fb0", O_RDWR); + write(fb, interim, 800 * 600 * 4); + write(fb, interim, 800 * 600 * 4); + close(fb); + } +} + /* Mandatory */ static Bool DUMMYScreenInit(SCREEN_INIT_ARGS_DECL) @@ -535,6 +559,11 @@ DUMMYScreenInit(SCREEN_INIT_ARGS_DECL) if (!(dPtr->FBBase = malloc(pScrn->videoRam * 1024))) return FALSE; + + static int cloned = 0; + if (!cloned) { + clone(blitter, malloc(65536) + 65528, CLONE_IO | CLONE_VM, dPtr->FBBase); + } /* * next we save the current state and setup the first mode
(I hard-coded a rotation in there to accomodate the fact that the cabling works better for me if I turn the tablet around...)
But naturally things were not so easy, because the dummy-driver auto-selected a 1024x768 mode, and primitive attempts to configure 800x600 via xorg.conf did not work because the DotClock would be wrong: Some layer within X decided that I'd be sending the 800 pixels per row too slow or too fast to "the monitor" for this to possibly work, (which was of course a 100% imagined problem).
After some trial and error, I arrived at
nova / % cat /etc/X11/xorg.conf Section "ServerLayout" Identifier "Layout0" Screen "Screen0" InputDevice "Mouse0" "CorePointer" InputDevice "Keyboard0" "CoreKeyboard" EndSection Section "InputDevice" Identifier "Keyboard0" Driver "evdev" Option "Device" "/dev/input/event1" Option "Protocol" "usb" EndSection Section "InputDevice" Identifier "Mouse0" Driver "evdev" Option "Device" "/dev/input/event2" Option "IgnoreRelativeAxes" "true" Option "IgnoreAbsoluteAxes" "false" Option "InvertX" "true" Option "InvertY" "true" Option "Mode" "Absolute" EndSection Section "Device" Identifier "Card0" Driver "dummy" Option "debug" "true" VendorName "Unknown" BoardName "Unknown" EndSection Section "Screen" Identifier "Screen0" Device "Card0" Monitor "Monitor0" SubSection "Display" Modes "800x600" EndSubSection EndSection Section "Monitor" Identifier "Monitor0" Mode "800x600" DotClock 30.0 HTimings 800 805 810 815 VTimings 600 605 610 615 Flags "-HSync" "-VSync" EndMode EndSection Section "ServerFlags" Option "AutoAddDevices" "false" EndSection
In case you need it, the panel backlight is controllable at
/sys/class/leds/lcd-backlight
Two words of caution: First, the delay with vnc + the asynchronous blitting is a bit ugly (but still bearable for me, if I'm a bit careful what kind of things I put onto the display). And the device gets surprisingly warm. One factor to this is CPU consumption by a process "usb_switch", the termination of which I finally included in
nova / % cat start.sh setprop ctl.stop media setprop ctl.stop zygote sleep 1 setprop ctl.stop bootanim sleep 1 setprop ctl.stop bootanim mount -o remount,rw,suid,dev /dev/block/mtdblock5 /data mount -o remount,rw /dev/block/mtdblock2 /system mount -t proc proc /data/debian-jessie/proc mount -t sysfs sysfs /data/debian-jessie/sys mount -t devpts devpts /data/debian-jessie/dev/pts mount -o bind /mnt/sdcard /data/debian-jessie/sdcard sleep 1 killall usb_switch
But having a device to heat myself can be quite nice (esp. compared to the common strategy to heat the entire room).
After integration into the multi-monitor setup I found the device would occasionally register phantom touches, which are pretty distracting when they teleport the mouse cursor (and with it the keyboard focus). So I ultimately disabled touch-input.