Commit Graph
610 Commits
Author SHA1 Message Date
piotr f183836b0f hilink: pace MSP VTX table frames 2026-09-28 16:07:47 +02:00
piotr fc34d457a0 hilink: synchronize live WFB state over MSP VTX 2026-09-28 15:03:22 +02:00
piotr 9b1f96bf92 hilink: resync MSP VTX table after FC restart 2026-09-28 13:30:31 +02:00
piotr c1886a666c hilink: wait for FC MSP VTX readiness before table sync 2026-09-28 12:15:29 +02:00
piotr c59e8aefda hilink: retry MSP VTX table after FC handshake 2026-09-28 11:00:58 +02:00
piotr 45c0c6b795 hilink: serialize MSP VTX tables correctly 2026-09-28 10:16:51 +02:00
piotr 847f0bb157 hilink: add read-only YAML-aware MSP VTX status 2026-09-27 23:01:52 +02:00
piotr b8c32c84a1 hilink: bake WFB management and low latency runtime hilink-urllc-20260927-b8c32c8 latest 2026-09-27 10:26:14 +02:00
piotr 8c4661665b hilink: omit unused 88XXau module from NOR image 2026-09-26 17:52:47 +02:00
piotr 2571aeaa4f ci: report rootfs contents when firmware exceeds flash 2026-09-26 15:35:46 +02:00
piotr d48f415bcf ci: include Node runtime for JavaScript actions 2026-09-26 15:03:28 +02:00
piotr 438b2fd47e ci: add dedicated OpenIPC builder runner image 2026-09-26 14:37:51 +02:00
piotr a8c9985704 ci: require preprovisioned build runner 2026-09-26 14:33:52 +02:00
piotr 79659433ea ci: install build dependencies with sudo 2026-09-26 14:30:59 +02:00
piotr bdd949c8c7 ci: add manual HiLink URLLC build workflow 2026-09-26 14:27:19 +02:00
ArthurKobaandGitHub a93762ee27 hi3516ev200: add GARUS GSL-5030X-30AP-NM device profile (SC2315E, 8M NOR, Ethernet only)
Device profile for GARUS GSL-5030X-30AP-NM on PCB BLK16EV2-4339P-38X38:
Hi3516EV200, 8 MiB SPI NOR, wired Ethernet, fixed lens, no radio.

Sensor path is SC2315E-compatible MIPI at 1920x1080. The stock firmware's
2304x1296 "3M" mode is an output scaling path; no native 3MP sensor mode was
confirmed, so the profile keeps the proven 1080p mode. The physical die is
unidentified — XM documentation points at SC4239H, which has no OpenIPC driver
path — so the README row carries SC2315E? deliberately.

Day/night uses GPIO15 as the hardware status input with a bistable IR-cut
filter on GPIO8/GPIO9. GPIO15 needs its EV200 pinmux reasserted on every boot,
so that read-modify-write lives in muxes.sh, which S30customizer runs each
boot, rather than in the once-only customizer.sh. transitionDelayMs is 150,
measured on this camera: an immediate transition showed a purple cast while the
filter was still moving.

The exclude list prunes 33 of the 34 sensor drivers the Hi3516EV200 packages
install, leaving libsns_sc2315e.so, and keeps iq/imx307.ini so the default.ini
symlink still resolves. Codec and fps are left unset because h264 at 20 fps are
already the shipped majestic defaults for this SoC and variant.

