Portowanie OpenWrt na EB-214A
Ostatnia zmiana: 2013-11-26 18:43

Mały blog poświęcony portowaniu OpenWrt na płytkę EB-214A. Chociaż słowo "portowanie" to trochę za dużo powiedziane. Sprzęt oparty jest o SoC Infineon ADM5120, jest to znana i wspierana platforma, więc trzeba tylko znaleźć sposób na uruchomienie OpenWrt na tej płycie i metodę dla flashowanie.
Oczywiście nie oznacza to że ta praca została wykonana dzień po dniu i zajęło to dokładnie XXX dni. Raczej chodzi poszczególne etapy podejścia do rozwiązania problemu.

Sama płytka jest interesująca z punktu widzenia "zabawy". Procesor, flash, ram, port szeregowy, ethernet, cztery porty USB 2.0, do tego przycisk i parę LED. Całkiem niezłe wyposażenie i wiele można z tego zrobić - dołączyć wifi na usb i zrobić ruter, podłączyć drukarkę i mieć printserwer czy w końcu jej pierwotne przeznaczenie - czyli zwykły nas. Fakt, wydajna nie jest i nie będzie, ale nie o to w tej całej zabawie chodzi.

Urządzenie występuje pod wieloma nazwami: Clarinetsys EB-214A, CompUSA 333060, Inno-Tide NU100, Multico EB-214A czy Intellinet Network Storage Adapter. Wszystkie oparte są właśnie o płytkę EB-214A z procesorem ADM5120

Dzień 1 - port szeregowy

Kilka dni po opublikowaniu minirecenzji zastanowił mnie brak możliwości wysyłania czegokolwiek do urządzenia. I ile brak takiej funkcjonalności w firmware jeszcze mogę zrozumieć (zabezpieczenie przed grzebaniem po systemie itp), o tyle blokowanie bootloadera jest już dziwne. Do tego stopnia że pokusiłem się z lupą przy oku o sprawdzenie przebiegu ścieżek - czy nie idą do przełącznika czy miejsca gdzie trzeba wlutować jakiś rezystor. Niestety, połączenia dochodziły bezpośrednio do układów; przez terminal szeregowy można było czytać dane ale nic wysyłać.

W akcie desperacji zamiast pomiaru napięć na pinach pokusiłem się o pomiar rezystancji połączeń. I nagle przyszło olśnienie: pomyliłem kolejność pinów! Zamiast zaprezentowanego wcześniej oznaczenia: pin1 - vcc,tx,rx,gnd powinno być pin 1 - vcc,tx,gnd,rx. Przy włączonym urządzeniu napięcie na ostatnim pinie równa się zero co zinterpretowałem jako gnd (na logikę patrząc tak jak jest w innych urządzeniach). Szybkie podłączenie kabla szeregowego, uruchomienie i jest! Mogę normalnie pisać w terminalu.

Na pierwszy ogień poszedł więc oryginalny system. Niestety, sama analiza zawartości nie przyniosła za wiele - prawie same binarki, system prawdopodobnie uruchamia się w ram. Brak jest podziału na obszary flash. Niedobrze. Ale i tak oryginalnych źródeł nie ma, a na dodatek opierał się o starą wersję kernela, więc nic straconego. Poniżej bootlog i parę informacji o systemie.


Boot Loader released on 10/12/2005
Test Memory: OK
Decompressing boot loader... ok
Board IP:	192.168.9.254
Server IP:	192.168.9.99
PCI devices:
devfn	vid	did	name
00:00.0	1317	5120	ADM5120 PCI
00:04.0	1106	3038	Via UHCI Host Controller
00:04.1	1106	3038	Via UHCI Host Controller
00:04.2	1106	3104	Via EHCI Host Controller
Flash( MX29LV160BB/c249) at bfc00000 with size 00200000
Decompressing Linux .....................................................
Ok, booting the kernel.

Starting Kernel
CPU revision is: 0001800b
Primary instruction cache 8kB, physically tagged, 2-way, linesize 16 bytes.
Primary data cache 8kB 2-way, linesize 16 bytes.
Linux version 2.4.20
Can't analyze prologue code at 8001c194
System has PCI BIOS
Determined physical RAM map:
    memory: 00e40000 @ 001c0000 (usable)
On node 0 totalpages: 4096
zone(0): 4096 pages.
zone(1): 0 pages.
zone(2): 0 pages.
Kernel command line: root=/dev/cblkfs console=ttyS0
CPU clock: 175MHz
Calibrating delay loop... 174.48 BogoMIPS
MIPS CPU counter frequency is fixed at 87500000 Hz
Memory: 14408k/14592k available (1418k kernel code, 184k reserved, 96k data, 56k init, 0k highmem)
Dentry cache hash table entries: 2048 (order: 2, 16384 bytes)
Inode cache hash table entries: 1024 (order: 1, 8192 bytes)
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
Page-cache hash table entries: 4096 (order: 2, 16384 bytes)
Checking for 'wait' instruction...  available.
POSIX conformance testing by UNIFIX
Autoconfig PCI channel 0x8018fee8
Scanning bus 00, I/O 0x11500000:0x115ffff0, Mem 0x11400000:0x11500000
00:00.0 Class 0600: 1317:5120
    Mem unavailable -- skipping
    I/O unavailable -- skipping
00:04.0 Class 0c03: 1106:3038 (rev 61)
    I/O at 0x11500000 [size=0x20]
