From zero to hero

A couple of years ago a friend of mine asked me to take a look at a pico projector that he had bricked during an update. He hadn’t tried anything fancy or dangerous, he had simply used the internal functionality of the device. At that point in time the device was stuck in a bootloop with the Samsung logo as the only output from the projector.

I obviously accepted to take a look and in this way started a journey that lasted like five years where I learned a lot about hardware and software.

Information gathering

The device in question is the Samsung Pico Projector SP-H03, this is the product page; this item has been discontinued and from the few reviews I have seen it didn’t do very well commercially.

The user manual doesn’t talk about upgrading and it seems there isn’t any post in the internet talking about some particular flashing mode for this device; I’m on my own to discover.

Opening the device is possible to unveil the internals: on top there is a PCB with SMD components on both the sides

but no signs of a connector for the UART: indeed my idea throughout the investigation will be to discover the serial port and find out why the system doesn’t work.

From the front image I found that the main chip for this box is the TCC9101, a pratically unknown SoC used in a couple of other devices and cited in the Wikipedia page about ARM SoCs as an ARM1176JZ(F)-S, i.e. an ARMv6 architecture. Take in mind that not even the company producing the chip, Telechips, has a page specific about this chip, only cited in a page.


Another thing available to look at is the firmware update that you can download from the official site: P-OBRNPWWC-1008.1.rom.

With my incredible skills I discovered that is a simple tar archive

$ file Pico\ SP-H03/P-OBRNPWWC-1008.1.rom 
Pico SP-H03/P-OBRNPWWC-1008.1.rom: POSIX tar archive (GNU)

containing some files

$ tar -tf Pico\ SP-H03/P-OBRNPWWC-1008.1.rom
  more stuffs

The most promising for further investigation are TCCKernel7.3.rom and TCCBoot4.2.rom

$binwalk Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCBoot4.2.rom 


$ binwalk Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCKernel7.3.rom 

55177         0xD789          Certificate in DER format (x509 v3), header length: 4, sequence length: 1416
59013         0xE685          Certificate in DER format (x509 v3), header length: 4, sequence length: 13588
78208         0x13180         gzip compressed data, maximum compression, from Unix, last modified: 2010-07-27 01:42:34
4720605       0x4807DD        Certificate in DER format (x509 v3), header length: 4, sequence length: 5376
5024709       0x4CABC5        Certificate in DER format (x509 v3), header length: 4, sequence length: 1328
5419085       0x52B04D        Certificate in DER format (x509 v3), header length: 4, sequence length: 4866
5531093       0x5465D5        Certificate in DER format (x509 v3), header length: 4, sequence length: 1292
5702529       0x570381        Certificate in DER format (x509 v3), header length: 4, sequence length: 13588
6446763       0x625EAB        Linux kernel version 2.6.28
6477872       0x62D830        CRC32 polynomial table, little endian
6821729       0x681761        Unix path: /dev/vc/0
6834593       0x6849A1        IMG0 (VxWorks) header, size: 1111564346

looking at the strings inside you can have some clues

$ strings Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCBoot4.2.rom | grep -i tele
$ strings Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCBoot4.2.rom | grep -i boot
    tcboot ver %s for %s Linux
Updating tcboot.rom -> 
$ strings Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCKernel7.3.rom | grep -i samsung
Samsung Oberon Board
Samsung Info. Systems America, Inc.
$ strings Pico\ SP-H03/P-OBRNPWWC-1008.1/TCCBoot4.2.rom | grep -i usb
MAX8903A: USB Input.

So we can deduce that the board has as codename Oberon and that is based on the TCC89XX SoC.

This SoC has a little more information (by the way, a similar named SoC, the TCC8920, is a complete different ARM revision so I don’t know what to think anymore about the naming convention used by Telechips) and indeed is possible to find a torrent containing a lot of leaked materials about this family of chips (f21b0242eac7f91090eaa949c699d2bc9252ca85 is the hash of such torrent); the datasheet is comprised in the leak.


With the datasheet in my hands I started to reversing the supposed bootloader contained in the firmware update, at first using radare2 but when ghidra has been officialy released I switched to it making easier the process.