Built, flashed and booted on hardware: 1080p video and RTSP, H.264 and H.265,
Ethernet, microphone capture and speaker output, and automatic day/night
switching with IR-cut operation.
2026-09-22 11:52:17 +03:00
Siddhant SaurabhandGitHub 01328cd601 hi3518ev300: add Smitch 360 PTZ device profile (JXF23, RTL8188FU, 16M NOR) (#82)
HI3518EV300 with a JXF23 sensor and an RTL8188FU USB radio on 16 MiB NOR.
Video and Wi-Fi are working on the hardware; the motors are not yet, which the
README row records.

Finished off before merging, since the branch had sat since March:

BR2_PACKAGE_JSONFILTER is dropped, as #128 did for every other defconfig after
firmware/#2304. The repack.sh section heading that had been reworded in passing
is restored to what master has, and customizer.sh gets its missing trailing
newline. Rebased onto master, where the README row now sits alongside the
SpezVision SVI-252B entry added since.

The UBI config reference is corrected. It pointed at
scripts/ubifs/ubinize_hisilicon.cfg, which is not in the firmware tree — the
two devices using that name each ship a private copy — so the build reached
rootfs.ubifs and then failed on the missing file. It now uses the in-tree
ubinize.cfg, matching both hi3518ev200 ultimate boards; that suits the nand
repack, which passes uImage separately rather than embedding it in a volume.

This profile had never been compiled by CI before, which is why a defect this
plain survived two reviews. Both artifacts now build: NOR rootfs 6828/8192 KB,
NAND rootfs.ubi 13568/16384 KB.
2026-09-21 14:19:56 +03:00
Davide BeatriciandGitHub 66933efc54 t31: add Jienuo JN-107AR-E-WIFI device profile (#58)
T31X with an SC5235 sensor and an RTL8733BU USB radio on 16 MiB NOR.

The profile pins BR2_OPENIPC_SNS_MODEL, so ingenic-osdrv-t31 installs only this
board's sensor yaml and params blob and no exclude list is needed.

Rebuilt on current master before merging. The original branch carried an
unrelated 2025 commit from another contributor touching ssc338q_apfpv, whose
customizer no longer exists in the tree, and its README hunk had gone stale
against the device table; replaying either would only have produced conflicts
over devices this PR has nothing to do with. What landed is the net device
addition and nothing else.

BR2_PACKAGE_JSONFILTER is dropped, as #128 did for every other defconfig after
firmware/#2304.
2026-09-21 13:47:43 +03:00
ArthurKobaandGitHub 3924343c43 t31: add XVI ISI-2010C device profile (T31N, GC2053, Ethernet only) (#166)
Ingenic T31N with a GalaxyCore GC2053, 64 MiB RAM, 8 MiB SPI NOR and wired
Ethernet. Tested on the physical camera: builds, flashes and boots, with
Ethernet, GC2053 video, H.264/H.265 streaming, automatic day/night switching,
IR-cut control and audio capture/playback all working.

The board has no radio, so the defconfig drops wireless-tools, wpa_supplicant
and the Realtek driver that the nearest sibling carries, along with the QR
onboarding helper that only exists to join a network.

The kernel fragment turns off MMC, the wireless stack, USB and FAT rather than
forking the generic T31 config. Because FAT goes with it, the profile also
overrides /etc/modules: the stock list asks for vfat and exfat, which would no
longer exist.

The exclude list prunes motor.ko and the two sample_pwm modules. Those are
built for every T31 board by ingenic-opensdk regardless of the pinned sensor,
since they sit outside the sensor tree in the openingenic Kbuild, and no board
in the tree had pruned them until now. There is no Kconfig symbol to disable
them, so the exclude list is the only lever.
2026-09-21 12:27:03 +03:00
ArthurKobaandGitHub 9ea182a026 svi-252b: record that the IR illuminator is switched autonomously (#165)
The profile already left backlightPin unset, but the comment still described
the IR illuminator control as unresolved, which read as unfinished work rather
than as a board fact.

Hardware confirms the illuminator is driven by the board's own daylight-sensor
circuitry. The SoC only observes the resulting light state on GPIO3_0 and
drives the bistable IR-cut filter on GPIO34/33, so there is no SoC-controlled
backlight GPIO to configure here.

Documentation only; runtime configuration is unchanged. This retires the last
caveat the original SVI-252B K202 profile shipped with.
2026-09-21 12:26:40 +03:00
ArthurKobaandGitHub 9753e10c09 svi-252b: set the night mode transition delay this board's IR-cut filter needs (#163)
The profile went in with a known remainder: a brief purple frame during the
day/night transition, tracked upstream in OpenIPC/majestic#325 and deliberately
left without a board-specific workaround. That issue has since been fixed -- into
night the picture goes grey before the filter moves, coming back out it returns to
colour after the filter has shut, and the switch now reaches the stream on the next
frame instead of waiting out the current GOP. With that, dusk is clean on this
camera at the default `transitionDelayMs: 0`.

What is left is the dawn side, and it is mechanical: the bistable IR-cut filter is
still physically swinging shut while the picture is already back in colour, which
is worth about two frames of cast. `nightMode.transitionDelayMs` is the documented
pause between the picture changing and the filter moving (0-2000 ms, default 0),
and how fast a filter swings is a property of the board -- so this belongs in the
device profile rather than in any shared default. 150 ms clears it here.

Tested on the physical SVI-252B K202 by the author, repeated transitions in both
directions, both now clean.
2026-09-20 18:47:07 +03:00
ArthurKobaandGitHub ca991733a4 Add hi3516cv200_lite_spezvision-svi-252b-k202 device profile (SpezVision SVI-252B K202, IMX323, Ethernet only) (#161)
SpezVision SVI-252B K202 on board BLK16CV-0323-38X38-V1.01: HiSilicon Hi3516CV200,
Sony IMX323 on the I2C/DC 1080p configuration, 8 MiB SPI NOR, wired Ethernet only.

The board's day/night path is what needs describing. The hardware daylight sensor
is GPIO3_0 (legacy HiSilicon numbering 24) and the bistable IR-cut filter is
GPIO4_2 / GPIO4_1 (34 and 33), which the customizer seeds into majestic. GPIO3_0
has to be muxed to function 0 at 0x200f0034 before it can be read, and
load_hisilicon's imx323 branch does not touch that register, so the board ships a
muxes.sh for it — S30customizer runs that on every boot, independently of which
streamer is in use, so the pin contract holds for anything running on the camera.
No backlightPin is set: a separate software-controlled IR illuminator GPIO was not
proven on this board.

Rather than forking the whole hi3516cv200.generic.config into the device directory
the way the other CV200 profile does, the board carries a kernel config fragment
naming only its own hardware facts — no SD/MMC socket, no Wi-Fi radio, no used USB
path, NOR only — so it keeps tracking upstream kernel changes instead of shadowing
them. This is the first device here to use BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES.
Together with an exclude list trimmed to the one sensor this camera has, the image
lands at 3956736 B of rootfs, leaving 1256 KB of rootfs headroom and 524 KB of
kernel headroom on the 8 MiB layout.

Tested on physical hardware by the author: builds, flashes and boots, with IMX323
video, Ethernet, the hardware daylight sensor and automatic IR-cut switching all
working. A brief purple frame can still appear during the colour-to-grayscale
transition; that is tracked upstream in OpenIPC/majestic#325 and no board-specific
workaround is carried here.
2026-09-19 15:47:44 +03:00
5e1bb92866 ssc377_lite: add TP-Link Tapo C120 device profile (#162)
SSC377, SmartSens SC430AI, 16 MB NOR, RTL8188FTV on USB. The generic
ssc377_lite defconfig with the 16 MB layout, the rtl8188fu driver and the
SAE-capable wpa_supplicant-openipc; the Wi-Fi module's power gate (GPIO 42)
in the wireless/usb entry; IR-cut, backlight and IR LED pins; the SC430AI
IQ file.

Depends on OpenIPC/sensors#7 (driver), OpenIPC/firmware#2445 (loader entry)
and OpenIPC/firmware#2449 (supplicant package).

Co-authored-by: nzzane <12163646+nzzane@users.noreply.github.com>
2026-09-19 15:44:14 +03:00
OpenIPC AI teamandGitHub c98adedbeb apfpv: stop writing outgoing.enabled, which majestic no longer has (#158)
`outgoing.enabled` was one switch for the whole list of places a camera
publishes to. Each destination has carried its own since
OpenIPC/majestic#701, and the section switch was removed rather than kept
as a second gate — so this line writes a key nothing reads. majestic
strips it from the tree on load and the next save drops it from the file.

The `.outgoing.server` line below it stays and is untouched: `cli` edits
majestic.yaml directly, majestic converts that address into a row of its
destination list on load, and the camera publishes exactly as before.
There is no way to write a list element through yaml-cli — its path
validator rejects the brackets — so the single-address spelling is the one
this script must keep using.
2026-09-14 12:28:45 +03:00
Signor PellegrinoandGitHub 930a886836 Update Telegram link to point to community page 2026-09-13 23:46:35 +03:00
Dmitry IlyinandGitHub a225f75f6d hi3518ev300: add an RTL8188EU profile with the driver in the kernel, and drop excludes firmware now handles (#157)
Follow-up to OpenIPC/firmware#2410, which left MT7601U as the only in-tree USB
Wi-Fi stack on hi3518ev300. RTL8188EU lands here as a camera profile, splitting
the two adapters across the two repositories.

hi3518ev300_lite_generic-rtl8188eu builds the driver INTO the kernel rather than
shipping it as a module: a profile whose only Wi-Fi is that driver has no reason
to carry it as loadable code, and the rootfs is the partition under pressure.
The out-of-tree rtl8188eus-openipc cannot make that move -- it is an external
kernel-module package, never linked into vmlinux -- so this uses the in-tree
staging driver at CONFIG_R8188EU=y through a variant kernel config, the same
shape as gk7205v200.generic-otg.config.

Built in, the driver also has no modprobe problem: as a module it was
unreachable, because /etc/wireless/usb's rtl8188eu-generic profile modprobes
'8188eu' while the in-tree module is 'r8188eu'. Linked into the kernel it binds
off its own USB device table at boot.

CONFIG_R8188EU selects WIRELESS_EXT, so the wext ABI firmware#2410 dropped comes
back on this variant only. It requests rtlwifi/rtl8188eufw.bin, so
linux-firmware-openipc comes with it. Registered in .github/firmware-drift.json
against the firmware blob it derives from.

MEASURED in CI, against the module-based version of the same profile:
  uImage           [1873KB/2048KB] -> [1991KB/2048KB]   56KB free
  rootfs.squashfs  [5084KB/5120KB] -> [4728KB/5120KB]  392KB free

Also drops excludes entries firmware now handles upstream: r8188eu.ko from
xiaomi-mjsxj02hl, and r8188eu.ko + scsi_mod.ko + sd_mod.ko from
yuancam-dgk423403. Both cameras build green; both run out-of-tree Realtek
drivers over nl80211 and are otherwise unaffected.

Not tested on a camera. This changes which 8188EU driver is used -- mainline
staging rather than the Realtek vendor fork -- so it wants confirmation that a
dongle is detected at boot without a modprobe, associates, and streams.
2026-09-13 16:39:33 +03:00
OpenIPC AI teamandGitHub ba182ac589 excludes: strip two sensors these boards cannot use, and a dead f2fs entry (#155)
sensor_sc3336_mipi.ko, sensor_os03b10_mipi.ko and sc3336.bin arrived in
sigmastar-osdrv-sensors after the ssc333 exclude lists were written, so
meari-speed-6s (JXF37) and babysense-see-hd-ip206 (GC2053) have been shipping
both sensors they cannot use. No board in the README table uses SC3336 or
OS03B10.

Scoped to those two devices on purpose. The other nine infinity6b0 profiles
carry the same two files, but ssc333_lite_tp-link-tapo-c110-v26 has "?" in the
sensor column and stripping a sensor blob from a board whose sensor nobody has
recorded is not a safe guess.

f2fs.ko has not been built since F2FS was switched off upstream
(# CONFIG_F2FS_FS is not set in hi3518ev200.generic.config), so five
hi3518ev200_lite lists have been asking the rootfs script to delete a file that
is never there, and every build prints "1 of 55 entries matched no file" for
it. The warning is how a stale exclude gets noticed, so it should not be
permanently on.

Measured by rebuilding the shipped nightly-20260909-e0a643f images with the
build's own squashfs parameters (the unmodified tree reproduces them
byte-exactly): meari 5108 -> 5092 KB, babysense 5104 -> 5092 KB.
2026-09-12 07:39:49 +03:00
TagBugandGitHub b63df059c3 Add ssc337de_lite_zte-k543 device profile (ZTE K543, SC401AI, MT7601U Wi-Fi) (#152)
The first ssc337de_lite device in the tree to ship an excludes list: it prunes 17 of the 18 sensors OpenIPC/sensors builds for infinity6b0 and keeps sc401ai, the board's own.

Wi-Fi is an MT7601U on the vendor STA driver, so the device carries its own /etc/wireless/usb with the gpio 14 power cycle ahead of the modprobe, and execs modprobe so the profile's exit status is the module's rather than an unconditional success. The overlay is registered in .github/firmware-drift.json against the current firmware blob rather than left to drift silently.

Built green on CI before a rebase over #151 and #153, which collided only on the README device table.
2026-09-11 20:28:56 +03:00
TagBugandGitHub 4a96c82eb4 Add t31_lite_zte-k545 device profile (ZTE K545, SC4336, ATBM6012B Wi-Fi) (#151)
A clone of the in-tree t31_lite_zte-k540 with the K545's own deltas: gpio-motors driven by ptz_control/ptz_gpio rather than the older motors package, majestic IR-cut, backlight and speaker pins, and the same SD-card detect patch (GPIO_PB(18) is pin 50). It reuses K540's atbm603x-t31-zte-k540 wlandev profile, which is already upstream in firmware's shared adapter table.

Built green on CI, narrowed to this device. Two points left open with the author, both inherited from K540 rather than introduced here: the defconfig declares an 8M flash while the README row says NOR_16M, which caps the rootfs partition at 5120KB instead of 8192KB; and the device ships no t31_lite.list, as K540 does not either.
2026-09-11 20:27:47 +03:00
Dmitry IlyinandGitHub 3bd11b95cc Rename hi3518ev300_lite_bathhouse to yuancam-dgk423403 (#153)
The device is a YuanCam-branded (HQLS/hdwificam OEM) HI3518EV300 board —
confirmed on a lab unit via its Factory.bin/SysParam.bin tags
("YuanCamFactory"/"YuanCamSysparam") and its P2P device id
(DGK-423403-YZHGE). Rename the placeholder "Bathhouse" profile to a
proper vendor-model name and fill in the sensor, identified on hardware
with ipctool as JXH62 (previously "?" in the README table). The
defconfig, customizer.sh and excludes list carry over unchanged except
for the upgrade URL, which now points at the renamed release asset.
2026-09-11 20:22:37 +03:00
Signor PellegrinoandGitHub e0a643f494 Fix image links in README.md
Updated image links in README to use raw GitHub URLs.
2026-09-07 20:44:39 +03:00
Dmitry IlyinandGitHub 694749162c Stop shipping a dangling /etc/sensors/iq/default.ini (#150)
The osdrv packages create default.ini as a symlink to one fixed sensor's
IQ file -- imx307.ini for hisilicon-osdrv-hi3516ev200 and
goke-osdrv-gk7205v200. Two device profiles prune that exact file to fit
NOR and leave the symlink behind, so the image ships a default.ini that
resolves to nothing.

majestic answers that silently. init_iq_monitor() (src/hisi/sdk.c) has no
default for isp.iqProfile, falls through to /etc/sensors/iq/default.ini,
and returns on `access(default_prof, 0) != 0` without logging at any
level. No IQ profile means no monitor thread, so SCENE_Set3DNR() never
runs and VPSS keeps the single static NRX block from scene.c tune_vpss()
at every ISO. The explicit-iqProfile path does log "Cannot find %s file";
only this fallback is mute.

Measured on a lab hi3516ev300_85h50ai (IMX335, nightly-20260904-605fa9d)
against a night scene at ISO ~30000, changing nothing but the profile
majestic loads:

                        dangling      imx335.ini
  RTSP, 20 s capture    14.13 Mbit/s  3.54 Mbit/s
  /proc/umap/rc InsBr   14310 kbps    3626 kbps
  StartQp               42 (= MaxQp)  31
  3DNR SFS2             5/16/0/30     36/60/60/52
  3DNR TFS              1/7/12/10     5/9/13/11

The encoder was configured correctly the whole time -- VBR 4096, MinQp
28, MaxQp 42. Rate control pinned at MaxQp while still emitting 14 Mbit/s
is what an undenoised sensor at ISO 30000 costs, not a bitrate that was
ignored. It reads as "the bitrate setting does nothing" and "3DNR is
off", which is how it was reported.

hi3516ev300_lite_xm-85h50ai keeps imx335.ini and runs an IMX335, so
general/overlay now carries a default.ini aimed at it. BR2_ROOTFS_OVERLAY
is rsynced after the packages install and rootfs_script.sh runs the
excludes list after that, so the overlay's symlink replaces the package's
and the pruning that follows is harmless. This also stops the board
loading an IMX307 profile for an IMX335 sensor, which is what it would
have got had imx307.ini simply been kept.

gk7202v300_lite_ipg-g3-wr prunes every IQ file its package installs
(imx307, imx335, sc2232), so there is nothing to aim a symlink at. It
drops default.ini instead. Behaviour is unchanged -- majestic already
loads no profile there -- but the image stops carrying a broken link that
would silently start resolving to the wrong sensor's profile the moment
one of those entries goes stale. The board runs jxh63, which no shipped
IQ file covers; a note in the list records that keeping imx307.ini costs
126 KB if it turns out to want one.

An audit of every device profile against its SoC's osdrv package finds no
others: the rest either keep the symlink's target, remove default.ini
already, or pin .isp.iqProfile in customizer.sh.
2026-09-07 10:25:17 +03:00
OpenIPC AI teamandGitHub 2a26e86e45 gk7205v200_otg: stop the UVC gadget freeing its own video device (#149)
Tearing down a UVC gadget that userspace still has open corrupts the V4L2 core:
the node number leaks (the gadget climbs /dev/video0 -> video1 -> video2 across
role switches), and once the kernel is tainted by the WARN that goes with it, a
NULL dereference follows with v4l2_device_release on the backtrace.

struct uvc_device embeds its struct video_device and uvc_free() kfree()s the
lot, but the V4L2 core holds a reference to that video_device for as long as
/dev/videoN is open. Reference-count the structure instead, which is what the
driver's own "TODO reference counting" has been asking for.

Verified on the bench: the gadget returns to /dev/video0 every time instead of
climbing, and no WARN or Oops after repeated switching.
2026-09-06 14:40:07 +03:00
OpenIPC AI teamandGitHub 0df089bf61 gk7205v200_otg: take the role scripts from firmware's package (#148)
Follow-up to OpenIPC/firmware#2370, which moves `usb-mode`,
`uvc-gadget-setup` and `S72usbmode` into a selectable `usb-dual-role`
package. Nothing about them was device-specific; they were only here because
this is where the OTG image was first put together.

So this profile stops carrying its own copies and selects the package instead.
Keeping both would be worse than untidy: firmware installs them in /usr/sbin
and this profile installed them in /usr/bin, so a build with both would put two
versions of each script on the camera and let PATH order decide which ran --
with the copy here being the older, missing the bus settling, the UDC guard and
the idempotent apply that came with the move.

MERGE AFTER the firmware change, not before: until that lands there is no
package to select, and a build from this branch ships no role scripts at all.

What stays here is what is genuinely particular to this board: the defconfig,
the kernel config, and the dwc3/uvc patches.
2026-09-06 14:38:10 +03:00
OpenIPC AI teamandGitHub f86ea4f4a6 gk7205v200_otg: settle the USB role before xHCI probes (#147)
The OTG profile could not see USB devices in host mode. On a board with a
UVC webcam on the USB tail, `lsusb` showed only the two root hubs, while the
same board running a plain lite image enumerated the webcam normally.

The controller was in the host role and the host controller was running --
mode file "host", xhci-hcd bound, USBCMD 0x5, PORTSC1 port-power set -- and
the port still never reported a connect (PORTSC1 0x00000280, CCS clear).

dr_mode = "otg" makes dwc3_core_init() program GCTL.PRTCAPDIR = OTG(3), and
dwc3_core_init_mode() registers xhci-hcd on top of that. This 4.9 dwc3 has no
working OTG role-detection block, so xHCI probes, resets the controller and
brings up its root hubs while the core is in a role it cannot drive. The port
is then dead for the life of that probe.

S72usbmode's `usb-mode host` did not repair it and could not: writing the
debugfs mode file reaches dwc3_set_mode(), which flips GCTL.PRTCAPDIR and
nothing else, leaving the already-probed xHCI with the port state machine it
built in OTG. It also skipped the bind, because xhci-hcd was already bound.

Fix it where the ordering is: patch dwc3_core_init() to program
PRTCAPDIR = HOST for the OTG case, so xHCI probes into a role the core can
drive. dwc3_gadget_init() only registers a UDC and does not care, and
`usb-mode device` re-aims the controller itself when it takes the port.

usb-mode's host path also learns to re-probe an xHCI that was bound in some
other role, which is what makes a newer rootfs recover on an older kernel.
2026-09-06 09:57:08 +03:00
Dmitry IlyinandGitHub c2f5024972 ci: check the setting paths shipped scripts hand to cli (#146)
* ci: check the setting paths shipped scripts hand to cli

`cli -s` cannot fail. yaml-cli stores whatever dotted path it is given,
creating intermediate mappings as it goes, and exits 0; majestic then silently
ignores a key it does not recognise. So a typo in a setting path applies
nothing and reports nothing, from the moment it is written to the end of the
device's life.

t40_lite_movols-mo-805p has been shipping six of them:

    cli -s .video0.bitrate: 4000
    cli -s .video0.rcMode: avbr

The path is `.video0.bitrate:`, not `.video0.bitrate`. Bitrate, rate-control
mode, profile, GOP size, GOP mode and OSD size have never applied on that
camera. The trailing colons are dropped here, including the one on the
commented-out `.onvif.enabled:`, which was a trap for whoever uncommented it.

The linter checks shape and nothing else. Whether a well-formed key is one a
given majestic build actually declares can only be answered by the running
binary -- it varies by vendor and by flavour -- while shape is decidable from
the tree, costs nothing, and is the whole of the class that shipped. A path
built at runtime is skipped rather than guessed at: uvc-gadget-setup reads
`cli -g ".$1"`, which is fine and unknowable here.

It carries a floor check for the same reason lint-workflow-shell.py does. A
matcher that quietly stops matching finds no bad paths, which looks exactly
like a clean tree -- so finding no cli invocation at all is itself a failure.

80 scripts scanned, 610 invocations, 6 bad paths, all in the one device.

* ci: recheck the merged result when the cli linter itself changes

lint.yml's push trigger names the checkers it guards one by one, and the new
one was not on the list -- so a master push touching only
.github/scripts/lint-cli-paths.py would have skipped the very job that exists
because #121 shipped a broken merged result nothing re-checked.

Same reasoning as the lint-workflow-shell.py entry beside it, and the same
cost: seconds, no runners.
2026-09-05 16:31:10 +03:00
Dmitry IlyinandGitHub 605fa9dd97 hi3516ev300_lite_xm-85h50ai: ship the majestic-af autofocus plugin (#145)
Select BR2_PACKAGE_MAJESTIC_AF in the device defconfig so the test board's firmware carries /usr/lib/majestic-af.so out of the box. The package is defined in the firmware external tree (OpenIPC/firmware#2357, merged first); builder.sh clones firmware master, and the device build compiles the plugin from source (7caebdc6) and installs it — verified end-to-end in CI after the firmware merge.
2026-09-03 09:30:29 +03:00
Signor PellegrinoandGitHub a16e77a2f1 Update gk7205v200_otg_generic_defconfig (#144) 2026-09-02 15:24:59 +03:00
Signor PellegrinoandGitHub 60cbe0cd1d Add customizer.sh for IP camera settings (#143)
This script sets a custom upgrade URL for the IP camera and reboots the device to apply changes.
2026-09-02 15:14:40 +03:00
OpenIPC AI teamandGitHub bac7c697b2 xm-85h50ai: turn on majestic's autofocus out of the box (#142)
The first camera with working autofocus on HiSilicon: the customizer
enables majestic's contrast AF engine (widgetii/majestic#522), which
reads the ISP's BE AF zone statistics and drives this board's XM zoom
block over the same UART the pad uses, serialised by the same lock.
The engine defaults to off everywhere — a lens without a focus motor
must not grow an /autofocus endpoint — so enabling it in the profile
of the board it was proven on is the whole change; port and speed
defaults already match. The WebUI grows an AF button and books a
settle-gated pass after each zoom step (majestic-webui#265).

Verified end to end on the lab unit: from total blur to a tack-sharp
frame, fv 30 -> 5126 against a 5606 peak in 21s.
2026-08-31 17:37:24 +03:00
OpenIPC AI teamandGitHub 3921b342f3 Add hi3516ev300_lite_xm-85h50ai device profile (XiongMai, motorized zoom+focus) (#141)
XiongMai's 85H50AI: hi3516ev300 + imx335 with a motorized zoom/focus
module whose MCU shares /dev/ttyAMA0 with the serial console at 115200,
speaking the XM near-Pelco protocol (majestic-webui's btzoom-xm).
Proven on a lab unit driven end to end through the WebUI
(majestic-webui#227).

The defconfig is a byte-copy of firmware's hi3516ev300_lite_defconfig.
customizer.sh (first boot) sets sensor imx335, the upgrade URL, and the
PTZ trio — ptz_control pelco-xm, ptz_port /dev/ttyAMA0, ptz_caps
'zoom focus' (majestic-webui#261) — and deletes the getty line from
/etc/inittab: the MCU owns the console UART. The edit lands in the
overlay and heals itself (an upgrade that wipes the overlay wipes
/etc/custom.ok too, so the script re-runs). S01printk quiets runtime
kernel messages that would drip onto the PTZ wire. The required
hi3516ev300_lite excludes list prunes every non-imx335 sensor blob,
generated from the live lite image so no entry is stale on arrival.
2026-08-31 16:00:28 +03:00
OpenIPC AI teamandGitHub b2267404b9 customizer: gpio_motors becomes ptz_gpio, with ptz_control naming the method (#140)
Completes the #227 PTZ env rename (majestic-webui#232, firmware#2341) in
the last place still writing the old name: the three customizer.sh files
carrying 'fw_setenv gpio_motors ...' now write ptz_gpio instead. The
gpio-motors utility reads ptz_gpio first and falls back to gpio_motors
since firmware#2341, and it is the only consumer of the variable anywhere
in the org, so the rename is mechanical.

Each device also gains 'fw_setenv ptz_control gpio' — not part of the
rename, but required by it: the WebUI dropped its auto-detection from a
bare pin list (unset ptz_control now means no PTZ, per flyrouter's ruling
on the same issue; majestic-webui#255). Without the method named, these
devices would keep their motors but lose their pads. In
t23_lite_jooan-a6m-u the pin line was commented out, so both new lines
are commented there too.
2026-08-31 14:29:27 +03:00
Dmitry IlyinandGitHub 4f640b335b ssc377qe fpv: drop the prebuilt imx335 module (#139)
This overlay carried a 25KB sensor_imx335_mipi.ko because the driver OpenIPC
built at the time could not reach the rates this board needs: its 2560x1920@60
mode served 30fps and its 90fps mode produced no usable output at all. The blob
shadowed the built module and fixed it for this one device, and for nobody else.

The source behind it is now what OpenIPC builds. openipc/sensors carries it as
sigmastar/infinity6c/sensor_imx335_mipi.c instead of a symlink to the infinity6e
driver (OpenIPC/sensors#3), and the infinity6c loader states the lane count it
needs (OpenIPC/firmware#2343). sigmastar-osdrv-sensors is unpinned, and this
device's defconfig already selects it -- BR2_PACKAGE_SIGMASTAR_OSDRV_INFINITY6C
selects BR2_PACKAGE_SIGMASTAR_OSDRV_SENSORS -- so the module is built and
installed to /lib/modules/5.10.61/sigmastar either way. The overlay copy was
only ever shadowing it.

Removing it means this device tracks the driver instead of freezing a copy of
it, which is the point: the next person who needs a mode changed can change it.

The built driver is a later revision than the blob (72ad30b against the
30fdac4 it came from) and measures the same or better on an SSC377QE, rebooting
before every mode:

  2592x1944@59    59.2 fps   (blob 59.2)
  2560x1920@60    60.1 fps   (blob 58.4)
  2208x1248@90    89.5 fps   (blob 89.5)
  1920x1080@120  116.1 fps   (blob 119.6)

Verified end to end on hardware afterwards: with the built module and the
merged loader and nothing else, the loader applies lane_num=4 on its own and a
60fps request delivers 60.0.

Nothing else in the overlay changes.
2026-08-31 10:25:51 +03:00
OpenIPC AI teamandGitHub 2b619b9e76 publish with an explicit release credential, and prove it before building (#138)
* publish with an explicit release credential, and prove it before building

This repo was next in line for the failure OpenIPC/firmware hit on
2026-08-29. Moving the nightly off `schedule:` onto a dispatch changes
the identity the run executes as, and the default GITHUB_TOKEN's
release-write goes with it: a scheduled run executes as the repository's
scheduling identity, a dispatched one as whoever dispatched it, and for
an app/OAuth-backed identity the releases API answers 403 "Resource not
accessible by integration".

It does so even though the job declares `contents: write` and the run
log confirms the token was granted it, which is why nothing in the
workflow file looks wrong. firmware's run 33273333359 built 102 boards
over two hours before finding out. This repo builds 107 devices and
publishes the same way -- `gh release create` plus a PATCH of
refs/tags/latest -- so it would have failed identically the first time
the train reached it. It never got that far only because the train
correctly refused to advance past firmware's failure.

The publish step takes RELEASE_TOKEN instead of the default token, and
preflight proves that credential can create a release before the matrix
is allowed to spend hours on it, by creating a draft release and
deleting it: invisible, no tag, two API calls.

Asserting the secret is merely non-empty would not have caught the
firmware incident. The token that failed existed and was simply refused,
so the probe has to attempt the write.

* probe: survive re-runs and clean up after itself

Review caught a real defect in the credential probe. It deleted its
draft with a single fail-fast call, so an interrupted or cancelled job
left the draft behind -- and because github.run_id is stable across
re-runs, that leftover would collide with its own re-run and report a
perfectly good credential as broken. A guard that fails wrongly is worse
than no guard, because it blocks good runs and teaches people to ignore
it.

Three changes. The probe tag now carries run_attempt as well as run_id,
so a re-run can never collide with its own leftovers. Deletion moved
into a trap, so a cancelled job still cleans up. And a best-effort sweep
removes probe drafts stranded by earlier runs, because cleanup.yml does
not know this tag pattern and they would otherwise accumulate forever.

The sweep is capped at the 10 most recent releases and swallows every
error. That cap is not arbitrary: listing 100 releases in these repos
returns HTTP 504 because each carries hundreds of assets, measured at
~2s for 10 and ~7s for 30. Housekeeping must never be the thing that
fails a build.

Verified end to end against the live repo with a real token: creates the
draft, reports "release credential OK", the trap removes it, no tag is
created and no published release is touched. Both failure paths still
fail loudly -- unset token, and a credential the API refuses.

* probe: retry the write, and do not narrate garbage while sweeping

Review caught that the probe treated any one-shot POST failure as a bad
credential. GitHub answers 403 for secondary rate limiting as well as
for refusal -- the publish step retries six times for exactly that
reason -- and these repos return 504 on ordinary release listings often
enough that one was hit while writing this change. A blip would have
failed the nightly with "the token is wrong", sending whoever read it
off to reissue a credential that was never the problem.

The POST now retries three times with 5s and 10s backoff, and the error
says "after 3 attempts ... if this is not a transient API error". Three
rather than the publish step`s six because the value here is failing
fast: a genuine misconfiguration still surfaces in well under a minute
instead of after the whole matrix.

Testing the retry then exposed a second defect, in the sweep added by
the previous commit. gh writes its error body to STDOUT, so a failed
list feeds the cleanup loop lines of JSON instead of release ids, and
the log filled with `removing stranded probe draft {"status": "401"}`.
The deletes were harmless -- they simply failed -- but a step whose
entire purpose is to be believed must not narrate confident nonsense.
Only numeric ids are acted on now.

Verified against the live repo: happy path creates the draft, reports
OK, the trap removes it, nothing is left behind; a refused credential
retries three times and fails with the honest message; a failing list
produces zero bogus removal lines.
2026-08-30 11:00:05 +03:00
OpenIPC AI teamandGitHub 96fc55698c Take the nightly off GitHub's scheduler (#137)
This is the last stage of a three-stage release train — widgetii/majestic
publishes the majestic tarball, OpenIPC/firmware builds the images, this builds
from that tree. Each stage was its own GitHub cron sized against a guess at the
previous stage's runtime: 17:25, 22:30 and 03:00 UTC.

From 2026-08-26 GitHub began delivering scheduled events hours late and
independently per repo, and this repository was the worst affected of the three
— +10h38m and +11h49m on successive nights, a 03:00 slot sitting in the deepest
part of the congestion band. On the 08-28 cycle that made this stage run FIRST,
at 14:49, ahead of both the repositories it is supposed to follow. It went
green, as it always would: no stage checks that its inputs are newer than the
ones it used last time.

The delay is not ours and not fixable here: for every late run
`created_at == run_started_at` to the second, so all of it sits upstream of run
creation, inside GitHub's dispatcher. Moving the cron off the top of the hour
and pushing a commit to resync the schedule were both tried on the majestic
side; neither changed anything.

The ordering moves to the host that runs majestic's self-hosted runners, which
dispatches each stage in turn and waits for it to succeed before starting the
next.

Unlike majestic and firmware this needed no gate change, and the preflight
comment now says why: those two skip when nothing has changed and keyed that
skip on the event name, which a dispatch would have made unreachable. This one
deliberately always builds, so there was no event-name condition to go stale.

The clone guard rail keeps its logic and loses half its argument: an inherited
workflow can no longer run nightly and unattended on a stranger's Actions bill,
but it can still do so on every internal PR.
2026-08-29 23:09:15 +03:00
Ben NeigherandGitHub b676643864 Add gk7202v300_lite_generic-w7 device profile (GK-W7, GC2053-DVP, SSV6006C Wi-Fi) (#119)
GK-W7 board (product string ipc533331a-W7-gc2053dvp-f8): GK7202V300, 8MB NOR,
GC2053 wired in DVP/parallel mode with the SID strap high, iComm/SSV SSV6006C USB
WiFi (8065:6000), no Ethernet PHY.

Differs from the generic gk7202v300_lite target in two ways it cannot express
without a profile. The sensor is on the DVP pads, so open_sys_config's MIPI
arguments mux i2c to pads nothing is connected to and no sensor is ever detected;
customizer.sh opts into the sensor_dvp / sensor_mclk gate added in
OpenIPC/firmware#2276. And the Lite target ships only mt7601u, which will never
bind an SSV part, so the defconfig selects BR2_PACKAGE_SSV635X_OPENIPC and the
overlay adds the ssv6x5x-generic wlandev branch.

Named generic-w7 rather than after a vendor: the board carries no identifiable
vendor marking, and "gk" is Goke -- the SoC vendor, already encoded in the soc
field -- not the board's. `generic` is the established token here
(gk7205v200_otg_generic, ssc30kq_rubyfpv_generic).

Verified: builds green, rootfs 5017600B with 220KB of headroom in the 5120KB
partition; the baked load_goke is master's verbatim (md5 confirmed after
normalising the build's comment stripping); customizer.sh carries both env vars
and is sh/dash/ash clean; S30customizer runs it on first boot ahead of S70vendor,
so the vars are set before load_goke reads them; and on hardware, master's
load_goke driven only by those two vars brings up the DVP GC2053 at 1920x1080
@ 25 fps with i2c init failures 0 and ISP errors 0.

Not verified: the conjunction of the above on a single unattended first boot from
this image with no hand-set environment. Each link is independently evidenced;
the composition is not. Follow-up expected on the PR.

Headroom note for later: 220KB of rootfs headroom is about 4% of the partition,
and this family has a history of profiles going over as majestic grows.
gk7202v300_lite.list is the lever.

Refs: OpenIPC/firmware#2074, OpenIPC/firmware#2276
2026-08-29 07:58:06 +03:00
Ben NeigherandGitHub 15d004912b builder.sh: propagate the build failure instead of always exiting 0 (#136)
Verified rather than taken on description.

`BUILD_RC=$?` is the line immediately after `make BOARD=${DEVICE}`, so nothing
clobbers the status before it is captured, and `exit ${BUILD_RC}` uses the saved
value rather than re-reading `$?` after `echo_c`. `sh -n`, `dash -n` and
`bash -n` all clean. The branch point (`222db9e`) carries a byte-identical
`builder.sh` to master -- md5 `2292222e081fecf876e022c373a943e5` at both -- so
the diff is exactly these twenty lines. 114 checks green.

Agreed on explicit status propagation over `set -e`: the script does enough
unguarded `cp`/`rm`/`cd` that global errexit would change failure behaviour well
beyond this line, and `size-report` keeping its `|| true` is right.

Worth stating plainly for whoever reads this later: this makes the six-step
backoff in `master.yml` live for the first time. A deterministically failing
board -- a rootfs that no longer fits NOR, say -- will now be rebuilt seven times
across roughly 38 minutes of sleeps before the job goes red, where previously it
went green instantly. That is the correct behaviour and the point of the change,
but it is a real shift in what a broken nightly costs, and if it proves
expensive the fix is to make the retry discriminate transient failures from
deterministic ones, not to go back to a script that cannot fail.
2026-08-29 07:15:19 +03:00
Dmitry IlyinandGitHub 832c2afa1a ci: let a pull request keep the image it just built (#135)
Verified on this PR's own run rather than merged on the reasoning.

Across all 15 devices in the matrix:

```
Stage artifacts          15 success
Upload build artifacts   15 success
Send binary              15 skipped
Publish releases            skipped
CI Gate                     success
```

15 `fw-*` artifacts produced, and the retention the fix was about is what
actually landed:

```
fw-hi3516ev300_ultimate_rvi-1ncmw2028
  created  2026-08-27T21:17:00Z
  expires  2026-09-03T21:16:58Z   -> 7 days
```

So a pull request now keeps the images it built, for a week, while the two
paths that must stay closed to a PR -- the Telegram secret and shared release
assets -- are still closed.
2026-08-28 00:18:18 +03:00
moontwisterandGitHub 2ca0796b58 tplink-kasa-kc110: ship the vendor OV2735 libsns and select ultimate majestic (#108)
Two changes for the TP-Link Kasa KC110, both device-local.

BR2_OPENIPC_MAJESTIC="ultimate" brings the defconfig in line with the only other devices
that set it, hi3516ev300_ultimate_rostelecom-ipc8232swc-we and
hi3516ev300_ultimate_rvi-1ncmw2028.

The overlay ships a vendor libsns_ov2735.so, which fixes the no-video symptom on this
board. hisilicon-osdrv-hi3516cv200 already installs a libsns_ov2735.so of its own, so
this overrides it rather than filling a gap; the two are different builds (23076 bytes
here against 17988 upstream), consistent with the KC110 needing the MIPI variant while
the osdrv ships the DVP/i2c one. That matches the ov2735_mipi_1080p.ini and the patched
load_hisilicon this device already carried.

Verified in CI against master (run 33106540798, merge dc5c2fc):

    ci-matrix: 1/110 devices (needs_build=True) --- narrowed to the affected devices
    Firmware (hi3518ev200_ultimate_tplink-kasa-kc110)   SUCCESS
    - uImage:          [1776KB/2048KB]
    - rootfs.squashfs: [6276KB/8192KB]

No entry in hi3518ev200_ultimate.list reported "matched no file", so the prune list is
not stale against the overlay, and /usr/lib/sensors/libsns_ov2735.so is not one of the
paths it strips -- the blob reaches the image.

This device only started building at all in #132, which took it out of the NOT_BUILT
opt-out in ci-matrix.py; the CI-registration half of this PR landed there.
2026-08-27 22:28:22 +03:00
OpenIPC AI teamandGitHub 9df259cb6f ci: build the KC110 and the Tapo TC70 v3 instead of skipping them (#132)
Both devices have had a complete devices/<dir>/ in this tree for a long time and had
never once been built. #120 froze that state rather than changing it -- it put the 7
defconfigs that were in no master.yml matrix into NOT_BUILT so that a device falling out
of CI would be a test failure instead of a silent skip. Freezing was right for a change
whose job was to not alter what gets built; it was never meant to be permanent for
devices whose only problem is that nobody added the matrix line.

Two contributors did try to add that line. #111 (Tapo TC70 v3, July) and #108 (KC110,
June) both append to the device list in master.yml, which is where the registry lived
when they were written. It does not live there any more -- the matrix is read off the
tree -- so both PRs are merge conflicts now, and the devices they asked for are exactly
the two entries removed here. This is their CI half, landed the way the selector wants
it. #108 also carries a BR2_OPENIPC_MAJESTIC="ultimate" line and a vendor
libsns_ov2735.so for the no-video bug; those are unaffected and stay with that PR.

The two self-test cases that used t31_lite_tp-link-tapo-tc70-v3 as their example of an
unbuilt device move to t31_lite_xiaomi-mjsxj05hl, which is still in NOT_BUILT. The
overlay case also switches to a path that exists, since the device it now names has no
etc/inittab.

Neither device had ever been through a build, so this PR was where we found out. That
was affordable exactly once: a change to ci-matrix.py cannot narrow -- it is what does
the narrowing -- so this ran the full matrix and proved both in the same run. Both are
green:

    Firmware (hi3518ev200_ultimate_tplink-kasa-kc110)   SUCCESS
    Firmware (t31_lite_tp-link-tapo-tc70-v3)            SUCCESS
2026-08-27 21:24:20 +03:00