00:04.1 Class 0c03: 1106:3038 (rev 61)
    I/O at 0x11500020 [size=0x20]
00:04.2 Class 0c03: 1106:3104 (rev 63)
    Mem at 0x11400000 [size=0x100]
ADM5120 PCI FIXUP
PCI_BIOS_FIXUP
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Starting kswapd
Disabling the Out Of Memory Killer
Journalled Block Device driver loaded
fuse init (API version 7.5)
fuse distribution version: 2.5.3
pty: 256 Unix98 ptys configured
flash: QRY ok
15 02 00 00 00 04 00 00 40 00 01 00 20 00 00 00 80 00 1e 00 00 01 
flash: manufacturer id = c2, device id = 2249, device size = 00200000
sysconf: loading configuration data from flash
cblock: Welcome to cblock v1.00
cblock: 49 blocks, 65536 bytes/block, largest block is 55965 bytes.
SCSI subsystem driver Revision: 1.00
usb.c: registered new driver hub
ehci_hcd 00:04.2: PCI device 1106:3104
ehci_hcd 00:04.2: irq 6, pci mem b1400000
usb.c: new USB bus registered, assigned bus number 1
PCI: 00:04.2 PCI cache line size set incorrectly (0 bytes) by BIOS/FW, correcting to 32
ehci_hcd 00:04.2: USB 2.0 enabled, EHCI 1.00, driver 2003-Dec-29/2.4
hub.c: USB hub found
hub.c: 4 ports detected
host/usb-uhci.c: $Revision: 1.275 $ time 17:16:26 Aug  8 2006
host/usb-uhci.c: High bandwidth mode enabled
host/usb-uhci.c: USB UHCI at I/O 0x11500000, IRQ 6
host/usb-uhci.c: Detected 2 ports
usb.c: new USB bus registered, assigned bus number 2
hub.c: USB hub found
hub.c: 2 ports detected
host/usb-uhci.c: USB UHCI at I/O 0x11500020, IRQ 6
host/usb-uhci.c: Detected 2 ports
usb.c: new USB bus registered, assigned bus number 3
hub.c: USB hub found
hub.c: 2 ports detected
host/usb-uhci.c: v1.275:USB Universal Host Controller Interface driver
usb.c: registered new driver usblp
printer.c: v0.13: USB Printer Device Class driver
Initializing USB Mass Storage driver...
usb.c: registered new driver usb-storage
USB Mass Storage support registered.
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP, IGMP
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 1024 bind 2048)
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
FAT: bogus logical sector size 0
VFS: Mounted root (iso9660 filesystem) readonly.
Freeing prom memory: 0kb freed
Freeing unused kernel memory: 56k freed
Bummer, can't write to log on /dev/tty0!
console=/dev/ttyS0
Embedded Linux File System
init started:  BusyBox v0.60.5 (2006.08.07-05:34+0000) multi-call binary
Starting Network
IP Address: 192.168.1.1, Netmask: 255.255.255.0
Starting Samba
Starting FTP Server
Starting Web Management Server
webmgrd listen address = 192.168.1.1, port = 80
Starting DHCP Client
dhcp client (v0.9.7) started
Starting Device Finder Daemon


BusyBox v0.60.5 (2006.08.07-05:34+0000) Built-in shell (lash)
Enter 'help' for a list of built-in commands.

/ # ls /proc
1            49           cpuinfo      ioports      net          sysvipc
2            5            devices      irq          partitions   tty
3            51           dma          kcore        pci          uptime
30           53           driver       kmsg         scsi         version
33           6            execdomains  loadavg      self
36           7            filesystems  locks        slabinfo
4            8            fs           meminfo      stat
42           bus          interrupts   misc         swaps
44           cmdline      iomem        mounts       sys
/ # cat /proc/cpuinfo 
system type		: Embedded System
processor		: 0
cpu model		: MIPS 4Kc V0.11
BogoMIPS		: 174.48
wait instruction	: yes
microsecond timers	: yes
tlb_entries		: 16
extra interrupt vector	: yes
hardware watchpoint	: yes
VCED exceptions		: not available
VCEI exceptions		: not available
/ # cat /proc/interrupts 
                                            CPU0       
        1:        186    ADM5120 INTC  serial
        6:          0    ADM5120 INTC  ehci_hcd, usb-uhci, usb-uhci
        9:         45    ADM5120 INTC  eth0

ERR:          0
/ # cat /proc/pci 
PCI devices found:
        Bus  0, device   0, function  0:
                Class 0600: PCI device 1317:5120 (rev 0).
                        Master Capable.  Latency=128.  
                        Non-prefetchable 32 bit memory at 0xf0000000 [0xffffffff].
                        I/O at 0xff000000 [0xfeffffff].
        Bus  0, device   4, function  0:
                Class 0c03: PCI device 1106:3038 (rev 97).
                        IRQ 6.
                        Master Capable.  Latency=128.  
                        I/O at 0x11500000 [0x1150001f].
        Bus  0, device   4, function  1:
                Class 0c03: PCI device 1106:3038 (rev 97).
                        IRQ 6.
                        Master Capable.  Latency=128.  
                        I/O at 0x11500020 [0x1150003f].
        Bus  0, device   4, function  2:
                Class 0c03: PCI device 1106:3104 (rev 99).
                        IRQ 6.
                        Master Capable.  Latency=128.  
                        Non-prefetchable 32 bit memory at 0x11400000 [0x114000ff].

