diff --git a/.github/workflows/mf68e.yml b/.github/workflows/mf68e.yml new file mode 100644 index 0000000..ea6527b --- /dev/null +++ b/.github/workflows/mf68e.yml @@ -0,0 +1,97 @@ +name: OpenWrt Builder (MF68E) +on: + repository_dispatch: + workflow_dispatch: + +env: + UPLOAD_BIN_DIR: true + UPLOAD_FIRMWARE: true + UPLOAD_RELEASE: false + TZ: Europe/Madrid + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Initialization environment + env: + DEBIAN_FRONTEND: noninteractive + run: | + sudo rm -rf /etc/apt/sources.list.d/* /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + sudo -E apt-get -qq update + sudo -E ./devenv/dependencies.sh + sudo -E apt-get -qq autoremove --purge + sudo -E apt-get -qq clean + sudo timedatectl set-timezone "$TZ" + + - name: "Clone OpenWrt source code" + run: | + df -hT $PWD + git clone --depth=1 --branch "main" https://git.openwrt.org/openwrt/openwrt.git openwrt + + - name: Load msm89xx target and uz801 packages + run: | + git apply --unsafe-paths enable_wcn36xx_mac80211.patch + mv msm89xx openwrt/target/linux/msm89xx + mv packages openwrt/package/uz801 + if [ "${{ inputs.openwrt_version }}" == "openwrt-24.10" ]; then + sed -i '/^KERNEL_PATCHVER/c\KERNEL_PATCHVER:=6.6' openwrt/target/linux/msm89xx/Makefile + fi + + - name: Update and install feeds + run: | + cd openwrt + rm -rf tmp + ./scripts/feeds update -a + ./scripts/feeds install -a + + - name: "Load diffconfig" + run: | + [ -f mf68e_diffconfig ] && mv mf68e_diffconfig openwrt/.config + + - name: Download package + id: package + run: | + cd openwrt + make defconfig + make download -j8 + find dl -size -1024c -exec ls -l {} \; || true + find dl -size -1024c -exec rm -f {} \; || true + + - name: Compile the firmware + id: compile + run: | + cd openwrt + echo -e "$(nproc) thread compile" + make -j$(nproc) || make -j1 || make -j1 V=s + echo "status=success" >> $GITHUB_OUTPUT + grep '^CONFIG_TARGET.*DEVICE.*=y' .config | sed -r 's/.*DEVICE_(.*)=y/\1/' > DEVICE_NAME + [ -s DEVICE_NAME ] && echo "DEVICE_NAME=$(cat DEVICE_NAME)" >> $GITHUB_ENV + echo "FILE_DATE=$(date +"%Y%m%d%H%M")" >> $GITHUB_ENV + + - name: Organize files + id: organize + if: env.UPLOAD_FIRMWARE == 'true' && !cancelled() + run: | + cd openwrt/bin/targets/*/* + rm -rf packages + echo "FIRMWARE=$PWD" >> $GITHUB_ENV + echo "status=success" >> $GITHUB_OUTPUT + + - name: Upload firmware + uses: actions/upload-artifact@v4 + if: steps.organize.outputs.status == 'success' && !cancelled() + with: + name: openwrt_firmware_${{ env.DEVICE_NAME }}_${{ env.FILE_DATE }} + path: | + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3-squashfs-gpt_both0.bin + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3-squashfs-boot.img + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3-squashfs-system.img + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3.manifest + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3-flash.sh + ${{ env.FIRMWARE }}/openwrt-msm89xx-msm8916-yiming-uz801v3-firmware.zip diff --git a/.github/workflows/builder.yml b/.github/workflows/uz801.yml similarity index 99% rename from .github/workflows/builder.yml rename to .github/workflows/uz801.yml index 8bde158..9637c2b 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/uz801.yml @@ -1,4 +1,4 @@ -name: OpenWrt Builder +name: OpenWrt Builder (UZ801) on: repository_dispatch: workflow_dispatch: diff --git a/diffconfig b/diffconfig index d0f5cba..7d5ce74 100644 --- a/diffconfig +++ b/diffconfig @@ -22,7 +22,6 @@ CONFIG_PACKAGE_kmod-tun=y CONFIG_PACKAGE_kmod-udptunnel4=y CONFIG_PACKAGE_kmod-udptunnel6=y CONFIG_PACKAGE_kmod-wireguard=y -CONFIG_PACKAGE_ledcontrol=y CONFIG_PACKAGE_libattr=y CONFIG_PACKAGE_libdbus=y CONFIG_PACKAGE_libexpat=y diff --git a/mf86e_diffconfig b/mf86e_diffconfig new file mode 100644 index 0000000..3210102 --- /dev/null +++ b/mf86e_diffconfig @@ -0,0 +1,94 @@ +CONFIG_TARGET_msm89xx=y +CONFIG_TARGET_msm89xx_msm8916=y +CONFIG_TARGET_msm89xx_msm8916_DEVICE_generic-mf68e=y +CONFIG_BUSYBOX_CUSTOM=y +CONFIG_BUSYBOX_CONFIG_FBSET=y +CONFIG_BUSYBOX_CONFIG_USLEEP=y +CONFIG_LIBQMI_COLLECTION_BASIC=y +CONFIG_LIBQMI_WITH_MBIM_QMUX=y +CONFIG_LIBQMI_WITH_QRTR_GLIB=y +CONFIG_MODEMMANAGER_WITH_MBIM=y +CONFIG_MODEMMANAGER_WITH_NETIFD=y +CONFIG_MODEMMANAGER_WITH_QMI=y +CONFIG_MODEMMANAGER_WITH_QRTR=y +CONFIG_PACKAGE_bash=y +CONFIG_PACKAGE_cgi-io=y +CONFIG_PACKAGE_dbus=y +CONFIG_PACKAGE_glib2=y +CONFIG_PACKAGE_ip-tiny=y +CONFIG_PACKAGE_kmod-crypto-kpp=y +CONFIG_PACKAGE_kmod-crypto-lib-chacha20=y +CONFIG_PACKAGE_kmod-crypto-lib-chacha20poly1305=y +CONFIG_PACKAGE_kmod-crypto-lib-curve25519=y +CONFIG_PACKAGE_kmod-crypto-lib-poly1305=y +CONFIG_PACKAGE_kmod-tun=y +CONFIG_PACKAGE_kmod-udptunnel4=y +CONFIG_PACKAGE_kmod-udptunnel6=y +CONFIG_PACKAGE_kmod-wireguard=y +CONFIG_PACKAGE_libattr=y +CONFIG_PACKAGE_libdbus=y +CONFIG_PACKAGE_libexpat=y +CONFIG_PACKAGE_libfdisk=y +CONFIG_PACKAGE_libffi=y +CONFIG_PACKAGE_libiwinfo=y +CONFIG_PACKAGE_libiwinfo-data=y +CONFIG_PACKAGE_liblua=y +CONFIG_PACKAGE_liblucihttp=y +CONFIG_PACKAGE_liblucihttp-lua=y +CONFIG_PACKAGE_liblucihttp-ucode=y +CONFIG_PACKAGE_libmbim=y +CONFIG_PACKAGE_libmount=y +CONFIG_PACKAGE_libncurses=y +CONFIG_PACKAGE_libpcre2=y +CONFIG_PACKAGE_libqmi=y +CONFIG_PACKAGE_libqrtr-glib=y +CONFIG_PACKAGE_libreadline=y +CONFIG_PACKAGE_libubus-lua=y +CONFIG_PACKAGE_lsblk=y +CONFIG_PACKAGE_lua=y +CONFIG_PACKAGE_luci=y +CONFIG_PACKAGE_luci-app-firewall=y +CONFIG_PACKAGE_luci-app-package-manager=y +CONFIG_PACKAGE_luci-app-usb-gadget=y +CONFIG_PACKAGE_luci-base=y +CONFIG_PACKAGE_luci-lib-base=y +CONFIG_PACKAGE_luci-lib-ip=y +CONFIG_PACKAGE_luci-lib-json=y +CONFIG_PACKAGE_luci-lib-jsonc=y +CONFIG_PACKAGE_luci-lib-nixio=y +CONFIG_PACKAGE_luci-lib-uqr=y +CONFIG_PACKAGE_luci-light=y +CONFIG_PACKAGE_luci-lua-runtime=y +CONFIG_PACKAGE_luci-mod-admin-full=y +CONFIG_PACKAGE_luci-mod-network=y +CONFIG_PACKAGE_luci-mod-rpc=y +CONFIG_PACKAGE_luci-mod-status=y +CONFIG_PACKAGE_luci-mod-system=y +CONFIG_PACKAGE_luci-proto-ipv6=y +CONFIG_PACKAGE_luci-proto-modemmanager=y +CONFIG_PACKAGE_luci-proto-ppp=y +CONFIG_PACKAGE_luci-proto-wireguard=y +CONFIG_PACKAGE_luci-theme-bootstrap=y +CONFIG_PACKAGE_modemmanager=y +CONFIG_PACKAGE_mount-utils=y +CONFIG_PACKAGE_mtools=y +CONFIG_PACKAGE_nano=y +CONFIG_PACKAGE_resize2fs=y +CONFIG_PACKAGE_resolveip=y +CONFIG_PACKAGE_rpcd=y +CONFIG_PACKAGE_rpcd-mod-file=y +CONFIG_PACKAGE_rpcd-mod-iwinfo=y +CONFIG_PACKAGE_rpcd-mod-luci=y +CONFIG_PACKAGE_rpcd-mod-rrdns=y +CONFIG_PACKAGE_rpcd-mod-ucode=y +CONFIG_PACKAGE_sfdisk=y +CONFIG_PACKAGE_terminfo=y +CONFIG_PACKAGE_tune2fs=y +CONFIG_PACKAGE_ucode-mod-html=y +CONFIG_PACKAGE_ucode-mod-log=y +CONFIG_PACKAGE_ucode-mod-lua=y +CONFIG_PACKAGE_ucode-mod-math=y +CONFIG_PACKAGE_uhttpd=y +CONFIG_PACKAGE_uhttpd-mod-ubus=y +CONFIG_PACKAGE_wireguard-tools=y +# CONFIG_TARGET_ROOTFS_EXT4FS is not set diff --git a/msm89xx/config-6.12 b/msm89xx/config-6.12 index 0098fc8..bc6c544 100644 --- a/msm89xx/config-6.12 +++ b/msm89xx/config-6.12 @@ -1,4 +1,5 @@ + # CONFIG_ARM_MHU_V2 is not set # CONFIG_ARM_MHU_V3 is not set # CONFIG_ARM_QCOM_CPUFREQ_HW is not set @@ -22,6 +23,7 @@ # CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set # CONFIG_CMA_SYSFS is not set # CONFIG_COMPAT_ALIGNMENT_FIXUPS is not set +# CONFIG_CONSOLE_TRANSLATIONS is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ONDEMAND is not set @@ -35,9 +37,13 @@ # CONFIG_DEVFREQ_GOV_POWERSAVE is not set # CONFIG_DEVFREQ_GOV_USERSPACE is not set # CONFIG_DEVFREQ_THERMAL is not set +# CONFIG_DRM_MSM is not set +# CONFIG_DUMMY_CONSOLE is not set +# CONFIG_EC_ACER_ASPIRE1 is not set # CONFIG_EXPERT is not set # CONFIG_FIPS_SIGNATURE_SELFTEST is not set # CONFIG_FORTIFY_SOURCE is not set +# CONFIG_FRAMEBUFFER_CONSOLE is not set # CONFIG_FW_LOADER_USER_HELPER is not set # CONFIG_GCC_PLUGIN_LATENT_ENTROPY is not set # CONFIG_GCC_PLUGIN_STACKLEAK is not set @@ -69,6 +75,7 @@ # CONFIG_KPSS_XCC is not set # CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set # CONFIG_LEDS_TRIGGER_NETDEV is not set +# CONFIG_LOGO is not set # CONFIG_LWTUNNEL_BPF is not set # CONFIG_MAILBOX_TEST is not set # CONFIG_MFD_HI6421_SPMI is not set @@ -260,6 +267,9 @@ # CONFIG_VIRTIO_IOMMU is not set # CONFIG_VIRTIO_MENU is not set # CONFIG_VIRTIO_NET is not set +# CONFIG_VT is not set +# CONFIG_VT_CONSOLE is not set +# CONFIG_VT_HW_CONSOLE_BINDING is not set CONFIG_64BIT=y CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y @@ -323,7 +333,10 @@ CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y CONFIG_AUTOFS_FS=y CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GPIO=y CONFIG_BACKLIGHT_PWM=y +CONFIG_BATTERY_PM8916_BMS_VM=y +CONFIG_BATTERY_PM8916_BMS=y CONFIG_BLK_DEBUG_FS=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_SD=y @@ -342,6 +355,7 @@ CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y CONFIG_CACHESTAT_SYSCALL=y CONFIG_CC_HAVE_SHADOW_CALL_STACK=y CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CHARGER_PM8916_LBC=y CONFIG_CLONE_BACKWARDS=y CONFIG_CLZ_TAB=y CONFIG_CMA_ALIGNMENT=8 @@ -357,7 +371,6 @@ CONFIG_COMPAT_BINFMT_ELF=y CONFIG_COMPAT_OLD_SIGACTION=y CONFIG_COMPAT=y CONFIG_CONFIGFS_FS=y -CONFIG_CONSOLE_TRANSLATIONS=y CONFIG_CONTEXT_TRACKING_IDLE=y CONFIG_CONTEXT_TRACKING=y CONFIG_CONTIG_ALLOC=y @@ -468,7 +481,6 @@ CONFIG_DST_CACHE=y CONFIG_DT_IDLE_GENPD=y CONFIG_DT_IDLE_STATES=y CONFIG_DTC=y -CONFIG_DUMMY_CONSOLE=y CONFIG_EDAC_SUPPORT=y CONFIG_ELF_CORE=y CONFIG_EXCLUSIVE_SYSTEM_RAM=y @@ -481,8 +493,12 @@ CONFIG_EXTCON_USB_GPIO=y CONFIG_EXTCON=y CONFIG_FANOTIFY=y CONFIG_FAT_FS=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y CONFIG_FB_CORE=y CONFIG_FB=y +CONFIG_FB=y CONFIG_FHANDLE=y CONFIG_FIX_EARLYCON_MEM=y CONFIG_FIXED_PHY=y @@ -490,9 +506,6 @@ CONFIG_FONT_8x16=y CONFIG_FONT_8x8=y CONFIG_FONT_SUPPORT=y CONFIG_FRAME_POINTER=y -CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y -CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FS_IOMAP=y CONFIG_FS_MBCACHE=y CONFIG_FS_POSIX_ACL=y @@ -632,10 +645,6 @@ CONFIG_LOCALVERSION_AUTO=y CONFIG_LOCALVERSION="-msm8916" CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y -CONFIG_LOGO_LINUX_CLUT224=y -CONFIG_LOGO_LINUX_MONO=y -CONFIG_LOGO_LINUX_VGA16=y -CONFIG_LOGO=y CONFIG_LRU_GEN_WALKS_MMU=y CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" CONFIG_LWTUNNEL=y @@ -710,6 +719,7 @@ CONFIG_OF_DYNAMIC=y CONFIG_OF_EARLY_FLATTREE=y CONFIG_OF_FLATTREE=y CONFIG_OF_GPIO=y +CONFIG_OF_GRAPH=y CONFIG_OF_IOMMU=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y @@ -959,15 +969,13 @@ CONFIG_UTS_NS=y CONFIG_VDSO_GETRANDOM=y CONFIG_VFAT_FS=y CONFIG_VIDEO=y +CONFIG_VIDEOMODE_HELPERS=y CONFIG_VIRTIO_ANCHOR=y CONFIG_VIRTIO=y CONFIG_VLAN_8021Q_GVRP=y CONFIG_VLAN_8021Q_MVRP=y CONFIG_VM_EVENT_COUNTERS=y CONFIG_VMAP_STACK=y -CONFIG_VT_CONSOLE=y -CONFIG_VT_HW_CONSOLE_BINDING=y -CONFIG_VT=y CONFIG_WANT_DEV_COREDUMP=y CONFIG_WATCHDOG_CORE=y CONFIG_WATCHDOG_SYSFS=y diff --git a/msm89xx/image/msm8916.mk b/msm89xx/image/msm8916.mk index 0d0f205..4f2dd42 100644 --- a/msm89xx/image/msm8916.mk +++ b/msm89xx/image/msm8916.mk @@ -35,8 +35,20 @@ define Device/yiming-uz801v3 FILESYSTEMS := squashfs DEVICE_PACKAGES := configs-uz801 wpad-basic-wolfssl rmtfs uci-usb-gadget \ block-mount f2fs-tools prepare-rootfs-data \ - msm-firmware-dumper + msm-firmware-dumper ledcontrol endef TARGET_DEVICES += yiming-uz801v3 +define Device/generic-mf68e + $(Device/msm8916) + DEVICE_VENDOR := Generic + DEVICE_MODEL := MF68E + FILESYSTEMS := squashfs + CMDLINE := "earlycon console=ttyMSM0,115200 quiet loglevel=0 vt.global_cursor_default=0 vt.cur_default=0 root=/dev/mmcblk0p14 rootfstype=squashfs rootwait" + DEVICE_PACKAGES := configs-mf68e wpad-basic-wolfssl rmtfs uci-usb-gadget \ + block-mount f2fs-tools prepare-rootfs-data \ + msm-firmware-dumper kmod-fbtft-gc9107 router-display +endef +TARGET_DEVICES += generic-mf68e + endif diff --git a/msm89xx/patches-6.12/801-arm64-dts-qcom-add-mf68e-makefile.patch b/msm89xx/patches-6.12/801-arm64-dts-qcom-add-mf68e-makefile.patch new file mode 100644 index 0000000..febf68a --- /dev/null +++ b/msm89xx/patches-6.12/801-arm64-dts-qcom-add-mf68e-makefile.patch @@ -0,0 +1,21 @@ +From: hkfuertes +Date: Sat, 11 Oct 2025 11:49:00 +0200 +Subject: [PATCH] arm64: dts: qcom: Add MF68E to Makefile + +Add msm8916-generic-mf68e.dtb to Makefile. + +Signed-off-by: hkfuertes +--- + arch/arm64/boot/dts/qcom/Makefile | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/qcom/Makefile ++++ b/arch/arm64/boot/dts/qcom/Makefile +@@ -23,6 +23,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsu + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-gt58.dtb + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-j3ltetw.dtb + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-j5.dtb ++dtb-$(CONFIG_ARCH_QCOM) += msm8916-generic-mf68e.dtb + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-j5x.dtb + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-rossa.dtb + dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-serranove.dtb diff --git a/msm89xx/patches-6.12/802-arm64-dts-qcom-add-msm8916-generic-mf68e.patch b/msm89xx/patches-6.12/802-arm64-dts-qcom-add-msm8916-generic-mf68e.patch new file mode 100644 index 0000000..e9fe175 --- /dev/null +++ b/msm89xx/patches-6.12/802-arm64-dts-qcom-add-msm8916-generic-mf68e.patch @@ -0,0 +1,168 @@ +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/msm8916-generic-mf68e.dts +@@ -0,0 +1,165 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++/dts-v1/; ++ ++#include "msm8916-ufi.dtsi" ++#include ++#include ++#include ++ ++/ { ++ model = "MF68E 4G LTE Mobile WiFi Router"; ++ compatible = "generic,mf68e", "qcom,msm8916"; ++ ++ bat: battery { ++ compatible = "simple-battery"; ++ voltage-min-design-microvolt = <3400000>; ++ voltage-max-design-microvolt = <4350000>; ++ energy-full-design-microwatt-hours = <11100000>; ++ charge-full-design-microamp-hours = <3000000>; ++ ++ ocv-capacity-celsius = <25>; ++ ocv-capacity-table-0 = <4330000 100>, <4265000 95>, ++ <4208000 90>, <4154000 85>, <4102000 80>, ++ <4063000 75>, <4006000 70>, <3972000 65>, ++ <3931000 60>, <3880000 55>, <3841000 50>, ++ <3817000 45>, <3798000 40>, <3783000 35>, ++ <3767000 30>, <3747000 25>, <3729000 20>, ++ <3700000 16>, <3687000 13>, <3683000 11>, ++ <3681000 10>, <3680000 9>, <3678000 8>, ++ <3674000 7>, <3666000 6>, <3641000 5>, ++ <3597000 4>, <3537000 3>, <3457000 2>, ++ <3336000 1>, <3000000 0>; ++ }; ++ ++ usb_id: usb-id { ++ compatible = "linux,extcon-usb-gpio"; ++ id-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_id_default>; ++ }; ++ ++ backlight: backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pm8916_pwm 0 100000>; ++ brightness-levels = <255 128 64 32 16 8 4 0>; ++ num-interpolated-steps = <255>; ++ default-brightness-level = <6>; ++ status = "okay"; ++ }; ++}; ++ ++/* GC9107 Panel on SPI5 (0x78b9000) */ ++&blsp_spi5 { ++ status = "okay"; ++ ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&spi5_default>; ++ pinctrl-1 = <&spi5_sleep>; ++ ++ panel@0 { ++ compatible = "galaxycore,gc9107"; ++ reg = <0>; ++ spi-max-frequency = <16000000>; ++ ++ reset-gpios = <&tlmm 11 GPIO_ACTIVE_LOW>; ++ dc-gpios = <&tlmm 30 GPIO_ACTIVE_HIGH>; ++ backlight = <&backlight>; ++ ++ vdd-supply = <&pm8916_l17>; ++ vddio-supply = <&pm8916_l6>; ++ ++ width-mm = <23>; ++ height-mm = <23>; ++ ++ buswidth = <8>; ++ width = <128>; ++ height = <128>; ++ rotate = <0>; ++ bgr; ++ fps = <27>; ++ debug = <1>; ++ }; ++}; ++ ++&pm8916_l6 { ++ regulator-always-on; ++}; ++ ++&pm8916_l13 { ++ regulator-boot-on; ++ regulator-allow-set-load; ++}; ++ ++&pm8916_rpm_regulators { ++ pm8916_l17: l17 { ++ regulator-min-microvolt = <2850000>; ++ regulator-max-microvolt = <2850000>; ++ regulator-always-on; ++ }; ++}; ++ ++&pm8916_pwm { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_out>; ++}; ++ ++&pm8916_mpps { ++ pwm_out: mpp4-state { ++ pins = "mpp4"; ++ function = "digital"; ++ output-low; ++ qcom,dtest = <1>; ++ power-source = ; ++ }; ++}; ++ ++&pm8916_bms { ++ status = "okay"; ++ monitored-battery = <&bat>; ++ power-supplies = <&pm8916_charger>; ++}; ++ ++&pm8916_charger { ++ status = "okay"; ++ monitored-battery = <&bat>; ++ qcom,fast-charge-safe-current = <900000>; ++ qcom,fast-charge-safe-voltage = <4350000>; ++}; ++ ++&usb { ++ status = "okay"; ++ extcon = <&usb_id>, <&usb_id>; ++}; ++ ++&usb_hs_phy { ++ extcon = <&usb_id>; ++}; ++ ++&sdhc_1 { ++ status = "okay"; ++}; ++ ++&tlmm { ++ spi5_default: spi5-default-state { ++ pins = "gpio16", "gpio17", "gpio19"; ++ function = "blsp_spi5"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ ++ spi5_sleep: spi5-sleep-state { ++ pins = "gpio16", "gpio17", "gpio19"; ++ function = "gpio"; ++ drive-strength = <2>; ++ bias-pull-down; ++ }; ++ ++ usb_id_default: usb-id-default-state { ++ pins = "gpio110"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-pull-up; ++ }; ++}; diff --git a/msm89xx/patches-6.12/901-fbtft-fb_gc9107.patch b/msm89xx/patches-6.12/901-fbtft-fb_gc9107.patch new file mode 100644 index 0000000..3fe6c76 --- /dev/null +++ b/msm89xx/patches-6.12/901-fbtft-fb_gc9107.patch @@ -0,0 +1,218 @@ +--- /dev/null ++++ b/drivers/staging/fbtft/fb_gc9107.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * FB driver for the GC9107 LCD Controller ++ * ++ * Copyright (C) 2025 ++ * Based on: ++ * - Espressif ESP-IDF GC9107 driver (esp_lcd_gc9107.c) ++ * - Craig Bishop init sequence ++ * - Android MSM8916 MF68E device ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_gc9107" ++#define WIDTH 128 ++#define HEIGHT 128 ++#define BPP 16 ++ ++/* GC9107 Commands */ ++#define GC9107_SWRESET 0x01 ++#define GC9107_SLPOUT 0x11 ++#define GC9107_INVON 0x21 ++#define GC9107_INVOFF 0x20 ++#define GC9107_DISPOFF 0x28 ++#define GC9107_DISPON 0x29 ++#define GC9107_CASET 0x2A ++#define GC9107_RASET 0x2B ++#define GC9107_RAMWR 0x2C ++#define GC9107_MADCTL 0x36 ++#define GC9107_COLMOD 0x3A ++ ++/* ++ * Init sequence from Espressif ESP-IDF driver ++ * Includes proper gamma, power control, and timing ++ */ ++static int init_display(struct fbtft_par *par) ++{ ++ par->fbtftops.reset(par); ++ mdelay(120); ++ ++ /* Frame Rate Control */ ++ write_reg(par, 0xB0, 0xC0); ++ ++ /* Porch Control */ ++ write_reg(par, 0xB2, 0x2F); ++ write_reg(par, 0xB3, 0x03); ++ ++ /* Display Function Control */ ++ write_reg(par, 0xB6, 0x19); ++ write_reg(par, 0xB7, 0x01); ++ ++ /* Power Control Settings */ ++ write_reg(par, 0xAC, 0xCB); ++ write_reg(par, 0xAB, 0x0E); ++ write_reg(par, 0xB4, 0x04); ++ ++ /* Internal Register */ ++ write_reg(par, 0xA8, 0x19); ++ write_reg(par, 0xB8, 0x08); ++ ++ /* Tesla Control */ ++ write_reg(par, 0xE8, 0x24); ++ write_reg(par, 0xE9, 0x48); ++ write_reg(par, 0xEA, 0x22); ++ ++ /* Interface Control */ ++ write_reg(par, 0xC6, 0x30); ++ write_reg(par, 0xC7, 0x18); ++ ++ /* Positive Gamma Control */ ++ write_reg(par, 0xF0, ++ 0x1F, 0x28, 0x04, 0x3E, 0x2A, 0x2E, 0x20, ++ 0x00, 0x0C, 0x06, 0x00, 0x1C, 0x1F, 0x0F); ++ ++ /* Negative Gamma Control */ ++ write_reg(par, 0xF1, ++ 0x00, 0x2D, 0x2F, 0x3C, 0x6F, 0x1C, 0x0B, ++ 0x00, 0x00, 0x00, 0x07, 0x0D, 0x11, 0x0F); ++ ++ /* Tearing Effect Line ON */ ++ write_reg(par, 0x35, 0x00); ++ ++ /* Pixel Format: RGB565 (16 bits/pixel) */ ++ write_reg(par, GC9107_COLMOD, 0x55); ++ ++ /* Display Inversion OFF - looks bad with INVON */ ++ write_reg(par, GC9107_INVOFF); ++ ++ /* Sleep Out */ ++ write_reg(par, GC9107_SLPOUT); ++ mdelay(120); ++ ++ /* Display ON */ ++ write_reg(par, GC9107_DISPON); ++ mdelay(20); ++ ++ return 0; ++} ++ ++/* ++ * Set address window with proper offsets for GC9107 ++ * GC9107 GRAM is 132x130, but display is 128x128 ++ * Requires COL_OFFSET=2, ROW_OFFSET=1 ++ */ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ /* GC9107 specific offsets */ ++ const int col_offset = 2; ++ const int row_offset = 1; ++ ++ /* Column Address Set */ ++ write_reg(par, GC9107_CASET, ++ (xs + col_offset) >> 8, ++ (xs + col_offset) & 0xFF, ++ (xe + col_offset) >> 8, ++ (xe + col_offset) & 0xFF); ++ ++ /* Row Address Set */ ++ write_reg(par, GC9107_RASET, ++ (ys + row_offset) >> 8, ++ (ys + row_offset) & 0xFF, ++ (ye + row_offset) >> 8, ++ (ye + row_offset) & 0xFF); ++ ++ /* Memory Write */ ++ write_reg(par, GC9107_RAMWR); ++} ++ ++/* ++ * Memory Data Access Control (MADCTL) ++ * Controls rotation and BGR mode ++ */ ++static int set_var(struct fbtft_par *par) ++{ ++ u8 madctl = 0; ++ ++ /* Rotation */ ++ switch (par->info->var.rotate) { ++ case 0: ++ madctl = 0x00; ++ break; ++ case 90: ++ madctl = 0x60; /* MV | MX */ ++ break; ++ case 180: ++ madctl = 0xC0; /* MY | MX */ ++ break; ++ case 270: ++ madctl = 0xA0; /* MV | MY */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* BGR mode from DTS */ ++ if (par->bgr) ++ madctl |= 0x08; ++ ++ write_reg(par, GC9107_MADCTL, madctl); ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .bpp = BPP, ++ .fps = 30, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++ ++FBTFT_REGISTER_DRIVER(DRVNAME, "galaxycore,gc9107", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:gc9107"); ++MODULE_ALIAS("platform:gc9107"); ++ ++MODULE_DESCRIPTION("FB driver for GC9107 LCD Controller"); ++MODULE_AUTHOR("Based on Espressif ESP-IDF driver"); ++MODULE_LICENSE("GPL"); +--- a/drivers/staging/fbtft/Kconfig ++++ b/drivers/staging/fbtft/Kconfig +@@ -39,6 +39,12 @@ config FB_TFT_AGM1264K_FL + help + Generic Framebuffer support for AGM1264K-FL LCD display + ++config FB_TFT_GC9107 ++ tristate "FB driver for the GC9107 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for GalaxyCore GC9107 128x128 LCD ++ + config FB_TFT_BD663474 + tristate "FB driver for the BD663474 LCD Controller" + depends on FB_TFT +--- a/drivers/staging/fbtft/Makefile ++++ b/drivers/staging/fbtft/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_FB_TFT) += fbtft.o + fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o + + obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o ++obj-$(CONFIG_FB_TFT_GC9107) += fb_gc9107.o + obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o + obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o diff --git a/packages/configs-mf68e/Makefile b/packages/configs-mf68e/Makefile new file mode 100644 index 0000000..c0a8357 --- /dev/null +++ b/packages/configs-mf68e/Makefile @@ -0,0 +1,55 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=configs-mf68e +PKG_VERSION:=1.0 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=Utilities + CATEGORY:=Utilities + TITLE:=Default Settings For mf68e + DEPENDS:=+PACKAGE_luci:luci-proto-modemmanager +endef + +# Optional description +define Package/$(PKG_NAME)/description +Tweaks and defaults for mf68e, including cleanup of problematic APK feeds and LuCI battery widget. +endef + +define Build/Configure +endef + +define Build/Compile +endef + +# Install configuration files and payload you already had +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/uci_defaults $(1)/etc/uci-defaults/99-default-config + + $(INSTALL_DIR) $(1)/root/ + $(INSTALL_BIN) ./files/luci-app-tailscale*.apk $(1)/root/ + + # Battery widget for LuCI + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/admin_status/index + $(INSTALL_DATA) ./files/battery.htm $(1)/usr/lib/lua/luci/view/admin_status/index/20_battery.htm +endef + +define Package/$(PKG_NAME)/postinst +#!/bin/sh +set -e +FILE="/etc/apk/repositories.d/distfeeds.list" +ROOT="$${IPKG_INSTROOT:-/}" +TARGET_FILE="$$ROOT$$FILE" + +if [ -f "$$TARGET_FILE" ]; then + sed -i '\|/targets/msm89xx/msm8916/packages|d' "$$TARGET_FILE" || true +fi + +exit 0 +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/packages/configs-mf68e/files/battery.htm b/packages/configs-mf68e/files/battery.htm new file mode 100644 index 0000000..73bf072 --- /dev/null +++ b/packages/configs-mf68e/files/battery.htm @@ -0,0 +1,69 @@ +<%- +local sys = require "luci.sys" +local utl = require "luci.util" + +local bms_path = "/sys/class/power_supply/pm8916-bms-vm" +local charger_path = "/sys/class/power_supply/pm8916-lbc-chgr" + +local function read_file(path) + local f = io.open(path, "r") + if f then + local val = f:read("*all") + f:close() + return utl.trim(val) + end + return nil +end + +local voltage = read_file(bms_path .. "/voltage_now") +local status = read_file(bms_path .. "/status") +local health = read_file(bms_path .. "/health") +local vmax = read_file(bms_path .. "/voltage_max_design") +local vmin = read_file(bms_path .. "/voltage_min_design") +local online = read_file(charger_path .. "/online") + +-- Calcular porcentaje aproximado basado en voltaje +local percentage = "N/A" +if voltage and vmax and vmin then + local v = tonumber(voltage) + local vmax_n = tonumber(vmax) + local vmin_n = tonumber(vmin) + if v and vmax_n and vmin_n then + local pct = ((v - vmin_n) / (vmax_n - vmin_n)) * 100 + pct = math.max(0, math.min(100, pct)) + percentage = string.format("%.0f%%", pct) + end +end +-%> + +<% if voltage then %> +
+ <%:Battery Status%> +
+
+
<%:Property%>
+
<%:Value%>
+
+
+
<%:Estimated Capacity%>
+
<%=percentage%>
+
+
+
<%:Voltage%>
+
<%=string.format("%.2f V", tonumber(voltage)/1000000)%>
+
+
+
<%:Status%>
+
<%=status%>
+
+
+
<%:Health%>
+
<%=health%>
+
+
+
<%:Charger%>
+
<%=online == "1" and "Connected" or "Disconnected"%>
+
+
+
+<% end %> diff --git a/packages/configs-mf68e/files/luci-app-tailscale-1.2.6-r1.apk b/packages/configs-mf68e/files/luci-app-tailscale-1.2.6-r1.apk new file mode 100644 index 0000000..726f159 Binary files /dev/null and b/packages/configs-mf68e/files/luci-app-tailscale-1.2.6-r1.apk differ diff --git a/packages/configs-mf68e/files/uci_defaults b/packages/configs-mf68e/files/uci_defaults new file mode 100644 index 0000000..dcd0c4b --- /dev/null +++ b/packages/configs-mf68e/files/uci_defaults @@ -0,0 +1,47 @@ +#!/bin/sh + +# Enabling WiFi +uci set wireless.@wifi-device[0].disabled='0' +uci set wireless.@wifi-iface[0].disabled='0' + +# Create modem interface +uci set network.modem=interface +uci set network.modem.proto='modemmanager' +uci set network.modem.device='qcom-soc' +uci set network.modem.auth='none' +uci set network.modem.apn='internet' +uci set network.modem.allowedmode='4g|3g' +uci set network.modem.preferredmode='4g' +uci set network.modem.ifname='wwan0' +uci del_list firewall.@zone[1].network='modem' +uci add_list firewall.@zone[1].network='modem' + +# We don't have eth0 +uci -q del_list "network.@device[0].ports=eth0" 2>/dev/null || true +sed -i 's/"device": "eth0"/"device": "br-lan"/g' /etc/board.json + +# Commit all changes +uci commit + +# Apply network and wireless configuration +wifi reload +/etc/init.d/network restart + +cat > /etc/rc.button/power << 'EOF' +#!/bin/sh + +SECONDS=3 + +[ "${ACTION}" = "pressed" ] && exit ${SECONDS} + +[ "${ACTION}" = "timeout" ] && { + logger "Power held for ${SECONDS}: Shutting down system" + poweroff + exit 0 +} + +exit 0 +EOF +chmod +x /etc/rc.button/power + +exit 0 diff --git a/packages/configs-uz801/Makefile b/packages/configs-uz801/Makefile index b70ddff..121c933 100644 --- a/packages/configs-uz801/Makefile +++ b/packages/configs-uz801/Makefile @@ -1,4 +1,3 @@ - include $(TOPDIR)/rules.mk PKG_NAME:=configs-uz801 @@ -11,13 +10,13 @@ include $(INCLUDE_DIR)/package.mk define Package/$(PKG_NAME) SECTION:=Utilities CATEGORY:=Utilities - TITLE:=Defaults Setting For uz801v3 + TITLE:=Default Settings For uz801v3 DEPENDS:=+PACKAGE_luci:luci-proto-modemmanager endef -# (Opcional) Descripción +# Optional description define Package/$(PKG_NAME)/description -Tweaks y defaults para uz801v3, incluyendo limpieza de feeds APK problemáticos. +Tweaks and defaults for uz801v3, including cleanup of problematic APK feeds. endef define Build/Configure @@ -26,7 +25,7 @@ endef define Build/Compile endef -# Instala ficheros de configuración y payload que ya tenías +# Install configuration files and payload you already had define Package/$(PKG_NAME)/install $(INSTALL_DIR) $(1)/etc/uci-defaults $(INSTALL_BIN) ./files/uci_defaults $(1)/etc/uci-defaults/99-default-config diff --git a/packages/fbtft-gc9107/Makefile b/packages/fbtft-gc9107/Makefile new file mode 100644 index 0000000..7ad4013 --- /dev/null +++ b/packages/fbtft-gc9107/Makefile @@ -0,0 +1,56 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=fbtft-gc9107 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/fbtft-gc9107 + SUBMENU:=Video Support + TITLE:=GC9107 Framebuffer Driver + DEPENDS:=@TARGET_msm89xx +kmod-backlight + KCONFIG:= \ + CONFIG_FB=y \ + CONFIG_FB_BACKLIGHT=y \ + CONFIG_FB_DEFERRED_IO=y \ + CONFIG_FB_SYS_FILLRECT=y \ + CONFIG_FB_SYS_COPYAREA=y \ + CONFIG_FB_SYS_IMAGEBLIT=y \ + CONFIG_FB_SYS_FOPS=y \ + CONFIG_FB_DMAMEM_HELPERS=y \ + CONFIG_BACKLIGHT_CLASS_DEVICE=y \ + CONFIG_BACKLIGHT_PWM=y \ + CONFIG_FONTS=y \ + CONFIG_FONT_8x16=y \ + CONFIG_FRAMEBUFFER_CONSOLE=y \ + CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=n \ + CONFIG_VT_HW_CONSOLE_BINDING=y \ + CONFIG_VT_CONSOLE_SLEEP=y \ + CONFIG_STAGING=y \ + CONFIG_FB_TFT=y \ + CONFIG_FB_TFT_GC9107=y + FILES:= \ + $(LINUX_DIR)/drivers/staging/fbtft/fbtft.ko \ + $(LINUX_DIR)/drivers/staging/fbtft/fb_gc9107.ko + AUTOLOAD:=$(call AutoProbe,fbtft fb_gc9107) +endef + +define KernelPackage/fbtft-gc9107/description + GC9107 framebuffer driver for MF68E device with boot logo support. +endef + +define Build/Compile +endef + +define KernelPackage/fbtft-gc9107/install + $(INSTALL_DIR) $(1)/etc + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_DIR) $(1)/lib/preinit + $(INSTALL_DATA) ./files/boot_logo.fb $(1)/etc/boot_logo.fb + $(INSTALL_DATA) ./files/poweroff_logo.fb $(1)/etc/poweroff_logo.fb + $(INSTALL_BIN) ./files/05_display_logo $(1)/lib/preinit/05_display_logo + $(INSTALL_BIN) ./files/01_poweroff_logo $(1)/etc/init.d/01_poweroff_logo +endef + +$(eval $(call KernelPackage,fbtft-gc9107)) diff --git a/packages/fbtft-gc9107/files/01_poweroff_logo b/packages/fbtft-gc9107/files/01_poweroff_logo new file mode 100644 index 0000000..159a7f8 --- /dev/null +++ b/packages/fbtft-gc9107/files/01_poweroff_logo @@ -0,0 +1,13 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=01 + +start() { + return 0 +} + +stop() { + cat /etc/poweroff_logo.fb > /dev/fb0 2>/dev/null + sleep 2 +} diff --git a/packages/fbtft-gc9107/files/05_display_logo b/packages/fbtft-gc9107/files/05_display_logo new file mode 100644 index 0000000..7e4a342 --- /dev/null +++ b/packages/fbtft-gc9107/files/05_display_logo @@ -0,0 +1,30 @@ +#!/bin/sh + +show_logo() { + ( + local BACKLIGHT="/sys/class/backlight/backlight" + local i=0 + + while [ ! -c /dev/fb0 ] && [ $i -lt 100 ]; do + usleep 100000 # 100ms = 0.1s + i=$((i + 1)) + done + + if [ -c /dev/fb0 ] && [ -f /etc/boot_logo.fb ]; then + cat /etc/boot_logo.fb > /dev/fb0 2>/dev/null + fi + + i=0 + while [ ! -d "$BACKLIGHT" ] && [ $i -lt 50 ]; do + usleep 100000 # 100ms = 0.1s + i=$((i + 1)) + done + + if [ -d "$BACKLIGHT" ]; then + echo 0 > $BACKLIGHT/bl_power 2>/dev/null + echo 1 > $BACKLIGHT/brightness 2>/dev/null + fi + ) & +} + +boot_hook_add preinit_main show_logo diff --git a/packages/fbtft-gc9107/files/boot_logo.fb b/packages/fbtft-gc9107/files/boot_logo.fb new file mode 100644 index 0000000..d2cdfdf Binary files /dev/null and b/packages/fbtft-gc9107/files/boot_logo.fb differ diff --git a/packages/fbtft-gc9107/files/poweroff_logo.fb b/packages/fbtft-gc9107/files/poweroff_logo.fb new file mode 100644 index 0000000..0407fdf Binary files /dev/null and b/packages/fbtft-gc9107/files/poweroff_logo.fb differ diff --git a/packages/fbtft-gc9107/files/readme.md b/packages/fbtft-gc9107/files/readme.md new file mode 100644 index 0000000..e56fca0 --- /dev/null +++ b/packages/fbtft-gc9107/files/readme.md @@ -0,0 +1,5 @@ +## Create boot image for framebuffer +We are converting the image using `ffmpeg`. Ensure you have it installed. +``` +ffmpeg -i -vf "scale=128:128" -pix_fmt rgb565le -f rawvideo .fb +``` \ No newline at end of file diff --git a/packages/router-display/Makefile b/packages/router-display/Makefile new file mode 100644 index 0000000..0c51961 --- /dev/null +++ b/packages/router-display/Makefile @@ -0,0 +1,67 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=router-display +PKG_VERSION:=1.0 +PKG_RELEASE:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +PKG_QRGEN_NAME:=QR-Code-generator +PKG_QRGEN_VERSION:=v1.8.0 +PKG_QRGEN_SOURCE:=$(PKG_QRGEN_VERSION).tar.gz +PKG_QRGEN_URL:=https://github.com/nayuki/QR-Code-generator/archive/refs/tags +PKG_QRGEN_HASH:=skip + +include $(INCLUDE_DIR)/package.mk + +define Package/router-display + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Framebuffer router status display + DEPENDS:=+libfreetype +kmod-button-hotplug +dejavu-fonts-ttf-DejaVuSans-Bold +dejavu-fonts-ttf-DejaVuSans +endef + +define Package/router-display/description + Display router status on RGB565 framebuffer +endef + +define Download/qrcodegen + URL:=$(PKG_QRGEN_URL) + FILE:=qrcodegen-$(PKG_QRGEN_VERSION).tar.gz + URL_FILE:=$(PKG_QRGEN_VERSION).tar.gz + HASH:=$(PKG_QRGEN_HASH) +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/main.c $(PKG_BUILD_DIR)/ + $(eval $(call Download,qrcodegen)) + tar -xzf $(DL_DIR)/qrcodegen-$(PKG_QRGEN_VERSION).tar.gz -C $(PKG_BUILD_DIR) + $(CP) $(PKG_BUILD_DIR)/$(PKG_QRGEN_NAME)-*/c/qrcodegen.c $(PKG_BUILD_DIR)/ + $(CP) $(PKG_BUILD_DIR)/$(PKG_QRGEN_NAME)-*/c/qrcodegen.h $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + $(TARGET_CC) $(TARGET_CFLAGS) -O3 -ffast-math \ + $(TARGET_CPPFLAGS) \ + -I$(STAGING_DIR)/usr/include/freetype2 \ + $(TARGET_LDFLAGS) \ + -o $(PKG_BUILD_DIR)/router-display \ + $(PKG_BUILD_DIR)/main.c \ + $(PKG_BUILD_DIR)/qrcodegen.c \ + -lfreetype -lm +endef + +define Package/router-display/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/router-display $(1)/usr/bin/ + + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/update-display.sh $(1)/usr/sbin/update-display + + $(INSTALL_DIR) $(1)/etc/hotplug.d/button + $(INSTALL_BIN) ./files/hotplugs/20_power_dimm $(1)/etc/hotplug.d/button/20_power_dimm + $(INSTALL_BIN) ./files/hotplugs/30_power_update $(1)/etc/hotplug.d/button/30_power_update +endef + +$(eval $(call BuildPackage,router-display)) diff --git a/packages/router-display/files/hotplugs/20_power_dimm b/packages/router-display/files/hotplugs/20_power_dimm new file mode 100644 index 0000000..600e295 --- /dev/null +++ b/packages/router-display/files/hotplugs/20_power_dimm @@ -0,0 +1,91 @@ +#!/bin/sh +# /etc/hotplug.d/button/20_power_dimm + +# Only process power button events +[ "$BUTTON" = "power" ] || exit 0 + +# Configuration variables +TIMEOUT_DIM=5 +TIMEOUT_OFF=3 +BACKLIGHT_PATH="/sys/class/backlight/backlight" +BRIGHTNESS_DIM_DIVISOR=8 +BRIGHTNESS_OFF=1 + +# Calculated variables +MAX_BRIGHTNESS=$(cat $BACKLIGHT_PATH/max_brightness 2>/dev/null || echo 1785) +BRIGHTNESS_FULL=$MAX_BRIGHTNESS +BRIGHTNESS_DIM=$((MAX_BRIGHTNESS / BRIGHTNESS_DIM_DIVISOR)) + +# PID file for timer management +TIMER_PID_FILE="/tmp/screen_timer.pid" + +maybe_set_lockscreen(){ + # Check if system is shutting down + if pgrep poweroff >/dev/null 2>&1 || \ + pgrep halt >/dev/null 2>&1 || \ + pgrep reboot >/dev/null 2>&1; then + logger -t display "System shutting down - skipping lockscreen" + return 0 + fi + + # Show lockscreen if brightness is OFF + [ -n "$1" ] && [ "$1" = "$BRIGHTNESS_OFF" ] && [ -f /etc/boot_logo.fb ] && \ + cat /etc/boot_logo.fb > /dev/fb0 2>/dev/null +} + +# Set brightness level +set_brightness() { + echo "$1" > "$BACKLIGHT_PATH/brightness" + + # Check if we should show lockscreen + maybe_set_lockscreen "$1" +} + +# Function to start a timer +# Parameters: $1=timeout (seconds), $2=brightness level, $3=log message +start_timer() { + local timeout=$1 + local brightness=$2 + local log_msg=$3 + + ( + sleep "$timeout" + logger -t display "$log_msg" + set_brightness "$brightness" + ) >/dev/null 2>&1 /dev/null + done < "$TIMER_PID_FILE" + rm -f "$TIMER_PID_FILE" + fi +} + +# Only act on button release +[ "${ACTION}" = "released" ] && { + logger -t display "Button event detected" + + # Cancel previous timers if any + cancel_timers + + # Set full brightness immediately + logger -t display "Backlight: FULL" + set_brightness $BRIGHTNESS_FULL + + # Start timer 1: DIM after TIMEOUT_DIM seconds + pid1=$(start_timer $TIMEOUT_DIM $BRIGHTNESS_DIM "Backlight: DIM") + echo "$pid1" >> "$TIMER_PID_FILE" + + # Start timer 2: OFF after (TIMEOUT_DIM + TIMEOUT_OFF) seconds + TOTAL_TIMEOUT=$((TIMEOUT_DIM + TIMEOUT_OFF)) + pid2=$(start_timer $TOTAL_TIMEOUT $BRIGHTNESS_OFF "Backlight: OFF") + echo "$pid2" >> "$TIMER_PID_FILE" +} + +exit 0 diff --git a/packages/router-display/files/hotplugs/30_power_update b/packages/router-display/files/hotplugs/30_power_update new file mode 100644 index 0000000..d59b7ee --- /dev/null +++ b/packages/router-display/files/hotplugs/30_power_update @@ -0,0 +1,20 @@ +#!/bin/sh +# /etc/hotplug.d/button/30_power_update + +# Only process power button events +[ "$BUTTON" = "power" ] || exit 0 + +# Execute update-display on button release +[ "${ACTION}" = "released" ] && { + # Check if system is shutting down + if pgrep poweroff >/dev/null 2>&1 || \ + pgrep halt >/dev/null 2>&1 || \ + pgrep reboot >/dev/null 2>&1; then + logger -t display "System shutting down - skipping update-display" + exit 0 + fi + + /usr/sbin/update-display +} + +exit 0 diff --git a/packages/router-display/files/update-display.sh b/packages/router-display/files/update-display.sh new file mode 100644 index 0000000..9832623 --- /dev/null +++ b/packages/router-display/files/update-display.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# /usr/sbin/update-display +# Optimized display update script - caches operator name (requires reboot to change SIM) + +# === BATTERY === +BMS_PATH="/sys/class/power_supply/pm8916-bms-vm" + +VOLTAGE=$(cat "$BMS_PATH/voltage_now" 2>/dev/null) +VMAX=$(cat "$BMS_PATH/voltage_max_design" 2>/dev/null) +VMIN=$(cat "$BMS_PATH/voltage_min_design" 2>/dev/null) + +if [ -n "$VOLTAGE" ] && [ -n "$VMAX" ] && [ -n "$VMIN" ]; then + BATTERY=$(awk "BEGIN {pct = (($VOLTAGE - $VMIN) / ($VMAX - $VMIN)) * 100; if(pct < 0) pct=0; if(pct > 100) pct=100; printf \"%.0f\", pct}") +else + BATTERY=100 +fi + +# === WiFi AP === +SHOW_QR=0 +if ip link show phy0-ap0 2>/dev/null | grep -q "state UP"; then + SHOW_QR=1 +fi + +# === MODEM (OPTIMIZED - single mmcli call + operator cache) === +OPERATOR_CACHE="/tmp/modem_operator.cache" +MODEM_IDX=$(mmcli -L 2>/dev/null | grep -oE 'Modem/[0-9]+' | head -1 | cut -d'/' -f2) + +if [ -n "$MODEM_IDX" ]; then + # Single mmcli call - cache all modem data + MODEM_DATA=$(mmcli -m "$MODEM_IDX" -K 2>/dev/null) + + # Try to get operator from cache first (SIM doesn't change without reboot) + if [ -f "$OPERATOR_CACHE" ]; then + OPERATOR=$(cat "$OPERATOR_CACHE") + else + # Cache miss - fetch operator from SIM + SIM_IDX=$(echo "$MODEM_DATA" | grep 'modem.generic.sim' | awk -F': ' '{print $2}' | grep -oE '[0-9]+$') + + if [ -n "$SIM_IDX" ]; then + OPERATOR=$(mmcli -i "$SIM_IDX" -K 2>/dev/null | grep 'sim.properties.operator-name' | awk -F': ' '{print $2}') + fi + + # Fallback: operator from network (use cached modem data) + if [ -z "$OPERATOR" ]; then + OPERATOR=$(echo "$MODEM_DATA" | grep 'modem.3gpp.operator-name' | awk -F': ' '{print $2}') + fi + + # Cache operator for future calls + if [ -n "$OPERATOR" ]; then + echo "$OPERATOR" > "$OPERATOR_CACHE" + fi + fi + + # Access technology (use cached modem data) + ACCESS_TECH=$(echo "$MODEM_DATA" | grep 'modem.generic.access-technologies' | awk -F': ' '{print $2}') + + # Determine network type only if connected + if [ -n "$ACCESS_TECH" ] && [ "$ACCESS_TECH" != "unknown" ]; then + case "$ACCESS_TECH" in + *lte*) NETWORK="4G" ;; + *umts*|*hspa*|*hsupa*|*hsdpa*) NETWORK="3G" ;; + *edge*|*gprs*|*gsm*) NETWORK="2G" ;; + *) NETWORK="$ACCESS_TECH" ;; + esac + else + # No connection: show operator but no network type + NETWORK="" + fi +else + # No modem + OPERATOR="" + NETWORK="" +fi + +# === WiFi Info === +SSID=$(uci get wireless.@wifi-iface[0].ssid 2>/dev/null || echo "WiFi") +PASSWORD=$(uci get wireless.@wifi-iface[0].key 2>/dev/null || echo "") +HOSTNAME=$(uci get system.@system[0].hostname 2>/dev/null || echo "Router") + +# === Update Display === +if [ "$SHOW_QR" -eq 1 ]; then + /usr/bin/router-display -q -b "$BATTERY" -n "$OPERATOR" -t "$NETWORK" -s "$SSID" -p "$PASSWORD" -h "$HOSTNAME" > /dev/fb0 +else + /usr/bin/router-display -b "$BATTERY" -n "$OPERATOR" -t "$NETWORK" -s "$SSID" -p "$PASSWORD" -h "$HOSTNAME" > /dev/fb0 +fi diff --git a/packages/router-display/src/main.c b/packages/router-display/src/main.c new file mode 100644 index 0000000..a6b1ec8 --- /dev/null +++ b/packages/router-display/src/main.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H +#include "qrcodegen.h" + +#define WIDTH 128 +#define HEIGHT 128 +#define RGB565(r,g,b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)) + +#define COLOR_WHITE RGB565(255, 255, 255) +#define COLOR_BLACK RGB565(0, 0, 0) + +#define QR_SIZE 108 +#define QR_TOP_MARGIN 6 +#define TEXT_HEIGHT 13 +#define MIN_MODULE_SIZE 3 // Minimum pixels per QR module for readability + +typedef struct { + uint16_t data[WIDTH * HEIGHT]; +} Framebuffer; + +typedef struct { + int battery; + char operator[32]; + char network_type[8]; + char ssid[64]; + char password[64]; + char hostname[32]; + int show_qr; + int uppercase; +} DisplayConfig; + +void fb_init(Framebuffer *fb) { + memset(fb->data, 0, sizeof(fb->data)); +} + +void fb_put_pixel(Framebuffer *fb, int x, int y, uint16_t color) { + if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) { + fb->data[y * WIDTH + x] = color; + } +} + +void fb_blend_pixel(Framebuffer *fb, int x, int y, unsigned char gray) { + if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return; + if (gray == 0) return; + + uint16_t bg_color = fb->data[y * WIDTH + x]; + + uint8_t bg_r = (bg_color >> 11) << 3; + uint8_t bg_g = ((bg_color >> 5) & 0x3F) << 2; + uint8_t bg_b = (bg_color & 0x1F) << 3; + + float alpha = gray / 255.0f; + uint8_t r = (uint8_t)(255 * alpha + bg_r * (1.0f - alpha)); + uint8_t g = (uint8_t)(255 * alpha + bg_g * (1.0f - alpha)); + uint8_t b = (uint8_t)(255 * alpha + bg_b * (1.0f - alpha)); + + fb->data[y * WIDTH + x] = RGB565(r, g, b); +} + +void fb_draw_rect(Framebuffer *fb, int x, int y, int w, int h, uint16_t color) { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + fb_put_pixel(fb, x + i, y + j, color); + } + } +} + +void fb_draw_text(Framebuffer *fb, FT_Face face, const char *text, int x, int y, int size) { + int pen_x = x; + + for (const char *p = text; *p; p++) { + FT_Set_Pixel_Sizes(face, 0, size); + + if (FT_Load_Char(face, *p, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL)) { + continue; + } + + FT_GlyphSlot slot = face->glyph; + FT_Bitmap bitmap = slot->bitmap; + + for (unsigned int row = 0; row < bitmap.rows; row++) { + for (unsigned int col = 0; col < bitmap.width; col++) { + int px = pen_x + slot->bitmap_left + col; + int py = y - slot->bitmap_top + row; + + unsigned char gray = bitmap.buffer[row * bitmap.pitch + col]; + + if (gray > 0) { + fb_blend_pixel(fb, px, py, gray); + } + } + } + + pen_x += slot->advance.x >> 6; + } +} + +int fb_get_text_width(FT_Face face, const char *text, int size) { + int width = 0; + FT_Set_Pixel_Sizes(face, 0, size); + + for (const char *p = text; *p; p++) { + if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) { + continue; + } + width += face->glyph->advance.x >> 6; + } + + return width; +} + +int fb_draw_qr(Framebuffer *fb, const char *text, int x, int y, int size) { + uint8_t qr_data[qrcodegen_BUFFER_LEN_MAX]; + uint8_t temp_buffer[qrcodegen_BUFFER_LEN_MAX]; + + bool ok = qrcodegen_encodeText(text, temp_buffer, qr_data, + qrcodegen_Ecc_LOW, + qrcodegen_VERSION_MIN, + qrcodegen_VERSION_MAX, + qrcodegen_Mask_AUTO, true); + + if (!ok) { + fprintf(stderr, "QR generation failed\n"); + return 0; + } + + int qr_modules = qrcodegen_getSize(qr_data); + int module_size = size / qr_modules; + + // Check if QR modules are too small (minimum pixels per module) + if (module_size < MIN_MODULE_SIZE) { + fprintf(stderr, "QR too large for display area (needs %d modules, only fits %dpx modules)\n", + qr_modules, module_size); + return 0; // Don't draw, QR would be unreadable + } + + // Calculate actual QR size and center horizontally, align top vertically + int actual_qr_size = qr_modules * module_size; + int offset_x = (size - actual_qr_size) / 2; // Center horizontally + int offset_y = 0; // Top aligned + + for (int row = 0; row < qr_modules; row++) { + for (int col = 0; col < qr_modules; col++) { + if (qrcodegen_getModule(qr_data, col, row)) { + fb_draw_rect(fb, + x + offset_x + col * module_size, + y + offset_y + row * module_size, + module_size, module_size, + COLOR_WHITE); + } + } + } + + return 1; // QR drawn successfully +} + +void str_to_upper(char *str) { + for (char *p = str; *p; p++) { + *p = toupper((unsigned char)*p); + } +} + +void generate_display(Framebuffer *fb, DisplayConfig *cfg, FT_Face face) { + fb_init(fb); + + char operator_text[32], network_text[8], hostname_text[32]; + strncpy(operator_text, cfg->operator, 31); + strncpy(network_text, cfg->network_type, 7); + strncpy(hostname_text, cfg->hostname, 31); + + if (cfg->uppercase) { + str_to_upper(operator_text); + str_to_upper(network_text); + str_to_upper(hostname_text); + } + + // Calculate QR position: centered horizontally, at top with margin + int qr_x = (WIDTH - QR_SIZE) / 2; + int qr_y = QR_TOP_MARGIN; + + if (cfg->show_qr) { + char wifi_qr[256]; + snprintf(wifi_qr, sizeof(wifi_qr), + "WIFI:T:WPA;S:%s;P:%s;;", cfg->ssid, cfg->password); + + // Try to draw QR, if it doesn't fit (returns 0), screen stays black + fb_draw_qr(fb, wifi_qr, qr_x, qr_y, QR_SIZE); + } + // If no QR or QR too large: stay black (fb_init already cleared to black) + + int fontsz = 12; + int line1_y = HEIGHT - fontsz - 3; + int line2_y = HEIGHT - 3; + + fb_draw_text(fb, face, operator_text, 2, line1_y, fontsz); + + int net_width = fb_get_text_width(face, network_text, fontsz); + fb_draw_text(fb, face, network_text, WIDTH - net_width - 2, line1_y, fontsz); + + fb_draw_text(fb, face, hostname_text, 2, line2_y, fontsz); + + char battery_text[8]; + snprintf(battery_text, sizeof(battery_text), "%d%%", cfg->battery); + int battery_width = fb_get_text_width(face, battery_text, fontsz); + fb_draw_text(fb, face, battery_text, WIDTH - battery_width - 2, line2_y, fontsz); +} + +void print_usage(const char *prog) { + fprintf(stderr, "Usage: %s [OPTIONS]\n", prog); + fprintf(stderr, " -b NUM Battery (0-100)\n"); + fprintf(stderr, " -n NAME Operator name\n"); + fprintf(stderr, " -t TYPE Network type (4G, LTE)\n"); + fprintf(stderr, " -s SSID WiFi SSID\n"); + fprintf(stderr, " -p PASS WiFi password\n"); + fprintf(stderr, " -h HOST Hostname\n"); + fprintf(stderr, " -q Show QR code (default: show logo)\n"); + fprintf(stderr, " -c Convert text to UPPERCASE\n"); +} + +int main(int argc, char *argv[]) { + DisplayConfig cfg = { + .battery = 100, + .operator = "Unknown", + .network_type = "4G", + .ssid = "WiFi", + .password = "password", + .hostname = "Router", + .show_qr = 0, + .uppercase = 0 + }; + + int opt; + while ((opt = getopt(argc, argv, "b:n:t:s:p:h:qc")) != -1) { + switch (opt) { + case 'b': cfg.battery = atoi(optarg); break; + case 'n': strncpy(cfg.operator, optarg, 31); break; + case 't': strncpy(cfg.network_type, optarg, 7); break; + case 's': strncpy(cfg.ssid, optarg, 63); break; + case 'p': strncpy(cfg.password, optarg, 63); break; + case 'h': strncpy(cfg.hostname, optarg, 31); break; + case 'q': cfg.show_qr = 1; break; + case 'c': cfg.uppercase = 1; break; + default: + print_usage(argv[0]); + return 1; + } + } + + FT_Library ft; + if (FT_Init_FreeType(&ft)) { + fprintf(stderr, "FreeType init failed\n"); + return 1; + } + + FT_Face face; + const char *fonts[] = { + "/usr/share/fonts/ttf-dejavu/DejaVuSans.ttf", + "/usr/share/fonts/ttf-dejavu/DejaVuSans-Bold.ttf", + NULL + }; + + int found = 0; + for (int i = 0; fonts[i]; i++) { + if (FT_New_Face(ft, fonts[i], 0, &face) == 0) { + found = 1; + break; + } + } + + if (!found) { + fprintf(stderr, "No font found\n"); + FT_Done_FreeType(ft); + return 1; + } + + Framebuffer fb; + generate_display(&fb, &cfg, face); + + fwrite(fb.data, sizeof(uint16_t), WIDTH * HEIGHT, stdout); + + FT_Done_Face(face); + FT_Done_FreeType(ft); + + return 0; +}