The reversing was not strictly necessary for the process I’m describing but helped me to exercise with the assembly and to know better the peripherals available on the device. In particular after have done it I was sure that TCCBoot4.2.rom was really the bootloader.

The first problem to overcome was to understand the header and the address where the bootloader is loaded: the first thing was pretty simple since I discovered a repository containing the source code of such bootloader (it would not have been such hard to deduce the fields looking at them or via the code in the bootloader).

Looking at a few addresses called from the firmware was apparent that it loads at 0x40000000.

Knowing that, and the address mapping from the datasheet, I set the following in ghidra

that are not perfect but good enough to allow a decent reversing; part of the mapping reported in the datasheet is the following

Address space Device name
0x00000000 - 0x0fffffff remapped region
0x10000000 - 0x10003fff 16kB on-chip RAM
0x40000000 - 0x4fffffff off-chip SDRAM
0xf0000000 - 0xffffffff on-chip peripherals

moreover the peripherals memory space is partitioned like the following

Base address Peripherals
0xf0000000 Graphic bus
0xf0100000 Memory bus
0xf0200000 DDI bus
0xf0300000 Memory bus
0xf0400000 System control
0xf0500000 IO bus
0xf0600000 MMU table
0xf0700000 Video bus

It’s amazing how with ghidra is possible to describe the registers that control for example the UART subsystem with a struct

and obtain a perfectly readable decompiled code


From the reading of several documents the presence of a USB update process seems pretty solid: in the datasheet itself is cited a so called USB boot mode that works in the following way

  1. The TCC8900 makes internal SRAM area starts from zero, and copies USB interrupt service routine to internal SRAM area.
  2. It waits until USB connection is established.
  3. Once it is connected, host transfers first the parameter for USB loader routine including start address, destination address and the amount of data to be transferred (with a unit of packet).
  4. The TCC8900 starts communicating between a host PC with fixed amount of data which is called as packet. The packet size of TCC8900 is 512 bytes.
  5. At every successful reception of packet, it copies them where the destination address pointed, and after all amount of data has been copied, it starts program where the start address pointed

It’s similar of what is described in the datasheet of TCC760 or that other people have found; it loads via the tcctool some code in SDRAM and executes it.

I discovered via the service manual (in chinese) that to enter in this mode you have to press a button (that is not soldered in my case) when you start the device (since the device has a touch surface with the power button on a side is a little tricky to press both at the same time)

Another approach is to short the CE pin of the NAND. What you see is a new USB device

turing kernel: usb 2-2: new high-speed USB device number 3 using xhci_hcd
turing kernel: usb 2-2: config 1 interface 0 altsetting 0 bulk endpoint 0x82 has invalid maxpacket 64
turing kernel: usb 2-2: config 1 interface 0 altsetting 0 bulk endpoint 0x1 has invalid maxpacket 64
turing kernel: usb 2-2: New USB device found, idVendor=140e, idProduct=b077
turing kernel: usb 2-2: New USB device strings: Mfr=0, Product=0, SerialNumber=0
turing kernel: usb 2-2: USB disconnect, device number 3

Modifing the code of tcctool, adding the correct VID:PID values and other minor modifications, I was able to execute code in the board and I started to write a piece of assembler code to toggle all the GPIO pins in order to find out where the serial was located on the board. There are 6 banks of GPIO with up to 32 pins each and to discover which GPIO corresponds to a given pin I driven it with a PWM signal having \(\hbox{bank} << 5\, |\, \hbox{id} \) cycles

Pico%20SP-H03/tccvirus.s view raw
	.align	2
	.syntax unified
	.fpu softvfp
	.type	__entry, %function
	.type	enable_watchdog, %function
	.type	gpio_identification, %function
	.type	gpio_signal, %function
	.type	watchdog_clear, %function
	.type	_delay, %function
 * Since we have not stack available for now (no hard constraint, we are only lazy)
 * we call routines and use r10, r11 and r12 as temporary registers to save lr.
	bl enable_watchdog
	bl gpio_identification @ uses r10
	bl watchdog_clear
	b gpio