/ # ps
        PID TTY     Uid        Size State Command
                1         admin       916   S   init 
                2         admin         0   S   [keventd]
                3         admin         0   S   [ksoftirqd_CPU0]
                4         admin         0   S   [kswapd]
                5         admin         0   S   [bdflush]
                6         admin         0   S   [kupdated]
                7         admin         0   S   [flashd]
                8         admin         0   S   [khubd]
            30         admin      1640   S   nmbd -D 
            33         admin      1824   S   smbd -D 
            36         admin      1040   S   ftpd 
            42         admin      1320   S   webmgrd 
            44         admin      1024   S   dhcpcd 
            49         admin      1368   S   devlisten 
            51 ttyS0   admin       932   S   /bin/sh 
            58 ttyS0   admin       912   R   ps 
/ # lsmod

/ # ls /dev
console  hdc4     ptypb    sda5     sdb6     sdc7     sdd8     ttypb
core     kmem     ptypc    sda6     sdb7     sdc8     sdd9     ttypc
full     mem      ptypd    sda7     sdb8     sdc9     tty      ttypd
fuse     null     ptype    sda8     sdb9     sdd      tty0     ttype
hda      port     ptypf    sda9     sdc      sdd1     ttyS0    ttypf
hda1     ppp      random   sdb      sdc1     sdd10    ttyS1    urandom
hda2     ptyp0    sda      sdb1     sdc10    sdd11    ttyp0    usblp0
hda3     ptyp1    sda1     sdb10    sdc11    sdd12    ttyp1    usblp1
hda4     ptyp2    sda10    sdb11    sdc12    sdd13    ttyp2    usblp2
hda5     ptyp3    sda11    sdb12    sdc13    sdd14    ttyp3    usblp3
hda6     ptyp4    sda12    sdb13    sdc14    sdd15    ttyp4    video
hda7     ptyp5    sda13    sdb14    sdc15    sdd2     ttyp5    video0
hda8     ptyp6    sda14    sdb15    sdc2     sdd3     ttyp6    video1
hdc      ptyp7    sda15    sdb2     sdc3     sdd4     ttyp7    video2
hdc1     ptyp8    sda2     sdb3     sdc4     sdd5     ttyp8    video3
hdc2     ptyp9    sda3     sdb4     sdc5     sdd6     ttyp9    zero
hdc3     ptypa    sda4     sdb5     sdc6     sdd7     ttypa
/ # 


Dzień 2 - bootloader

Skoro system nie przedstawiał się za ciekawie to zobaczymy co potrafi bootloader. Poszukiwania w internecie nie przyniosły nic ciekawego, być może zrobiony jest specjalnie dla tego urządzenia. Po zwarciu JP3 i podłączeniu zasilania system zgłosił się jako USBSHARE> i czekał na polecenia. Pierwsze co mi przyszło do głowy - polecenie help. Nie działa. Znak zapytania - nie działa. Kilka różnych standardowych poleceń które powinny działać - jednak nie działają. Wciskając niektóre klawisze coś jednak wyświetlał (np. klawisz d). Postanowiłem więc sprawdzić wszystkie klawisze po kolei. W połowie sprawdzania, przy klawiszu h pojawił się... help. Więc byłem blisko za pierwszym razem. Okazało się że polecenia są jedno- lub klikuliterowe.


    USBSHARE> h
    g	execute system program
    fg	execute system program from flash image
    d	dump memory content
    t	tftp client download
    tb	tftp client download for boot loader, "boot.bin"
    tz	tftp client download for compressed image, "kfs.bin"
    ts	tftp client download for compressed image, "vmlinux.bin"
    copy	copy command
    wboot	update boot loader
    wsys	update system image
    set	set/display environment
    rtc	get/set real time clock
    test	test all|rtc|sdram|gpio
    pci	dump pci configuration register
    h	help message
    USBSHARE> 

Mamy więc możliwość sprawdzenia systemu, ustawienia czasu, zładowania przez tftp firmware a także zapisania pamięci. Na razie nie martwiłem się o format, ważne że jest taka możliwość. Jest także opcja test, która za wiele nie robi:


    USBSHARE> test
    Usage: test all|rtc|sdram
    USBSHARE> test all
    Test SDRAM: Done
    Test RTC: OK
    USBSHARE> 

Ale samo OK cieszy.

Dzień 3 - uruchamianie OpenWrt

Trzeba sprawdzić czy w ogóle OpenWrt na nim działa. Sam chip ADM5120 jest wspierany (chociaż by popularny swego czasu Edimax na tym chodzi) więc nie powinno być większych problemów.

Przy starcie systemu zostały wypisane dwie wartości: Board IP i Server IP (192.168.9.99). Założyłem, że ta druga to domyślny adres serwera skonfigurowany w bootloaderze, więc na taki tez adres ustawiłem na sieciówce podłączonej kablem do płytki.

Bootloader miał kilka opcji załadowania systemu. Dwie z nich to załadowanie vmlinuz.bin (to jest sam kernel, co mnie zbytnio nie interesowało), natomiast druga to załadowanie pliku kfs.bin, co wyglądało obiecująco.

