Nel 2022 avevo aperto questo Telsey CPVL7: console seriale, dump della flash, mappa delle immagini. Mi ero fermato lì, perché OpenWrt non supporta questo modello e le immagini che avevo trovato erano per un altro chip. Da allora il router è rimasto spento.
Questa volta ho ripreso il lavoro con un obiettivo preciso: farne un access point e uno switch con VLAN.
Da dove sono ripartito
OpenWrt 25.12 ha un subtarget ufficiale per il BCM6368, bmips/bcm6368, quindi manca solo la descrizione della scheda. Con 16 MB di flash e 64 di RAM non si rientra tra i dispositivi 4/32, quelli per cui si consiglia di fermarsi alle versioni più vecchie. Il mio vero timore era ritrovarmi con un router lento e inutilizzabile, e potevo scoprirlo solo provando.
Oltre alla console seriale già saldata servivano un cavo Ethernet diretto tra computer e router, e Docker per compilare, visto che OpenWrt su macOS non si compila facilmente. Il cavo diretto è importante, perché il bootloader usa 192.168.1.1 e il firmware originale accende un server DHCP: su una rete di casa darebbero fastidio entrambi.
Il bootloader fa quasi tutto il lavoro
Dal prompt CFE> un comando rende possibile tutto il resto:
CFE> r 192.168.1.100:o.elf
r scarica un ELF via TFTP e lo esegue in RAM, senza scrivere nulla. In pratica si può avviare un sistema completo, provarlo e spegnere il router, ritrovando la flash esattamente com’era.
Il CFE ha anche i comandi per scrivere sulla flash, a cominciare da f, ma non li ho usati perché non avevo modo di sapere con certezza dove scrivessero. Ho preferito far scrivere il sistema operativo, che si può limitare alle zone volute. Partendo dalla mappa del 2022, ho diviso così la flash:
| Offset | Contenuto | Con OpenWrt |
|---|---|---|
0x000000–0x020000 | CFE e NVRAM | Sola lettura |
0x020000–0xcc0000 | Immagine 1, una zona vuota e dati residui | Partizione firmware |
0xcc0000–0xfe0000 | Immagine 2 | Sola lettura |
0xfe0000–0x1000000 | Ultimo blocco | Sola lettura |
OpenWrt occupa solo la zona centrale. Se qualcosa va storto si riscrive quella, mentre il bootloader non viene mai toccato.
La prima build e la prima prova
La descrizione della scheda sta in un device tree, che dichiara flash, switch e Wi-Fi. La parte che mi interessava di più è quella delle partizioni, perché il kernel rifiuta di scrivere su quelle marcate read-only:
partition@0 {
label = "CFE";
reg = <0x0000000 0x0020000>;
read-only;
};
partition@20000 {
compatible = "brcm,bcm963xx-imagetag";
label = "firmware";
reg = <0x0020000 0x0ca0000>;
};
partition@cc0000 {
label = "image2";
reg = <0x0cc0000 0x0320000>;
read-only;
};
partition@fe0000 {
label = "nvram";
reg = <0x0fe0000 0x0020000>;
read-only;
};
L’ultima partizione si chiama nvram, ma è solo un’etichetta: la NVRAM vera, con il board ID, sta nel blocco del CFE, mentre qui c’è l’ultimo blocco della flash, quello che nel 2022 sembrava contenere la configurazione. Per la primissima prova, però, ho messo in sola lettura anche firmware.
Il dispositivo si definisce in poche righe in coda a target/linux/bmips/image/bcm6368.mk:
define Device/telsey_cpvl7
$(Device/bcm63xx-cfe)
KERNEL := kernel-bin | append-dtb | relocate-kernel | lzma -d22
DEVICE_VENDOR := Telsey
DEVICE_MODEL := CPVL7
CHIP_ID := 6368
CFE_BOARD_ID := CPVL7
BLOCKSIZE := 0x20000
DEVICE_PACKAGES += $(RT28_PACKAGES) kmod-leds-gpio
endef
TARGET_DEVICES += telsey_cpvl7
lzma -d22 porta il dizionario a 4 MB, lo stesso valore del kernel Fastweb. Il CFE ha un decompressore tutto suo e ho preferito non rischiare con gli 8 MB predefiniti. Il resto è la compilazione standard di OpenWrt 25.12, con kernel 6.12, dentro un container Debian 12: circa un’ora la prima volta e 12 GB di disco.
L’avvio in RAM è riuscito al primo tentativo, con lo switch riconosciuto e il Wi-Fi funzionante con il driver rt2800pci. Spostando il cavo da una porta all’altra ho verificato che le etichette ETH1-ETH4 corrispondono a lan1-lan4.
La cosa che mi premeva di più era la velocità. Con un MacBook sul Wi-Fi e un computer collegato via cavo, ho misurato attraverso il router 71,7 Mbit/s in download e 48,1 in upload, con la CPU mai sotto il 56% di idle. Il client era vicino e il canale da 20 MHz, quindi è solo un ordine di grandezza, ma basta a escludere il router lento e inutilizzabile che temevo.
Poi ho provato le VLAN, che erano il motivo di tutto il lavoro. Con il vlan_filtering attivo sul bridge ho messo il Wi-Fi nella VLAN 10, taggata su lan1. Il traffico è arrivato taggato al computer collegato, a 67,3 Mbit/s, mentre il ping verso la rete fuori dalla VLAN non passava, come doveva essere.
Con OpenWrt in RAM si può anche rileggere la flash, e bastano pochi secondi contro le due ore del CFE:
# sul router, con nc in ascolto sul computer
cat /dev/mtd0 /dev/mtd1 /dev/mtd2 /dev/mtd3 | nc 192.168.1.100 PORTA
Ho riletto tutto due volte, e le due copie erano identiche byte per byte al dump del 2022. Il backup era buono, e potevo passare alle scritture.
Il limite della RAM
La seconda build, con LuCI e il layout definitivo, non è filata altrettanto liscia. L’ELF è stato caricato, ma il kernel è andato in panic con write error mentre estraeva il filesystem. Dei 64 MB di RAM del router, il kernel ne vedeva disponibili 33,6 MiB, e il filesystem temporaneo usato per l’avvio in RAM poteva occuparne al massimo la metà, circa 16,8 MiB. Il cpio con LuCI arrivava a 16,9 MiB e superava quel limite di poco. Il problema riguarda solo l’avvio da RAM, perché su flash il sistema non viene scompattato. La soluzione è compilare due immagini, una senza LuCI da avviare in RAM per fare l’installazione e una con LuCI da scrivere sulla flash.
La scrittura sulla flash
Fin qui non avevo scritto un solo byte. Prima di cancellare l’immagine Fastweb ho voluto provare mtd su un settore della zona vuota, a 0x740000, che nella partizione diventa 0x720000 perché questa comincia a 0x20000:
mtd -p 0x720000 write FILE firmware
È cambiato solo quel settore, e dopo averlo riportato a 0xff la flash è tornata identica al backup. Ho quindi avviato in RAM l’immagine senza LuCI e scritto quella con LuCI direttamente dalla rete, perché in RAM non c’è spazio per tenerla:
wget -q -O - http://192.168.1.100:8080/openwrt.bin | mtd write - firmware
head -c DIMENSIONE /dev/mtd1 | sha256sum
L’hash coincideva, ma restava la vera incognita, perché nessuna prova in RAM poteva dirmi se il CFE avrebbe accettato il kernel di OpenWrt dalla flash. Al riavvio è comparsa questa riga, e il sistema è partito:
Booting from previous image (0xb8020000) ...
Ho riletto subito le zone protette: bootloader con la NVRAM, immagine 2 e ultimo blocco erano identici al backup.
Il sistema installato sta anche meglio di quello in RAM, perché non deve tenere in memoria l’intero filesystem. Con l’access point acceso restano disponibili 18 MB di RAM invece di 8, e per le impostazioni ci sono 5,4 MB di spazio.
Per farne solo uno switch con access point è bastato poco. L’interfaccia lan prende l’indirizzo via DHCP dalla rete in cui si trova, e dnsmasq, odhcpd e firewall sono spenti. Sulla radio c’è stato un altro inciampo: il default è band=5g con htmode=HT40, quindi impostando solo il canale 6 hostapd si ferma con Could not determine operating frequency. Vanno impostati anche band=2g e htmode=HT20.
LED e pulsanti
All’inizio LED e pulsanti non c’erano nel device tree, e ho aggiunto entrambi in un secondo momento. Per i LED ho pilotato i GPIO uno alla volta via sysfs, annotando quale si accendeva. Per i pulsanti ho registrato un log mentre li premevo. Sono tutti attivi bassi:
| Etichetta | GPIO | Colore |
|---|---|---|
| POWER | 15 / 14 | Verde / rosso |
| ETHERNET | 2 | Verde |
| WI-FI | 4 | Verde |
| Voce 1 | 26 / 0 | Verde / rosso |
| Voce 2 | 27 / 1 | Verde / rosso |
| Linea | 32 | Verde |
| Fastweb | 3 / 24 | Verde / rosso |
| USB | 5 | Verde |
| Pulsante reset | 36 | |
| Pulsante Wi-Fi | 37 |
I GPIO da 16 a 20 sono riservati al PCI e non li ho toccati. Dopo prove di questo tipo conviene riavviare, perché l’esportazione via sysfs può togliere ai pin la loro funzione hardware.
La nuova immagine si può installare con sysupgrade, direttamente dalla rete locale e senza perdere le impostazioni. L’unico dettaglio è che le impostazioni conservate non contengono le nuove associazioni dei LED, quindi ETHERNET e WI-FI vanno legati a mano a br-lan e phy0-ap0 con uci, usando il trigger netdev.
Il pulsante Wi-Fi spegne e riaccende la radio, ma l’ho modificato perché agisca solo tenendolo premuto per almeno 3 secondi, così non si spegne per sbaglio. La modifica sta in /etc/rc.button/rfkill, da aggiungere a /etc/sysupgrade.conf per non perderla agli aggiornamenti. Il reset ha il comportamento standard di OpenWrt: una pressione breve riavvia, 5 secondi o più cancellano le impostazioni.
La porta USB
Il BCM6368 ha due controller USB, EHCI per l’USB 2.0 e OHCI per l’1.1, che condividono lo stesso PHY, usbh. Sono già descritti in bcm6368.dtsi con status = "disabled", quindi basta attivarli dal device tree della scheda:
&ehci {
status = "okay";
};
&ohci {
status = "okay";
};
&usbh {
status = "okay";
};
Il resto sono pacchetti da aggiungere al .config:
| Pacchetto | A cosa serve |
|---|---|
kmod-usb2, kmod-usb-ohci | Driver dei due controller |
kmod-usb-storage | Chiavette e dischi |
kmod-fs-ext4, kmod-fs-vfat, kmod-fs-exfat | Filesystem |
block-mount | Montaggio dei dischi, tramite /etc/config/fstab |
ksmbd-server, luci-app-ksmbd | Condivisione in rete via SMB, con il server nel kernel e la pagina in LuCI |
kmod-usb-ledtrig-usbport | Trigger per il LED USB |
Il LED si associa alle porte in base-files/etc/board.d/01_leds. Ogni porta fisica va elencata due volte, perché compare sia sul bus EHCI sia su quello OHCI:
ucidef_set_led_usbport "usb" "USB" "green:usb" "usb1-port1" "usb2-port1" "usb1-port2" "usb2-port2"
Questo script non aggiorna le impostazioni conservate da un sysupgrade: in quel caso il LED va aggiunto a mano con uci.
Per tenere l’USB facoltativo ho messo i blocchi del device tree in un file a parte, usb.dtsi, che aggiungo alla scheda insieme ai pacchetti solo quando compilo un’immagine con l’USB.
L’immagine cresce di 768 kB, tolti allo spazio per le impostazioni, e i file dei moduli aggiunti sommano circa 1,7 MB. Il device tree attiva solo i controller: su alcune schede l’alimentazione della porta è comandata da un GPIO, quindi se un dispositivo collegato non viene rilevato è il primo punto da controllare.
Come tornare al firmware originale
Ci si ferma al CFE, si avvia in RAM l’immagine senza LuCI, si riscrivono dal backup i byte da 0x20000 a 0xcc0000 con mtd write, e si rilegge tutto per confronto. Il bootloader resta intatto, quindi si può partire da lì anche se OpenWrt non si avvia più.
Se in futuro servirà anche il routing, resta da descrivere nel device tree eth5, la porta da cui il firmware Fastweb faceva il NAT verso l’esterno, come avevo visto nel 2022.