.set  EN, (1<<0)
.set IEN, (1<<3)
.set TCKSEL, (1<<4)
.set WATCHDOG_EN, (1<<31)
.set WATCHDOG_CLR, (1<<30)

	mov	r10, lr
	ldr	r3, .TWDCFG
	ldrb	r4, [r3]
	orr	r4, r4, #(EN | IEN)
	strb	r4, [r3]
	ldrb	r4, [r3]
	bic	r4, r4, 0x30 @ clear the TCKSEL
	orr	r4, r4, 0x40 @ set the TCKSEL to 0x04
	strb	r4, [r3]
	ldr	r3, .TWDCLR_ADDR
	mov r4, #0x00000000
	str	r4, [r3]
	ldr	r4, [r3]
	orr	r4, r4, #WATCHDOG_EN
	str	r4, [r3]
	mov	lr, r10
	mov pc, lr

	mov	r12, lr
	ldr	r1, [r0]
	orr	r1, r1, #WATCHDOG_CLR
	str	r1, [r0]
	mov lr, r12
	mov pc, lr

	.word	0xf0404000
	.word	0xf0403070
	.word	0xf0403074
	.word	0xf040400c

.set CONTROL_POFF, (1<<1)

	ldr	r3, .CONTROL_ADDR
	ldr	r4, [r3]
	orr	r4, r4, #CONTROL_POFF
	str	r4, [r3]
 * This function outputs via the GPIO, passed as the couple (group, idx)
 * its encoded identifier as a 32 bit number.
 *  r8: group in [0, 5]
 *  r9: idx in [0, 31]
 * so we need a number of bits to encode 6*32 = 192 values, i.e. 8bit are enough,
 * but we can use all 32-bit so that we have a start/stop pattern clearly
 * identifiable.
.set GPIO_GPxEN_OFFSET,  0x04
.set GPIO_GPxPD0_OFFSET, 0x1c
.set GPIO_GPxPD1_OFFSET, 0x20
.set GPIO_GPxFN0_OFFSET, 0x24
.set GPIO_GPxFN1_OFFSET, 0x28
.set GPIO_GPxFN2_OFFSET, 0x2c
.set GPIO_GPxFN3_OFFSET, 0x30
	mov	r11, lr                     @ save the return pointer
	/* initialize the GPIO requested in OUTPUT mode */
	mul	r4, r8, r7
	add	r5, r3, r4                 @ r5 points at the start of the right GPIO group address
	add	r6, r5, #GPIO_GPxEN_OFFSET @ r6 points to the direction control
	mov	r7, #1
	lsl	r7, r9                     @ r7 contains the pattern for the right GPIO to be activated
	str	r7, [r6]                   @ set OUTPUT MODE
	/* set pull-down */
	add	r6, r5, #GPIO_GPxPD0_OFFSET
	ldr	r3, .GPIO_PULL_DOWN
	str	r3, [r6]
	add	r6, r5, #GPIO_GPxPD1_OFFSET
	ldr	r3, .GPIO_PULL_DOWN
	str	r3, [r6]
	/* and set function mode to 0 */
	add	r6, r5, #GPIO_GPxFN0_OFFSET
	mov	r3, #0
	str	r3, [r6]
	add	r6, r5, #GPIO_GPxFN1_OFFSET
	mov	r3, #0
	str	r3, [r6]
	add	r6, r5, #GPIO_GPxFN2_OFFSET
	mov	r3, #0
	str	r3, [r6]
	add	r6, r5, #GPIO_GPxFN3_OFFSET
	mov	r3, #0
	str	r3, [r6]

	/* from now on, we need to loop over the encoding */
	orr	r4, r9, r8, LSL #5         @ r4 contains the number of cycles to toggle the GPIO
	mov	r3, #0                     @ this will be our counter
	add	r6, r5, #GPIO_GPxXOR_OFFSET @ r6 points at the GPxXOR register
	str	r7, [r6]                   @ we toggle the corresponding register
	ldr	r0, .DELAY_VALUE
	bl _delay
	str	r7, [r6]                   @ twice so to have a square wave
	bl _delay
	cmp	r3, r4
	add	r3, r3, #1
	bne	_loop_over_encoding

	/* now set to zero the GPIO */
	add	r6, r5, #GPIO_GPxCLR_OFFSET
	str	r7, [r6]

	/* return at home */
	mov	lr, r11
	mov pc, lr
	.word	0xf
	.word	0xf0102000
	.word	0xaaaaaaaa