Ściągnięcie źródeł OpenWrt to chwila. Polecenie make menuconfig i jestem w trakcie konfigurowania. Ponieważ nie chciałem już na początku zabawy stracić systemu, zamiast flashowania postanowiłem uruchamiać system w pamięci ram. Wybrałem więc jako target Infineon/ADMtek ADM5120, jako target profile Edimax br61x4wg (jako że to urządzenie ma USB standardowo) oraz target images: ramdisk. Odznaczyłem parę pakietów żeby obraz był jak najmniejszy - wyleciało więc wifi, wpad, dnsmasq, ppp, iptables. Został za to dropbear. Po skompilowaniu obraz miał 1,7MB.
W skompilowanych źródłach pojawił się plik openwrt-adm5120-br-6104wg-ramfs.gz. Rozpakowałem go (gzip -d openwrt...) i przegrałem pod nazwą kfs.bin do katalogu głównego serwera tftpd (używam akurat atftpd).
Powrót do bootloadera. polecenie tz, enter. Załadował plik, wypisał długość.


    USBSHARE> tz
    TFTP download for "kfs.bin" .................................... ok
    total length = 0021ffa0
    USBSHARE>

Fajnie, tylko jak to teraz uruchomić... Szybkie spojrzenie na help ("h") i w oko wpadła opcja "g". Więc wciskam g, dwie sekundy niepewności i system uruchamia się!


    LZMA loader for ADM5120, Copyright (C) 2007-2008 OpenWrt.org

    decompressing kernel... done!
    launching kernel...

Po kilku sekundach uruchamiania system zgłasza się ze znakiem zachęty.

Nie działają diody led, działa za to interfejs sieciowy, mogłem przez telnet zalogować do systemu (chociaż i tak mam kabel szeregowy...). Szybkie rozejrzenie po systemie potwierdza specyfikację sprzętową: 2MB flash (więc trzeba będzie budować obraz z extroot), 16MB ram. Sam procesor taktowany jest zegarem 175MHz (rakieta z tego nie będzie - fonera ma 180MHz a i tak się strasznie ślimaczyła).

Podłączyłem pendrive USB - cisza, nie działa. Szybkie przejrzenie komunikatów startowych ujawniło problem z przydzieleniem przerwań do kontrolera USB, więc gdzieś jest problem.


    ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
    ehci_hcd 0000:00:04.2: Found HC with no IRQ.  Check BIOS/PCI 0000:00:04.2 setup!
    ehci_hcd 0000:00:04.2: init 0000:00:04.2 fail, -19
    uhci_hcd: USB Universal Host Controller Interface driver
    uhci_hcd 0000:00:04.0: Found HC with no IRQ.  Check BIOS/PCI 0000:00:04.0 setup!
    uhci_hcd 0000:00:04.0: init 0000:00:04.0 fail, -19
    uhci_hcd 0000:00:04.1: Found HC with no IRQ.  Check BIOS/PCI 0000:00:04.1 setup!
    uhci_hcd 0000:00:04.1: init 0000:00:04.1 fail, -19

Cóż, użyłem obrazu od innego systemu, więc ma prawo nie działać. Trzeba będzie skompilować po swojemu, co mnie wcale nie dziwiło.

Dzień 4 - USB

Przerwania do kontrolera USB... Dziwne ale nigdy temat nie był mi potrzebny, więc zbytnio nie interesowałem się "co, jak i gdzie". Szybkie spojrzenie do kodu dla innych platform ujawnia, że trzeba napisać nową mapę dla przerwań których też nie znam. Oryginał wyglądał tak (plik target/linux/adm5120/files/arch/mips/adm5120/edimax/br-61x4wg.c):


    static struct adm5120_pci_irq br61x4wg_pci_irqs[] __initdata = {
        PCIIRQ(2, 0, 1, ADM5120_IRQ_PCI0),
    };

Interesowało mnie więc makro PCIIRQ, pierwsza wartość to slot, druga to function, trzecia pin, czwarta to przerwanie skojarzone z tym pinem. Trzeba było więc te informacje zdobyć.

Podczas startu wyświetlają się informacje o trzech urządzeniach PCI:


    00:04.0	1106	3038	Via UHCI Host Controller
    00:04.1	1106	3038	Via UHCI Host Controller
    00:04.2	1106	3104	Via EHCI Host Controller

Kontroler to ten sam stosowany w Asusie WL-500g Premium. Miałem więc już informacje o kontrolerach, wiem jaki jest slot (4), wiem jaka jest funkcja (0,1,2). Zostaje tylko więc ustalenie pinu który trzeba przekierować do przerwania. Do obrazu testowego dokompliowałem polecenie lspci (pakiet pciutils), uruchomiłem ponownie system z moim obrazem. Po wydaniu polecenia lspci -vv dostałem coś takiego:


    root@OpenWrt:/# lspci -vv
    00:00.0 Host bridge: ADMtek ADM5120 OpenGate System-on-Chip
        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=slow >TAbort- <TAbort- <MAbort+ >SERR- <PERR- INTx-
        Latency: 0, Cache Line Size: 16 bytes
        Region 0: [virtual] Memory at f0000000 (32-bit, non-prefetchable) [size=256M]
        Region 1: I/O ports at <unassigned>

    00:04.0 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 61) (prog-if 00 [UHCI])
        Subsystem: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller
        Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 0
        Region 4: I/O ports at 11500000 [size=32]
        Capabilities: [80] Power Management version 2
            Flags: PMEClk+ DSI- D1+ D2+ AuxCurrent=0mA PME(D0+,D1+,D2+,D3hot+,D3cold-)
            Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-

    00:04.1 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller (rev 61) (prog-if 00 [UHCI])
        Subsystem: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 Controller
        Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Interrupt: pin B routed to IRQ 0
        Region 4: I/O ports at 11500020 [size=32]
        Capabilities: [80] Power Management version 2
            Flags: PMEClk+ DSI- D1+ D2+ AuxCurrent=0mA PME(D0+,D1+,D2+,D3hot+,D3cold-)
            Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-

    00:04.2 USB Controller: VIA Technologies, Inc. USB 2.0 (rev 63) (prog-if 20 [EHCI])
        Subsystem: VIA Technologies, Inc. USB 2.0
        Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Interrupt: pin C routed to IRQ 0
        Region 0: Memory at 11400000 (32-bit, non-prefetchable) [size=256]
        Capabilities: [80] Power Management version 2
            Flags: PMEClk+ DSI- D1+ D2+ AuxCurrent=0mA PME(D0+,D1+,D2+,D3hot+,D3cold-)
            Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-

