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.cReszta 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.binCałą 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:
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.