.set GPIO_GROUPS_N, 0x06
.set GPIO_IDX_N,    0x20

	mov	r10, lr

	mov	r8, #0
	mov	r9, #0
	bl	gpio_signal
	add	r9, r9, #1
	cmp	r9, #GPIO_IDX_N
	bne	_loop_idx
	add	r8, r8, #1
	cmp	r8, #GPIO_GROUPS_N
	bne _loop_group

	mov lr, r10
	mov pc, lr

/* takes r0 as number of loop to wait
 * internally uses r1 and r2
	mov	r2, lr
	mov	r1, #0x00
	cmp	r1, r0
	add	r1, r1, #1
	bne	_loop_delay
	mov	lr, r2
	mov	pc, lr

I also wrote a program to help me debug the code I was using (I would call it emulator but it simply allocates the memory where to execute the firmware at the same address of the real device and then jumps into it, this is inspired from the work of Travis Goodspeed with the MD380). Obviously you need qemu to emulate the correct ARM architecture in a desktop.

Pico%20SP-H03/tcc-emu.c view raw
 * Loads and executes code wannabe running on a TCC8900 chips allocating
 * memory to emulate peripherals' memory.
 * You can use this with Qemu.
 *  $ export QEMU_LD_PREFIX=/usr/arm-linux-gnueabi
 *  $ qemu-arm -cpu arm1176 -g 4444 tcc-emu tccvirus.bin
 * and from another terminal
 *  $ gdb-multiarch tccvirus
 * Magically all the symbols will be visible from GDB!
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#define GPIO_MEMORY_ADDR 0xf0100000
#define GPIO_SIZE 0x400000

#define FIRMWARE_MEMORY_ADDR 0x00001000

void usage(const char progname[]) {
    fprintf(stderr, "usage: %s <firmware.bin>\n", progname);

int main(int argc, char* argv[]) {

    if (argc < 2) {

    char* filepath = argv[1];

    struct stat file_info;
    int retVal = stat(filepath, &file_info);

    if (retVal == -1) {
        perror("I could not stat file");
        goto fatal;


    if (gpio == MAP_FAILED) {
        perror("allocate gpio failed");
        goto fatal;

    int fw_fd = open(filepath, O_RDONLY);
    if (fw_fd == -1) {
        perror("I could not open file");
        goto fatal;

    void* (*firmware)();

    firmware = mmap((void*)FIRMWARE_MEMORY_ADDR, file_info.st_size, PROT_READ | PROT_EXEC , MAP_FIXED | MAP_PRIVATE, fw_fd, 0);

    if (gpio == MAP_FAILED) {
        perror("allocate firmware failed");
        goto fatal;

    fprintf(stderr, "-- Executing firmware at address 0x%08x--\n", FIRMWARE_MEMORY_ADDR);

    return 0;

After a couple of bug fixed I was able to look for the GPIOs: I used my oscilloscope to touch each pin exposed on the PCB and finally I find out the pinout of the UART interface: it’s located in the connector CN1101 with pin 6, 7, 8 as indicated in picture

Connecting to it with a baud of 115200 I was greeted with the following bootlog

LED Driver init.
TSADC init.
MAX8903A: DC Input.
MAX8903:DC = 1
␛[1;34m DC Input Mode! ␛[0m 
NAND: V7018
[NAND        ] [PORT CONFIG - CS[0, 1] [NAND Data Port: GPIO_B Hw4 ~ Hw11]
[NAND        ] [BClk 156MHZ][1Tick 65][RE-S:0,P:7,H:4][WR-S:0,P:5,H:4][COM-S:2,P:15,H:15]
[NAND        ] [NB Area:4MB][DT Area:931MB][HD Area0:9MB][HD Area1:9MB]

    tcboot ver 0.5 for TCC8900 Linux
    Board ver TCC8900_WINCE_LINUX_DEMO
    DATE:Jul 12 2010, TIME:13:11:27
CHIP ID: 0x0000000E0DD3C3B0
CLOCK: PLL0:540, PLL1:560, PLL2:468, PLL3:528, CPU:506, BUS:280  
       DDI:264, GRP:0, IOB:156, VBUS:0, VPU:0, SMU:117
LCD: Post Parallel Device Initialize..
DLP260X_Done Wait count = 1 msec 
emu_done reset = 0x1, ret = 0x1 
MAX8903A DC input =1 
DPP260X Initialize Success!
DLP : 854X480 RGB888 Mode
DPP260X Current Red=0x32A,Green=0x32A,Blue=0x32A
FWDN: checking...
length = 0x67C000, CRC = 0xFC025306
Load Ok! Jump to 0x40300000 (0xEA000016)

Linux version 2.6.28 (phking@linux-hth0) (gcc version 4.3.2 (Sourcery G++ Lite 2008q3-72) ) #3 Mon Jul 12 13:28:17 KST 2010
CPU: ARMv6-compatible processor [410fb766] revision 6 (ARMv7), cr=00c5387f
CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine: Samsung Oberon Board
Warning: bad configuration page, trying to continue
Memory policy: ECC disabled, Data cache writeback
Serial Number: 0000000e0dd3c3b0
Built 1 zonelists in Zone order, mobility grouping on.  Total pages20
Kernel command line: console=ttySAC0 lpj=2523136 initcall_debug=0   mem=80M
PID hash table entries: 512 (order: 9, 2048 bytes)
 ### CORE CLOCK (506250000 Hz), BUS CLOCK (280000000 Hz) ###
Console: colour dummy device 80x30
console [ttySAC0] enabled
Dentry cache hash table entries: 16384 (ord4, 65536 bytes)
Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
_etext:0xc0762000, _text:0xc04f5000, _end:0xc07a5db8, __data_start:0xc0762000, __init_end:0xc04f5000, __init_begin:0xc0100000
Memory: 80MB = 80MB total
Memory: 74240KB available (2484K code, 271K data, 4052K init)
Calibrating delay loop (skipped) preset value.. 504.62 BogoMIPS (lpj=2523136)
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
net_namespace: 288 bytes
NET: Registered protocol family 16
attached TCC adc driver
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 4096 (order: 3, 32768 bytes)
TCP bind hash table entries: 4096 (order: 2, 16384 bytes)
TCP: Hash tables configured (established 4096 bind 4096)
TCP reno registered
NET: Registered protocol family 1
Telechips Dynamic Power Management.
NetWinder Floating Point Emulator V0.97 (double precision)
fuse init (API version 7.10)
msgmni has been set to 145
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler noop registered
io scheduler cfq registered (default)
i2c /dev entries driver
tcc-i2c tcc-i2c: i2c-0: I2C adapter
fb[0]::map_video_memory: dma=45200000 cpu=c6000000 size=00800000
fb0: tccfb frame buffer device
fb[1]::map_video_memory: dma=45a00000 cpu=c7000000 size=00400000
fb1: tccfb frame buffer device
fb[2]::map_video_memory: dma=45e00000 cpu=c7800000 size=00400000
fb2: tccfb frame buffer device
tcc proc filesystem initialised
oberon_p5v_control: onoff = 1 
Obenron MAX8903A Driver
Battery Voltage = 0.4 
MAX8903A: DC Connected!
Obenron TW8816 Driver
tcc_intr: init (ver 2.1)
tcc_pwm: init (ver 0.1)
Obenron DPP260x Driver
DPP260X Check ID Success!.. 
Obenron TCC_WDT Driver
Obenron OBERON_LEDS Driver
TimerX init(TX-Timer0).
tcc8900-uart.0: tcc-uart0 at MMIO 0xf0532000 (irq = tcc8900-uart.1: tcc-uart1 at MMIO 0xf0532100 (irq = loop: module loaded
TRACE: DPM is now installed
Driver 'sd' needs updating - please use bus_type methods
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
dwc_otg: version 2.60a 22-NOV-2006
DWC_otg: Internal DMA Mode...
DWC_otg: Dedicated Tx FIFOs mode
DWC_otg: Using DMA mode
dwc_otg dwc_otg.0: DWC OTG Controller
dwc_otg dwc_otg.0: new USB bus registered, assigned bus number 1
dwc_otg dwc_otg.0: irq 48, io mem 0x00000000
DWC_otg: Init: Port Power? op_state=1
DWC_otg: Init: Power Port (0)
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
mice: PS/2 mouse device common for all mice
Oberon GPIO Keypad Driver...
input: Oberon keypad as /class/input/input0
TCC RTC, (c) 2009, Telechips 
tcc-rtc tcc-rtc: rtc core: registered tcc-rtc as rtc0
tcc-sdhc1: init
Set ID to device mode
Advanced Linux Sound Architecture Driver Version 1.0.18rc3.
ASoC version 0.13.2
TCC89XX  cs42l52 ALSA SoC driver ver1.1 10/2008
asoc: CS42L52 <-> tcc-i2s mapping ok
CS42L52: Cirrus CS42L52 codec , revision 3
soc_cs42l52_write: Can't set Limiter 
ID change ISR : Device
DWC_otg: Internal DMA Mode...
soc_cs42l52_setup: <Mono Speaker out>.
CS42L52: Cirrus Logic ALSA SoC codec cs42l52 driver verison 1.1 10/2008
ALSA device list:
  #0: TCC89XX (CS42L52)
TCP cubic registered
NET: Registered protocol family 17
VFP support v0.3: implementor 41 architecture 1 part 20 variant b rev 5
tcc-rtc tcc-rtc: hctosys: invalid date/time
DWC_otg: Internal DMA Mode...
hp_detect_work:  SPK Sound 
oberon_volume = 0, highlow=1 
init started:  BusyBox v1.4.2 (2010-05-20 22:17:07 KST) multi-call binary
Starting pid 352, console /dev/console: '/etc/init.d/rcS'
run /etc/init.d/rcS 
mdev init: ...
TCC8900_nand: module license 'Proprietary. (C) 2008 Telechips Inc.' taints kernel.
Dumy-Ready Interrupt-!!!!!!!!!!!!!!!!
[NAND        ] [PORT CONFIG - CS[0, 1] [NAND Data Port: GPIO_B Hw4 ~ Hw11]
[NAND        ] [BClk 156MHZ][1Tick 65][RE-S:0,P:7,H:4][WR-S:0,P:6,H:4][COM-S:2,P:15,H:15]
[NAND        ] [NB Area:4MB][DT Area:931MB][HD Area0:9MB][HD Area1:9MB]
 ndda: ndda1 ndda2 ndda3
[tcc_nand] init ndd(TCC8900, V7018)
[tcc-gadget]fsg_init start
[tcc-gadget]fsg_bind start
fsg_bind: gadget->name = dwc_otg_pcd 
usb_gadget_controller_number: gadget->name = dwc_otg_pcd 
g_file_storage gadget: File-backed Storage Gadget, version: 7 August 2007
g_file_storage gadget: Number of LUNs=1
[tcc-gadget]fsg init end
pwd = /
OBERON Update Script(pre)....
[Update File Check] Update Files Not Exist...
dosfsck_check pwd=/
[/usr/sbin/dosfsck] /dev/ndda1 .. ok
dosfsck_check pwd=/
[/usr/sbin/dosfsck] /dev/ndda2 .. ok
dosfsck_check pwd=/
[/usr/sbin/dosfsck] /dev/ndda3 .. ok
Starting pid 427, console /dev/ttySAC0: '/bin/sh'
/ #

and finally the shell. To my surprise the problem was that the user partition was empty, so simply untarring the update file into the root of the filesystem fixed the problem my friend had. Fortunately it is possible to insert a SD card with the update in it into the device and see it mounted in /media otherwise I would had to upload it using the serial with a my script (but it is very slow).

This wraps up this post: at end I’m very happy of what I obtained and there is space for further work on this device:

If you interested I have a repository with this and a couple of other devices that I analyzed during the years.