Jak łatwo wyczytać - pin A ma być skierowany do przerwania do urządzenia 00:04.0, pin B ma być skierowany do przerwania do urządzenia 00:04.1, a pin C ma być skierowany do przerwania do urządzenia 00:04.2.

Mają te informacje zmodyfikowałem tablicę mapowania przerwań:


    static struct adm5120_pci_irq br61x4wg_pci_irqs[] __initdata = {
        PCIIRQ(4, 0, 1, ADM5120_IRQ_PCI0),
        PCIIRQ(4, 1, 2, ADM5120_IRQ_PCI1),
        PCIIRQ(4, 2, 3, ADM5120_IRQ_PCI2),
    };

Skompilowałem, uruchomiłem, i... nie działa. Na starcie wypisał


    pci 0000:00:04.0: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.0: PME# disabled
    pci 0000:00:04.1: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.1: PME# disabled
    pci 0000:00:04.2: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.2: PME# disabled
    PCI: slot number 4 is not supported
    PCI: slot number 4 is not supported
    PCI: slot number 4 is not supported

No jak slot 4 może być nieobsługiwany, jak ja mam właśnie urządzenia PCI na tym slocie...

Szybkie przeszukanie źródeł zdradziło powód: w pliku target/linux/adm5120/files/arch/mips/pci/pci-adm5120.c jest taki kod:


    if (slot < 1 || slot > 3) {
        printk(KERN_ALERT "PCI: slot number %u is not supported\n",
            slot);
        goto out;
    }

Ktoś złożył, że slotów PCI dla ADM5120 nie może być więcej niż trzy. Cóż, pewnie wiedzą lepiej, ale ja właśnie mam takie urządzenie!

Poprawka funkcji na if (slot < 1 || slot > 4), ponowna kompilacja kodu, uruchomienie i...


    pci 0000:00:00.0: reg 10 32bit mmio: [0xf0000000-0xffffffff]
    pci 0000:00:04.0: reg 20 io port: [0x11500000-0x1150001f]
    pci 0000:00:04.0: supports D1 D2
    pci 0000:00:04.0: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.0: PME# disabled
    pci 0000:00:04.1: reg 20 io port: [0x11500020-0x1150003f]
    pci 0000:00:04.1: supports D1 D2
    pci 0000:00:04.1: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.1: PME# disabled
    pci 0000:00:04.2: reg 10 32bit mmio: [0x11400000-0x114000ff]
    pci 0000:00:04.2: supports D1 D2
    pci 0000:00:04.2: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.2: PME# disabled
    PCI: mapping irq for 0000:00:04.0 pin:1, irq:14
    PCI: mapping irq for 0000:00:04.1 pin:2, irq:15
    PCI: mapping irq for 0000:00:04.2 pin:3, irq:16

DZIAŁA! Mam mapowanie przerwań do kontrolera USB. Pełen zapału podłączyłem pendrive i kernel pięknie wyłożył się z oopsem. Why Leo, why?

Uruchomiłem ponownie oryginał. Okazało się, że na jednym przerwaniu (6 konkretnie) działają wszystkie trzy urządzenia 04.00, 04.01 oraz 04.02. Więc może popełniłem błąd robiąc mapę przerwań? Bardzo dużo niewiadomych, ale trzeba było to sprawdzić. Zmieniłem kod tym razem na taki:


    static struct adm5120_pci_irq br61x4wg_pci_irqs[] __initdata = {
        PCIIRQ(4, 0, 1, ADM5120_IRQ_PCI0),
        PCIIRQ(4, 1, 2, ADM5120_IRQ_PCI0),
        PCIIRQ(4, 2, 3, ADM5120_IRQ_PCI0),
    };

Czyli w slocie 4 są trzy urządzenia i wszystkie mają działać na tym samym przerwaniu. Ponowna kompilacja, uruchomienie i ... działa.


    pci 0000:00:00.0: reg 10 32bit mmio: [0xf0000000-0xffffffff]
    pci 0000:00:04.0: reg 20 io port: [0x11500000-0x1150001f]
    pci 0000:00:04.0: supports D1 D2
    pci 0000:00:04.0: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.0: PME# disabled
    pci 0000:00:04.1: reg 20 io port: [0x11500020-0x1150003f]
    pci 0000:00:04.1: supports D1 D2
    pci 0000:00:04.1: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.1: PME# disabled
    pci 0000:00:04.2: reg 10 32bit mmio: [0x11400000-0x114000ff]
    pci 0000:00:04.2: supports D1 D2
    pci 0000:00:04.2: PME# supported from D0 D1 D2 D3hot
    pci 0000:00:04.2: PME# disabled
    PCI: mapping irq for 0000:00:04.0 pin:1, irq:14
    PCI: mapping irq for 0000:00:04.1 pin:2, irq:14
    PCI: mapping irq for 0000:00:04.2 pin:3, irq:14

Działa, znaczy się uruchamia kernel. Podłączyłem pendrive - nie wywalił się, zamontował! Mam więc już działające usb. Pierwsze testy odczytu przyniosły pozytywne rezultaty.

Na koniec postanowiłem jeszcze sprawdzić jak szybko to USB działa. Na dysku z systemem plików ext2 był plik o rozmiarze ok 700MB. Zainstalowałem vsftpd, konfiguracja to dostęp anonimowy do nośnika. Oto wynik pobrania pliku z dysku podłączonego pod USB:


    $ wget -O /dev/null ftp://192.168.1.1/sdb2/a.bin
    --2010-03-25 19:30:32--  ftp://192.168.1.1/sdb2/a.bin
                                            => `/dev/null'
    Łączenie się z 192.168.1.1:21... połączono.
    Logowanie się jako anonymous ... Zalogowano się!
    ==> SYST ... zrobiono.    ==> PWD ... zrobiono.
    ==> TYPE I ... zrobiono.  ==> CWD (1) /sdb2 ... zrobiono.
    ==> SIZE a.bin ... 735613718
    ==> PASV ... zrobiono.    ==> RETR a.bin ... zrobiono.
    Długość: 735613718 (702M) (nie autorytatywne)
    
    100%[=====================================>] 735.613.718 1,96M/s   w  6m 10s  =
    
    2010-03-25 19:36:42 (1,90 MB/s) - zapisano `/dev/null' [735613718]

czyli prawie 2MB/s - czyli szybciej niż na oryginale. Mówiłem, rakieta to nie będzie...

Dzień 5 - przycisk

Płytka posiada jeden przycisk - reset. Najczęściej obsługa przycisków realizowana jest przez przestawienie jednego z portów gpio w tryb wejścia, więc jego obsługa to pewnie tylko znalezienia odpowiedniego numeru portu.

Ponieważ nadal korzystałem z plików od Edimax'a, skorzystałem z pliku target/linux/adm5120/files/arch/mips/adm5120/edimax/br-61xx.c. W nim znajduje się definicja przypisania przycisków, wystarczyło więc zdefiniować kilka różnych i sprawdzić czy działa. Zmiany wyglądały tak:


    static struct gpio_button br61xx_gpio_buttons[] __initdata = {
    {
        .desc		= "reset_button0",
        .type		= EV_KEY,
        .code		= BTN_0,
        .threshold	= 5,
        .gpio		= ADM5120_GPIO_PIN0,
    },
    {
        .desc		= "reset_button1",
        .type		= EV_KEY,
        .code		= BTN_1,
        .threshold	= 5,
        .gpio		= ADM5120_GPIO_PIN1,
    },

itd. Zdefiniowałem 8 wpisów o różnych oznaczeniach. Kolejna kompilacja obrazu, uruchomienie. Teraz tylko było trzeba sprawdzić która linia będzie reagowała na naciskanie. Zrobiłem prosty skrypt w /etc/hotplug.d/button/przyciski o takiej zawartości:


    #!/bin/sh
    logger $BUTTON
    logger $ACTION

Więc jeżeli coś nacisnę to w logach powinna pojawić się informacja co to było. Naciskałem więc przycisk, później polecenie logread i dostałem coś takiego:


    Jan  1 00:01:15 OpenWrt user.notice root: BTN_1
    Jan  1 00:01:15 OpenWrt user.notice root: pressed
    Jan  1 00:01:16 OpenWrt user.notice root: BTN_1
    Jan  1 00:01:16 OpenWrt user.notice root: released

Wykryty został przycisk który jest podłączony do GPIO1. Wystarczyło usunąć niepotrzebny kod i zostawić jeden wpis:


    .desc		= "reset",
    .type		= EV_KEY,
    .code		= BTN_0,
    .threshold	= 5,
    .gpio		= ADM5120_GPIO_PIN1,

Płytka wzbogaciła się o obsługę przycisku...

Dzień 6 - wykrywanie platformy, mac adres

Dotychczas wszystko było modyfikowane w plikach które należały do platformy Edimax. Tak było łatwiej, ale niestety wszystko co dobre w końcu się kończy i należało to zrobić tak jak powinno być, tzn. w osobnych plikach.

Pierwsza sprawa to wykrycie odpowiedniej platformy. Rzut oka do plików źródłowych ujawniło, że nazwa platformy odczytywana jest z ustawienia zmiennej środowiskowej board_name z pamięci flash. Aby było trudniej okazało się, że w ADM5120 może być kilka bootloaderów: admboot, cfe, myloader, routerboot, bootbase, u-boot lub generic. Wiedziałem już, że EB-214A nie posiada żadnego z nich - przy starcie mojego obrazu wypisywał:


    SoC      : ADM5120 rev 8, running at 175.000 MHz
    Bootdev  : NOR flash
    Prom     : Generic


Całość procesu wykrywania platformy zawarta jest w pliku target/linux/adm5120/files/arch/mips/adm5120/common/prom.c. Fragment - funkcja odpowiedzialna za wykrycie w bootloaderze generic wygląda następująco:


    static unsigned long __init detect_machtype_generic(void)
    {
            char *name;

            name = generic_prom_getenv("board_name");
            return find_machtype_byname(name);
    }

Nie wiedziałem co jest zwracane dla mojej płytki, więc wstawiłem przed return wyświetlenie tego co zwraca funkcja generic_prom_getenv


    static unsigned long __init detect_machtype_generic(void)
    {
            char *name;

            name = generic_prom_getenv("board_name");
            printk(KERN_WARNING "EB-214A identyfikowana jest jako %s\n",
                name);
            return find_machtype_byname(name);
    }

Następna kompilacja, uruchomienie i okazało się, ze zwracany jest w komunikatach startowych napis "ADM5120". Trochę zbyt to ogólne, ale założyłem właśnie tą wartość.

Prawidłowa detekcja tej płytki sprowadzała się do dodania wpisu:


    /* Generic EB-214A */
    DEFBOARD("ADM5120",MACH_ADM5120_EB_214A),

w strukturze common_boards[] w pliku target/linux/adm5120/files/arch/mips/adm5120/common/prom.c

Reszta to było tylko dodanie MACH_ADM5120_EB_214A do odpowiednich miejsc:
- target/linux/adm5120/files/arch/mips/include/asm/mach-adm5120/adm5120_info.h

Dodanie MACH_ADM5120_EB_214A jako następnego numeru

- target/linux/adm5120/router_le/config-2.6.32

Dodanie CONFIG_ADM5120_MACH_EB_214A=y

- target/linux/adm5120/files/arch/mips/adm5120/Kconfig

Dodanie


    config ADM5120_MACH_EB_214A
        bool "EB-214A support"
        depends on CPU_LITTLE_ENDIAN
        select ADM5120_SOC_BGA
        select ADM5120_OEM_EDIMAX
        default y

do możliwości wyboru EB-214A przy make kernel_menuconfig

- target/linux/adm5120/files/arch/mips/adm5120/edimax/Makefile

Dodanie obj-$(CONFIG_ADM5120_MACH_EB_214A) += eb-214a.o aby umożliwić kompilację mojego pliku opisującego platformę.

- target/linux/adm5120/image/router_le.mk

Dodanie


    define Image/Build/Template/Edimax/Initramfs
        $(call Image/Build/LZMAKernel/Admboot,eb-214a,bin)
    endef

dzięki czemu plik wynikowy firmware budował się z nazwą mojej platformy, czyli np. openwrt-adm5120-eb-214a-ramfs.bin

Całą definicję platformy zrobiłem natomiast w pliku target/linux/adm5120/files/arch/mips/adm5120/edimax/eb-214a.c - tam skopiowałem dane z plików od edimax'a oraz zmieniłem dane wg tego co odkryłem.

Ponowna kompilacja, uruchomienie i na starcie dostałem:


    MIPS: machine is EB-214A


Podczas testów wyszła jeszcze jedna sprawa - źle był ustawiany mac adres interfejsu sieciowego. Wiedziałem, że większość ruterów trzyma mac adres po prostu w pamięci flash - albo w obszarze nvram jak np. linksys albo gdzieś w okolicach bootloadera - jak ovislink czy tplink.

Zgrałem sobie na pendrive (miałem już działające usb :)) cały flash poleceniami


    # dd if=/dev/mtd0 of=/mnt/sda1/boot.bin
    # dd if=/dev/mtd1 of=/mnt/sda1/config.bin
    # dd if=/dev/mtd2 of=/mnt/sda1/firmware.bin

a następnie uruchomiłem oryginalny firmware i przy pomocy komendy ifconfig odczytałem oryginalny mac adres (później okazało się, że można go też odczytać w bootloaderze poleceniem set). Była to wartość 00:53:2D:xx:xx:xx. Mając zgrany obraz flash poszukałem edytorem hex występowania tej wartości - i nie pomyliłem się. Poszukiwany mac został znaleziony pod adresem 0x4000!

Rozbudowałem więc kod swojej platformy o następującą funkcję:


    static void __init eb214a_mac_setup(void)
    {
        u8 mac_base[6];
        u8 *cfg;
        int i;

        cfg = (u8 *) KSEG1ADDR(ADM5120_SRAM0_BASE + EB214A_CONFIG_OFFSET);
        for (i = 0; i < 6; i++)
            mac_base[i] = cfg[i];

        if (!is_valid_ether_addr(mac_base))
            random_ether_addr(mac_base);

        adm5120_setup_eth_macs(mac_base);
    }

gdzie EB214A_CONFIG_OFFSET to właśnie 0x4000. Następnie wprowadziłem wywołanie tej funkcji w odpowiednim miejscu w pliku target/linux/adm5120/files/arch/mips/adm5120/edimax/eb-214a.c. Coraz bliżej zakończenia.

Dzień 7 - ledy

Poprzednim razem całkiem zapomniałem o obsłudze diod LED. W sumie na płytce jest ich 6:
- jedna pomarańczowa oznaczona Power
- jedna zielona oznaczona LAN
- cztery zielone oznaczone odpowiednio USB1, USB2, USB3 i USB4

Podobnie jak przyciski, diody obsługiwane są zwyczajowo przez linie GPIO. Poszukiwania linii które sterują diodami można przeprowadzić na różne sposoby. Ja wybrałem jeden z prostszych: po prostu zdefiniowałem wszystkie możliwe, a później wpisując odpowiednie polecenia sprawdziłem która się zapali.

Aby zdefiniować diody należało wypełnić strukturę gpio_led analogicznie jak to było w przypadku przycisków. W moim przypadku napisałem więc coś takiego:


    static struct gpio_led eb214a_gpio_leds[] __initdata = {
        GPIO_LED_STD(ADM5120_GPIO_PIN0, "pin0", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN2, "pin2", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN3, "pin3", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN4, "pin4", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN5, "pin5", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN6, "pin6", NULL),
        GPIO_LED_STD(ADM5120_GPIO_PIN7, "pin7", NULL),	
        GPIO_LED_STD(ADM5120_GPIO_P0L0, "pin8", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P0L1, "pin9", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P0L2, "pin10", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P1L0, "pin11", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P1L1, "pin12", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P2L2, "pin13", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P2L0, "pin14", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P2L1, "pin15", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P3L2, "pin16", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P3L0, "pin17", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P3L1, "pin18", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P4L2, "pin19", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P4L0, "pin20", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P4L2, "pin21", NULL),
        GPIO_LED_STD(ADM5120_GPIO_P4L2, "pin22", NULL),
    }

Pin 1 nie ma, bo tam jest przycisk obsługiwany. Tyle jest gpio zdefiniowanych dla ADM5120; nazwy typu ADM5120_GPIO_P2L1 itp to definicje linii określone w target/linux/adm5120/files/arch/mips/include/asm/mach-adm5120/gpio.h.

Dodałem podpięcie tej struktury do inicjacji platformy, ponowna kompilacja firmware (już nawet nie liczyłem która) i uruchomienie. Podczas startu wypisały się odpowiednie komunikaty:


    Registered led device: pin0
    Registered led device: pin2
    Registered led device: pin3
    Registered led device: pin4
    Registered led device: pin5
    Registered led device: pin6
    Registered led device: pin7

ale również


    Registered led device: pin20
    leds-gpio: probe of leds-gpio failed with error -16

Oznaczało to że linia 21 jest już do czegoś używana. Należało więc usunąć definicję dla GPIO 21 oraz 22, skompilować i ponownie uruchomić. Teraz już było wszystko było w porządku oraz pojawiły się odpowiednie katalogi w /sys/class/leds/. Musiałem tylko sprawdzić która się zapali jak wydam polecenie


    # echo "0" > /sys/class/leds/pin0/brightness 
    # echo "255" > /sys/class/leds/pin0/brightness 
    # echo "0" > /sys/class/leds/pin1/brightness 
    # echo "255" > /sys/class/leds/pin1/brightness 
    itd.

Zapaliły się:
- pin7: power
- pin8: lan
- pin17: usb4
- pin18: usb1
Ale reagowały na "0" (zapalały się) i gasły po wpisaniu "255". A to oznaczało że sterowane są "odwrotnie" i powinny być wołane makrem GPIO_LED_INV. Po wpisaniu danych i kompilacji okazało się że nie działają w ogóle! Nadal brakowało obsługi jeszcze dwóch led.

Kilka godzin później zrozumiałem, że nie można tak bezkarnie testować wszystkich gpio bo mogę być one podłączone do innych komponentów systemu. Takie echo 0/255 może coś przestawić, co później z kolei może mieć wpływ na działanie całości.

Ostatecznie okazało się że USB3 jest jednak obsługiwane przez GPIO 22, a USB2 przez GPIO 21 (mimo wcześniejszego problemu z jego rejestracją). Po zdefiniowaniu tylko tych linii i odłączeniu zasilania całość zachowuje się tak jak powinna - czy poprawnie reagują wszystkie diody.

Obsługa diod LED zostały więc zdefiniowane jako:


    GPIO_LED_INV(ADM5120_GPIO_PIN7, "power", NULL),
    GPIO_LED_INV(ADM5120_GPIO_P0L0, "lan", NULL),
    GPIO_LED_INV(ADM5120_GPIO_P4L0, "usb1", NULL),
    GPIO_LED_INV(ADM5120_GPIO_P4L1, "usb2", NULL),
    GPIO_LED_INV(ADM5120_GPIO_P4L2, "usb3", NULL),
    GPIO_LED_INV(ADM5120_GPIO_P3L0, "usb4", NULL),


Dzień 8 - pakiety

Domyślny budowany profil zawiera kilka pakietów, które niekoniecznie są wymagane dla tej platformy. Zbędne są:
- wpad-mini (domyślnie nie ma tam wifi)
- admswconfig
- kmod-ledtrig-adm5120-switch (brak jest switcha)
- ppp/kmod-ppp (połączenia ppp raczej nie będziemy robić, można później zainstalować)
- iptables (jeden interfejs lan, więc można najwyżej później dograć)
- dnsmasq (za serwer dhcp raczej ta maszynka nie będzie robić)

Jest to o tyle istotne, że mamy tylko 2MB flash. Wymaga więc maksymalnego ograniczenia liczby pakietów; później można jest doinstalować jeżeli zrobimy extroot.

Powinny za to zostać wkompilowane dodatkowo:
- obsługa usb, usb-storage, system plików ext2/ext3 ew. dodatkowo vfat
- block-mount
- block-hotplug
- block-extroot

Bieżącą łatkę ze wszystkim co udało się odkryć można pobrać z repozytorium OpenWrt: https://dev.openwrt.org/ticket/7027

---
Wszystkie zmiany zostały włączone do bieżącej gałęzi openwrt pod ticketem r22275.

cdn.