ba182ac58963b24ec93cf022666c1795ea64610e
584
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
e0a643f494 |
Fix image links in README.md
Updated image links in README to use raw GitHub URLs. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
a16e77a2f1 | Update gk7205v200_otg_generic_defconfig (#144) | ||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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 |
||
|
|
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.
|
||
|
|
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. |
||
|
|
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.
|
||
|
|
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 |
||
|
|
5e2d58f644 |
fpv: give the 8812eu its own gk7205v200 build (#133)
#61 asks for 8812eu support on gk7205v200 FPV. #70 and the first version of this PR both tried to satisfy it by adding BR2_PACKAGE_RTL88X2EU_OPENIPC=y next to the existing BR2_PACKAGE_RTL8812AU_OPENIPC=y. CI built that and it does not fit: - uImage: [1988KB/2048KB] - rootfs.squashfs: [5504KB/5120KB] -- size exceeded by: 384KB Two heavy kernel Wi-Fi drivers do not go in one 8M image. The driver itself is fine -- 8812eu.ko links and installs -- it is purely flash budget, and there is no 384KB of slack in a 5MB rootfs that was already at its cap. The tree had already said so. Every defconfig here that ships both drivers is 16M: the ssc338q, ssc30kq, ssc377qe and ssc378qe fpv/apfpv/rubyfpv targets. Every 8M one ships 8812au alone: gk7205v200/v210/v300 fpv and venc, hi3516ev200_fpv, hi3516ev300_fpv. The "missing" 8812eu on the Goke configs was never an oversight; it is what 8M allows. So this is a separate build. gk7205v200_fpv_8812eu is gk7205v200_fpv with one line changed, the driver swapped rather than added: -BR2_PACKAGE_RTL8812AU_OPENIPC=y +BR2_PACKAGE_RTL88X2EU_OPENIPC=y The existing gk7205v200_fpv is untouched, so nobody on an 8812au adapter is affected. It builds with room to spare -- rootfs 5024KB/5120KB, 96KB headroom. Nothing else had to move for it: - Registration is automatic; the defconfig is the registration. --self-test passes at 108 devices and the diff narrows to exactly this target, so its own build proved it. - Excludes resolve as ${OPENIPC_SOC_MODEL}_${OPENIPC_VARIANT}.list in firmware's rootfs_script.sh, and VARIANT stays "fpv", so it reuses gk7205v200_fpv.list. That list strips neither driver. - Release assets do not collide. master.yml keys on the underscore count: gk7205v200_fpv is COMMON=1 and uploads the canonical openipc.gk7205v200-nor-fpv.tgz, this one is COMMON=2 and is renamed to gk7205v200_fpv_8812eu-nor.tgz. Same for its sizes.json. Only gk7205v200, which is what #61 asked for. The other four 8M FPV targets have the same constraint and could take the same treatment if anyone asks. Closes #70. |
||
|
|
82ab2e7a32 |
docs: stop telling contributors to register a device in master.yml (#134)
Step 6 of "Adding a new device" still said to add `- <device>` to the matrix in .github/workflows/master.yml, and called that matrix "the *only* build registry". Neither has been true since #120 moved the matrix into .github/scripts/ci-matrix.py, where it is derived from the tree rather than listed. master.yml has no device list at all. Not a hypothetical staleness: #111 (Tapo TC70 v3) and #108 (KC110) each append a line to a block that no longer exists, so both are merge conflicts rather than the one-line changes they were written as. Their authors did the documented thing. So step 6 is now the opposite instruction -- there is nothing to add, and adding it is the mistake. It also names NOT_BUILT, since that is the one list a contributor could reasonably go looking for, and says to leave it alone. Two adjacent claims went stale with the same change: the master.yml bullet described a cron-and-dispatch workflow with no pull_request trigger, and nothing mentioned ci-matrix.py at all -- including that --self-test runs in the Select devices job and will fail a PR that puts the tree and the selector out of step. A second correction, caught by Qodo in review: "opening the PR is what gets the device built" holds only for a non-draft PR. classify() returns needs_build=False for a draft and self_test() asserts it; master.yml lists ready_for_review in its triggers precisely because a PR opened as a draft would otherwise never build once marked ready. Left as written, a contributor could open a draft, see no build, and read that as the device being fine. The ci:full label is documented for the same reason -- it is the other contributor-facing lever over the matrix. |
||
|
|
6fef62c0f3 |
ci: watch for drift from firmware instead of finding it by accident (#129)
* ci: watch for drift from firmware instead of finding it by accident builder.sh copies devices/<item>/* over a fresh firmware clone, so a device directory that ships its own copy of a firmware file replaces it outright. A fleet-wide change in firmware reaches every device that only references a path and none that ships its own copy, silently, because nothing in either repository is looking. One comment on OpenIPC/firmware#2308 turned up three instances of that in an afternoon: 13 board configs that missed the CONFIG_VT sweep (#126), 97 defconfigs that kept BR2_PACKAGE_JSONFILTER after firmware retired it and put three devices over their rootfs cap (#128), and excludes lists naming files that no longer exist (firmware#2313 reports those at build time). The trigger is the design. Every one of those was caused by a commit in firmware while this repository sat untouched, so a pull_request check would never have fired for any of them -- nobody opens a builder PR when firmware changes. firmware-drift.yml runs on a schedule and files an issue rather than failing: drift someone else introduced is not a reason to redden a nightly that is otherwise fine. Only the self-test gates a merge here, out of lint.yml. Two checks, because the two kinds of drift are not the same shape. Shadowed files: comparing content is useless, a builder copy is *supposed* to differ. What matters is whether firmware's version moved since a human last reconciled them, so each of the 13 entries pins the blob it was reconciled against. Re-pinning is the act of looking again. The mapping is hand-authored -- nothing can infer that gk7205v200.generic-fpv derives from gk7205v200.generic. Defconfig symbols: nothing here copies a firmware defconfig, so pinning does not apply. Every BR2_PACKAGE_*=y is resolved against buildroot, firmware and this tree and sorted into "resolves nowhere" (a dead line kconfig ignores) and "resolves, but no firmware defconfig selects it" (needs an allowlist entry). An allowlist entry can also fence a symbol to the devices that need it; without that, writing JSONFILTER down as builder-only for devices/apfpv would equally bless it creeping back onto 95 unrelated defconfigs, which is #128 verbatim. Replayed against the three regressions, each reproduced by hand on a clean tree: firmware moves a shadowed config -> br-ext-chip-goke/board/gk7205v200/gk7205v200.generic.config moved in firmware since this copy was reconciled (2026-08-25). builder copy: devices/common/.../gk7205v200.generic-fpv.config pinned 7450c6803f6d, firmware now f97dce84c0f8 firmware commits since: 85233f0e kernel: drop CONFIG_VT and the busybox applets ... (#2308) jsonfilter creeps back onto a non-apfpv device -> BR2_PACKAGE_JSONFILTER is allowlisted only for apfpv/*/configs/*_defconfig, but 1 other defconfig(s) select it. firmware retires a symbol builder still selects -> BR2_PACKAGE_LIBUBOX is selected here but by no firmware defconfig. Resolving against buildroot is not optional and getting it wrong is how a checker lies. Buildroot is vendored by neither repository, so a first pass over OpenIPC packages alone called HOSTAPD, IW, PHP, UHTTPD, LIBZIP and BWM_NG dead; all six are upstream. Matching only "Config.in" then called PHP_EXT_ZIP dead, because buildroot declares php's extensions in package/php/Config.ext. Without --buildroot that half is skipped and says so rather than inventing findings. Two symbols really do resolve nowhere today -- BR2_PACKAGE_APFPV_GREG on the four greg-generic devices and BR2_PACKAGE_WIFIBROADCAST_EXT in devices/common. Both are recorded as known-dead and reported as a notice on every run rather than removed: whether those devices still want the packages is a maintainer's call, not a side effect of adding a check. The three new paths are classified in ci-matrix.py so this PR and every future re-pin cost no device builds. Without that a blob update is a 107-device run. * ci: discover shadowed files instead of listing them by hand Qodo caught the shadow list omitting the device-local t21 and t31 kernel configs. It was right, and the hole was much bigger than the five it named: a hand-written list of 13 board configs against 93 same-path shadows covering 35 firmware paths. The missing ones are not incidental -- load_goke, load_hisilicon, load_sigmastar and four vendor .mk files are all shadowed by some device, and those are exactly the shared files a fleet-wide fix lands in. firmware regression-tests load_hisilicon's os_mem_size derivation; two devices here ship their own copy of that script and would not have seen the fix. Listing them by hand was the wrong shape. builder.sh copies devices/<item>/* over the firmware clone, so any file at a path firmware also has replaces it -- which makes same-path shadows discoverable rather than something to remember. discover_shadows() walks devices/ and reports anything sitting on a firmware path with no entry in firmware-drift.json, so the list cannot silently rot and a device added tomorrow cannot quietly introduce an unreconciled shadow. The manifest goes from 13 entries to 101: the 88 discovered ones, plus the 13 hand-authored entries that discovery cannot find because the name differs from the file they replace -- gk7205v200.generic-fpv shadows gk7205v200.generic, and nothing can infer that. Two self-test cases for the new path: a same-path shadow with no entry is reported, and an entry silences it. Verified against the tree by dropping the load_hisilicon entries, which surfaces both devices immediately. |
||
|
|
a0418c2987 |
kernel: drop CONFIG_VT from the device configs that shadow firmware's (#126)
builder.sh copies devices/<item>/* over the firmware checkout before make, so a device directory that carries its own board .config replaces firmware's copy outright. OpenIPC/firmware #2308 and #2309 turned CONFIG_VT and CONFIG_INPUT off across the fleet; the 13 board configs here were never touched by that, so 21 device builds carried on shipping the virtual-terminal layer -- /dev/tty0, /dev/tty1..63, the keyboard translation tables, console selection and screen code -- for a monitor and keyboard no camera has. flyrouter pointed this gap out on #2308. The transformation is not re-derived here. For each file the symbol-level delta was taken straight out of firmware's history (85233f0e^..HEAD) for the config it shadows and replayed, so these land on exactly the state that was reviewed and proven on hardware there. Every affected symbol in every copy still held firmware's pre-sweep value, so nothing here was a deliberate customisation -- they were simply stale. Eleven of the thirteen also needed CONFIG_EXPERT=y first. In drivers/tty/Kconfig the symbol reads config VT bool "Virtual terminal" if EXPERT select INPUT default y so its prompt exists only under EXPERT, and without it olddefconfig finds the symbol invisible and forces it back to its default -- "# CONFIG_VT is not set" on its own does nothing. Two exceptions: * gk7102c keeps CONFIG_INPUT. It sets CONFIG_INPUT_EVDEV=y and firmware's gk710x, which it shadows, keeps INPUT for the same reason. Only VT moves. * CONFIG_INPUT_LEDS=m is dropped on the six FPV/OTG copies. It is the input-to-LED bridge, lives inside `if INPUT`, and firmware never carried it, so olddefconfig would have dropped it silently -- stated here so the file matches the kernel it generates. CONFIG_LEDS_CLASS is a separate symbol in drivers/leds and is untouched: the FPV status LEDs are unaffected. Nothing consumes VT on any of these. FRAMEBUFFER_CONSOLE is its only in-kernel consumer and no config in this repository sets it at =y or =m, so the sole console driver compiled in was DUMMY_CONSOLE and writes to /dev/tty0 went nowhere. The ingenic t21/t31/t40 copies already matched firmware -- VT off, INPUT kept for their GPIO keys -- and are left alone. Verified by generating the kernel config the way builder.sh does: copy devices/common over a firmware checkout at master and run the configure step, before and after. gk7205v200_fpv, comparing the two generated build/linux-custom/.config files symbol by symbol: turned OFF (8) CONSOLE_TRANSLATIONS DUMMY_CONSOLE HW_CONSOLE INPUT INPUT_LEDS VT VT_CONSOLE VT_CONSOLE_SLEEP turned ON (1) EXPERT value changed none LEDS_CLASS=m, TTY, UNIX98_PTYS, SERIAL_AMBA_PL011_CONSOLE, USB_GADGET, CFG80211, MAC80211, KALLSYMS, COREDUMP, NAMESPACES, ELF_CORE, CORE_DUMP_DEFAULT_ELF_HEADERS, DEBUG_KERNEL, DEBUG_MEMORY_INIT: unchanged. That last line is the check #2308 missed on its first pass: EXPERT carries `select DEBUG_KERNEL` and unhides a block of on-by-default options, which there silently moved COREDUMP and friends. DEBUG_KERNEL was already y in these configs, so nothing else moved. Building the kernel both ways in that same tree, on the OpenIPC/linux goke tarball these devices use: gk7205v200_fpv uImage 2071368 -> 2036040 -35328 bytes (-1.71%) zImage 2058872 -> 2023544 -35328 bytes (-1.72%) which lines up with the -34680 bytes firmware measured on gk7205v200_lite for the same delta. drivers/input/input-leds.ko is built in the before tree and is not built after. |
||
|
|
7bfcab91ff |
ci: stop a NOT_BUILT device widening the matrix to all of them (#127)
NOT_BUILT is documented as the opt-out that narrows to nothing, and a change to
one of those devices' defconfigs does exactly that. Anything else in the same
directory did the opposite: targets_in() only ever returns devices CI builds, so
a directory holding nothing but opted-out devices came back empty and fell
through to the "is in no directory CI builds" widen. Touching a device CI
deliberately skips cost a full 107-device run.
$ echo devices/gk7205v200_rubyfpv_generic/br-ext-chip-goke/board/\
gk7205v200/gk7205v200.generic-fpv.config | python3 .github/scripts/ci-matrix.py --stdin
ci-matrix: 107/107 devices (needs_build=True) --- ... is in no directory CI builds
$ echo devices/gk7205v200_rubyfpv_generic/br-ext-chip-goke/configs/\
gk7205v200_rubyfpv_generic_defconfig | python3 .github/scripts/ci-matrix.py --stdin
ci-matrix: 0/107 devices (needs_build=False) --- nothing that reaches a build
Same device, same opt-out, opposite answers. #126 touches three NOT_BUILT
devices and is building all 107 because of it.
The empty result was carrying two meanings: "every device here is opted out" and
"this directory is not one I know". Only the second should widen. devices_in()
answers the second question -- it reads directory_of, which is built from every
defconfig in the tree rather than the built subset -- so an unrecognised
directory still widens, which is the half of the rule worth keeping.
Three self-test cases, one per meaning: a kernel config and an overlay file in a
NOT_BUILT device reach nothing, and a devices/ directory with no defconfig at
all still widens. With this, #126's own file list narrows from 107 to 23.
|
||
|
|
a5882d5e80 |
package: stop selecting jsonfilter, mirroring firmware #2304 (#128)
OpenIPC/firmware dropped BR2_PACKAGE_JSONFILTER from every one of its defconfigs in #2304, once the WebUI stopped calling it. builder's defconfigs are its own files, so 97 of them kept selecting it. jsonfilter selects JSON_C and LIBUBOX, and on a device that enables neither uqmi-openipc nor wifibroadcast-ng nothing else selects libubox, so it and jsonfilter shipped for nothing. That is what has had three devices over their rootfs cap: hi3518ev200_lite_switcam-hs303 rootfs 5124KB/5120KB -4KB ssc333_lite_babysense-see-hd-ip206 rootfs 5124KB/5120KB -4KB ssc333_lite_meari-speed-6s rootfs 5128KB/5120KB -8KB all three red on the nightly, meari since 2026-08-24. Bisecting meari across firmware revisions lands on #2301, which added 408 bytes to the comment-stripped sysupgrade script -- these images had no headroom left at all, and 408 bytes of a genuine bug fix was enough to push one over a 4KB padding step. The fix is to give them headroom, not to give back the bug fix. Measured on ssc333_lite_meari-speed-6s, built the way builder.sh builds it -- devices/<item>/ copied over a firmware checkout at master, clean tree both times: before rootfs.squashfs 5128KB/5120KB size exceeded by: 8KB after rootfs.squashfs 5096KB/5120KB 24KB spare -32KB, from /usr/bin/jsonfilter (18068 raw) and /usr/lib/libubox.so (30404 raw). json-c does NOT go: majestic lists it in MAJESTIC_DEPENDENCIES, so buildroot builds and installs it whether or not BR2_PACKAGE_JSON_C is set. Only the two files above leave the image. Two consumers genuinely need jsonfilter and both keep it: * devices/apfpv's own etc/init.d/S99msposd reads majestic's config.json through it, and ssc338q_apfpv and ssc378qe_apfpv do not enable wifibroadcast-ng. Those two keep the symbol, with the reason written above the line -- the same undeclared-runtime-dependency trap firmware #2305 had to fix, and the reason this is 95 defconfigs and not 97. * every other FPV device enables wifibroadcast-ng, whose Config.in selects BR2_PACKAGE_JSONFILTER (added by #2305) for the same script. They keep it without saying so. LTE devices keep libubox through uqmi-openipc, which selects it independently. Nothing else in this repository calls jsonfilter: grepping devices/ and package/ finds only that one S99msposd line, and firmware's own tree has no caller left outside wifibroadcast-ng. |
||
|
|
6fcd657a00 |
ci: publish the devices that did build when the matrix goes red (#125)
* ci: publish the devices that did build when the matrix goes red This morning's nightly built 97 of 98 devices. ssc333_lite_meari-speed-6s failed, and that one device cost every device its release: there is no nightly-20260824-* release, and `nightly` and `latest` still carry the 23rd's images. 2026-08-21 went the same way on two failures out of 98 -- no nightly-20260821-* either. The publish job is documented as best-effort -- "publish whatever the matrix produced even if some devices failed" -- and its condition already lists 'failure' as an accepted matrix result. It never got that far. A job whose `needs` failed is skipped by default, and an `if:` overrides that only when it CONTAINS a status function; reading needs.buildroot.result inside contains() does not count. So the job was skipped outright, never evaluated, and reported "skipped" rather than anything red. This has been true since #121 consolidated the release writes into a single writer to stop the concurrent-upload rate limits. That change was right -- before it each device uploaded from its own matrix job, so a failing device took down only itself. Both nightlies that have failed since then lost their whole release this way. `!cancelled()` rather than `always()`: the contains() already drops a cancelled or skipped matrix, but a run cancelled after a green matrix would still start uploading under always(), and a half-written nightly is what the single-writer design exists to avoid. The gate stays red on a partial matrix; that is unchanged. What changes is that a red run now still delivers the images it built. Devices that failed keep their previous image on nightly/latest, which the ci-gate already assumed; the new warning in Collect assets says so on the run rather than leaving it to be inferred. Same fix as OpenIPC/firmware#2303 -- the two publish jobs share a common ancestor and carried the identical gate. Verification: master.yml parses; lint-workflow-shell.py checks 23 run blocks clean (and its own self-test passes); ci-matrix.py --self-test ok. Publish condition audited against every path -- PR, no-op schedule, clone guard, cancelled run, failed select and all-devices-failed all still skip; only "matrix failed, some devices built" changes from skip to publish. * ci: finish the sentence in the partial-publish warning The line ended on "for every device that did not", leaving the reader to supply "build" from the clause before it. It is an operator-facing log line on the one run where somebody is trying to work out what shipped, so say it in full and point at where the failed devices are listed. builder has no Build summary job, so the run's own job list is that place. Raised by Qodo on #125. |
||
|
|
fc476c895d |
ci: only lint real steps, and find the real end of an expression (#124)
Two bugs in the linter #122 added, found by Qodo reviewing the port of it to OpenIPC/firmware#2290 and confirmed against this copy rather than taken on trust. walk() collected any mapping with a scalar `run` key, anywhere in the document. That is one key name away from linting things that are not shell: an action input, an env var or a matrix field called `run` holds arbitrary text, and feeding it to bash -n fails a workflow that is fine. On a fixture with one real step plus an `env: run:` and a `with: run:`, this copy collects three blocks. Steps now have to come out of a `steps:` sequence, which is the actual schema invariant and still not a hardcoded jobs.*.steps[*] path, so composite actions (`runs: steps:`) keep working. The expression substitution used `\$\{\{.*?\}\}`, which stops at the first `}}` even when it is inside a string literal. On x=${{ fromJSON('{"a": {"b": 1}}') }} this copy produces `x=__GHA_EXPR__') }}` and bash -n then reports an unbalanced quote -- a false failure on a valid block, in the direction that trains people to ignore the job. Replaced with a scanner that tracks GitHub's single-quoted strings, including the doubled '' escape, and returns the text untouched if an expression is never closed. Both are latent here: nothing in this repo has a `run` key outside a step or a `}}` inside an expression string, and the block count is unchanged at 23. They are fixed because the failure mode is a red job with nothing wrong with the tree, which is how a check stops being believed. Self-tests for both. The docstring described the behaviour this replaces, so it is corrected too, and the now-unused `re` import drops. Keeps this copy identical to firmware's apart from the incident paragraph and the block floor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6872639ed8 |
ci: stop the lint job's apt step hanging for six minutes (#123)
The unconditional `apt-get update` added to satisfy review on #122 was fine on the PR runs (~5s) and then sat for six minutes on the first master push, still in progress when it was cancelled. Nothing was wrong with the tree; apt was just slow. That is a bad trade for this job in particular. Its argument for existing -- and for carrying a push trigger at all -- is that it answers in seconds, and it was made to depend on a network fetch it does not normally need. ubuntu-latest ships PyYAML. So import first and install only if that fails. The dependency is still handled rather than assumed, which was the point of the review finding, but a working runner pays nothing for it. `if` rather than `python3 -c 'import yaml' && exit 0`, because under `bash -e` the latter fails the step on the branch where the import fails -- exactly when the install needs to run. Both paths checked under -e. Also timeout-minutes: 10. The default is six hours, which is how a step that hangs rather than fails occupies a runner and tells nobody. The whole job now completes in three seconds, and selects 0 devices rather than 107 because #122 classified lint.yml as no-build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0455db0027 |
ci: close the if that cost last night's release, and parse-check the rest (#122)
The Collect guard added to the publish job in #121 shipped without its closing `fi`. Run 32096242353 -- the first nightly to reach the new single publish job -- built all 107 devices, staged 219 assets, and then died in `bash -e` with line 25: syntax error: unexpected end of file from `if' command on line 14 before writing anything. No nightly-20260818-* release exists, `nightly` and `latest` still carry the 17th's images, and ci-gate went red. The gate did its job; there was simply nothing left to publish by then. Nothing caught it earlier because the publish job carries `if: github.event_name != 'pull_request'` -- there are no artifacts to publish on a PR, so the entire path is unreachable from PR CI by construction. Both of #121's runs were green without ever executing the step. #121 said as much ("none of this path can run on a pull request") and checked the four states offline, but offline-checking the logic does not check that the script parses, and that is what broke. So the one-line fix, plus the check that would have caught it in seconds without a runner: lint-workflow-shell.py parses every `run:` block in .github/workflows/ with `bash -n`, and lint.yml runs it on PRs and on master pushes that touch either. Notes on the checker: - ${{ }} is not shell, so each expression is replaced with a plain word first. That is a real limitation -- an expression interpolating shell syntax is checked as the word, not as what it expands to -- and the substitution preserves line counts so reported lines still point at the right line. --self-test asserts an unterminated `if` containing an expression is still rejected while the closed form passes, because the way this breaks is by making everything pass. - Steps are found by walking for any mapping with a `run:` key rather than by the jobs.*.steps[*] path, so nothing depends on guessing where they live, and a discovery floor fails the run if the walk stops finding them. A checker that silently checks nothing looks exactly like a clean tree. - Syntax only. It says nothing about quoting or `-e` semantics; a shellcheck pass wants per-block disables for the substitution above and is a separate change. actionlint would cover more and is worth considering later; keeping this narrow is what makes it noise-free now. ci-matrix.py classifies both new files as no-build. They were names it had never heard of, and unknown widens by design, so the first push queued all 107 devices to prove a one-line YAML fix -- #121, which touched only master.yml, ran 19. This PR still took the full matrix because it edits ci-matrix.py itself, which always widens; the selector is not trusted to pick a smaller matrix for its own changes. PyYAML is installed explicitly rather than inherited from the runner image, and the workflow declares `contents: read` rather than taking the repo default. Both from review; neither would have failed the job today. Verified: the checker flags the merged master.yml with the same message the runner gave, passes the fixed tree, and all five guard states behave as #121 intended -- normal night publishes, a broken download and a green-but-empty dist both fail, and a dead matrix with nothing built still exits clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5701c6710a |
ci: write releases from one paced job, not from 107 at once (#121)
* ci: write releases from one paced job, not from 107 at once Every matrix job uploaded its images to three shared releases with softprops/action-gh-release, so a nightly fired on the order of 400 concurrent asset writes at `nightly`, `latest` and the dated tag. OpenIPC/firmware ran exactly this design and the releases API answered HTTP 500 under the contention -- run 27108181857 built every board and still went red on 14 jobs, all of them in their upload step. Firmware then hit the sequel: consolidating into one job but keeping concurrent uploads tripped the per-actor secondary rate limit at 350 of 393 assets. This lands where that ended up, without the two failures in between. Matrix jobs now stage what they produced into dist/ and hand it to actions/upload-artifact. A single publish job downloads all of it and drives the release writes with gh: one asset at a time, paced ~1/s under the per-minute mutation ceiling, with exponential backoff so a transient 403/429 is retried rather than failing the nightly. The single-writer property is what fixes the 500s; the pacing is what keeps the fix from tripping the other limit. The dated release keeps the full asset set, because enrich_manifest.py only ever reads dated releases. `nightly` and `latest` are delivery aliases for flashers and now get images only: re-uploading the size sidecars to two more tags is pure rate-limit cost, and nothing reads them from those tags. Staging copies whichever of the two naming schemes the build used -- compound devices rename to <device>-<flash>.tgz in the workspace root, single-underscore ones leave openipc.<soc>-<flash>-<variant>.tgz under openipc/output/images/ -- so asset names are byte-identical to today. Checked both against enrich_manifest.py's COMPOUND_RE and SIMPLE_RE. ci-gate covers publish, so a failed release write fails the run instead of passing quietly. The staging loop uses `if` rather than a trailing `&&` chain: the step runs under `bash -e`, where a chain that tests false is only harmless because something after it happens to reset the status. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: stop the publish job succeeding without publishing Two High findings on #121, both real. The download step carried continue-on-error: true, so a transient or real artifact-download failure produced an empty dist/, the publish step's count guard skipped it, the job went green, and ci-gate accepts publish=success. A nightly could silently write nothing while the run stayed green. The flag stays -- it exists so a matrix where every device failed, and so uploaded no fw-* artifact at all, does not hard-fail before the gate can report why -- but Collect now turns it back into a failure for every other case: if the matrix went green, a download that did not succeed is an error, and zero collected assets is an error. Only "matrix failed and produced nothing" is still tolerated, and the gate fails that run on the matrix result anyway. The second finding said count could read " 0" because wc pads. On GNU coreutils, which is what the runner has, it does not -- that is BSD behaviour, and `printf '' | wc -l` here gives an unpadded 0. Normalised anyway, since it costs a tr and stops the guard depending on which wc is in front of it. Chasing it did surface a worse hole it sits next to. dist/ can hold sidecars and no images -- one device that produced a size report and no .tgz is enough -- and count is then 1, so publish ran, force-moved the nightly and latest tag refs, and uploaded zero images. The two tags users flash from would point at a build with no firmware behind them. Both tags now move only when there is at least one image to move them to; otherwise they stay on yesterday's, with a warning. Checked all four states offline, since none of this path can run on a pull request: green matrix with a broken download, green matrix with an empty dist, failed matrix with nothing produced, and a normal night. The first two now fail and used to pass. Note the same continue-on-error and unnormalised wc are in OpenIPC/firmware's publish job, which is where this one came from. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
edc0a0f88f |
ci: build pull requests, and only the devices they reach (#120)
* ci: build pull requests, and only the devices they reach This repo had no pull_request trigger at all. A change landed unbuilt, and the nightly found out the next morning -- or a user did. Turning PR CI on meant 107 device builds per push, which is why it stayed off. Almost every change here is one device: 639 of the file touches in the last 200 commits sit under a single devices/<dir>/, and the median commit reaches 2 devices. So narrowing first is what makes PR CI affordable at all. .github/scripts/ci-matrix.py maps the paths a PR touches to the devices that build them, and master.yml takes its matrix from it. The mapping is not written down, it is read off the tree the same way builder.sh reads it. builder.sh line 121 locates a device by its defconfig and copies that WHOLE devices/<dir>/ tree over the firmware clone, so the devices a file affects are exactly the devices whose defconfig shares its directory. That distinction is load-bearing: devices/common/ backs 18 targets and devices/apfpv/ backs 2, and treating a directory as one device would skip 17 real builds. A defconfig still names one target even inside a shared directory. THE NIGHTLY IS NOT NARROWED. builder.sh re-clones OpenIPC/firmware at HEAD on every run, so what a nightly builds is decided mostly outside this repo; master.yml already carries the scar of an earlier gate that skipped on this repo's HEAD and left upstream fixes invisible to users. Every event except pull_request gets the whole matrix, and --self-test asserts that for schedule, workflow_dispatch and push. Nothing on a pull_request may write a release. The three upload steps were gated only on an image existing, so with a pull_request trigger added they would have clobbered the published nightly and latest assets that users flash. They and the Telegram post are now guarded on the event. master.yml and check-adjacent plumbing get a 15-device smoke set rather than all 107: they decide how a build runs, not what it produces. --self-test enforces the cover -- every vendor, architecture, toolchain tuple and variant, plus both shared device directories -- rather than the list, so the set can be re-picked while the property holds. It already earned itself: the first run rejected my hand-picked set for missing arm-openipc-linux-gnueabi, which exactly one target in the tree has, and musleabihf. Also adds the CI Gate umbrella check, since a dynamic matrix cannot be named in branch protection and nothing reported a verdict before; and freezes the 7 defconfigs that exist but are in no matrix, so a device falling out of CI is a test failure rather than a silent skip. Replaying the last 200 commits: 20330 device-builds if every PR built everything, 5930 with the selector, 70.8% less, no coverage leaks. 43 of the 49 that still widen do so on paths that no longer exist (devices/ssc338q_apfpv/ before it became devices/apfpv/, the old top-level common/, a deleted builder-local.sh); restricted to commits whose paths survive, it is 78.2%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: derive the device list instead of writing it down Adding a camera IS the work in this repo: 8 of the 10 PRs open right now add a device, and 14% of recent commits do. With the list written down in ci-matrix.py, every one of those PRs also had to edit that file -- and a change to that file cannot narrow, because it is what does the narrowing. So registering one camera cost a full 107-device build to prove one device. The most common change got the worst outcome. The matrix is now every defconfig under devices/, minus a NOT_BUILT opt-out that keeps the 7 already in the tree but out of CI. Adding a device is creating its directory, nothing else, and it builds exactly that device. This is the opposite call to OpenIPC/firmware's ci-matrix.py, on purpose. There the matrix is curated -- 29 defconfigs are deliberately never built and boards are not the product -- so an explicit list is the honest description and an opt-out list would be noise. Here devices are the product and the list is just "all of them". The derived matrix is identical to the 107 master.yml carried: nothing lost, nothing gained. --self-test now checks the opt-outs still name real defconfigs, so a rename cannot leave a device silently building under its new name while its old name sits in NOT_BUILT. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci-matrix: own $GITHUB_OUTPUT instead of being redirected into it The docstring said the default mode writes $GITHUB_OUTPUT; it printed key=value lines to stdout and left the workflow to redirect them there. Raised in review on #120. Fixing the prose would have made the file honest, but the redirect itself is the weaker half. Under `>> ${GITHUB_OUTPUT}` every print() in this file is one keystroke away from corrupting the step outputs, and a crash between the first line and the last leaves a half-written file that Actions still reads. That the diagnostics go to stderr is what keeps it safe today -- a rule that has to hold forever, rather than a property of the code. So the script appends the three lines itself when $GITHUB_OUTPUT is set, and prints them when it is not, so a local run still shows its decision. --stdin never touches the file: it is the mode for reading with your eyes. master.yml drops the redirect, though leaving one in place would have been harmless -- with the file written directly, stdout is empty. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
222db9e291 |
ci: keep inherited workflows from billing non-canonical repos (#118)
A clone of this repo pushed to a new repository -- a private mirror, an internal re-host -- inherits .github/workflows together with its cron. The full platform matrix then runs on that owner's Actions bill, nightly and unattended. Forks are only partially covered: GitHub disables scheduled workflows there, but nothing covers a fresh repository, and nothing warns the owner. This mirrors OpenIPC/firmware#2255, where the same inherited cron was measured at 97 jobs and 2,146 billed minutes from a single unattended night, and never converged: publishing failed on the mirror, so the sha skip-gate could never engage and the matrix rebuilt in full every night. The rule: unattended runs happen only on the canonical repo. Anywhere else a workflow runs only if someone explicitly asked for it (workflow_dispatch), or if it is both free and attended (pull_request on a public repo). This is deliberately stricter than gating purely on cost -- a public re-host's minutes are free and its cron is still skipped, because a re-host quietly publishing nightly releases under its own name is a worse outcome than wasted minutes. master carries the guard on preflight; buildroot needs it and inherits the skip. manifest gates its cron-originated workflow_run path the same way, because a nightly whose jobs all skipped still completes and fires workflow_run. cleanup carries it on its weekly cron. build-one is dispatch-only and needs nothing. The pull_request clause is unreachable here today -- no workflow in this repo has a pull_request trigger -- and is kept on purpose: it is the clause that lets contributor PRs run, and omitting it would mean adding a pull_request trigger later silently skips CI on every fork. That over-block was the first thing reviewers caught upstream. Upstream impact is none: on OpenIPC/builder the first disjunct is true, so every added condition is a tautology and each job's if reduces to what it was before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
94cce13948 |
gk7205v200: flip dwc3 to host before unbinding xhci (otg) (#114)
Booting the otg profile with /etc/usbmode = device made S72usbmode's
`usb-mode device` log, on every single boot:
xhci-hcd xhci-hcd.0.auto: Host not halted after 16000 microseconds.
xhci-hcd xhci-hcd.0.auto: Host controller not halted, aborting reset.
dwc3_core_init() leaves GCTL.PRTCAPDIR = OTG(3) and this 4.9 dwc3 never
moves off OTG on its own. xhci-hcd binds and registers both buses on top
of that anyway, but it is not actually driving the port -- so unbinding
straight from OTG asks xhci_halt() to halt a host that was never really
running, and it times out.
The gadget composed regardless, so this was cosmetic in practice, but it
left the host controller un-halted: exactly the live-DMA-engine state the
unbind is there to avoid.
Flip the controller to host before unbinding, so the already-bound xHCI
is genuinely running and the unbind halts cleanly. A switch from host is
already in that state, so only boot pays for the extra flip.
Measured on gk7205v200 (Xiongmai IPC_GK7205V200_50H20AI_S38): 5/5 cold
boots warned before, 0/5 after. Runtime host<->device round trips stay
clean and still take ~0.35s, with no reboot and no majestic restart.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
96633d5577 |
gk7205v200: add an OTG profile that makes the camera a USB webcam (#113)
Adds gk7205v200_otg_generic, a firmware profile that puts the single DWC3 controller into dual-role so the board can either consume a USB webcam (existing usbcam path, host role) or present itself to a PC as one (UVC gadget, device role). The roles are mutually exclusive -- xHCI HCSPARAMS1 reports MaxPorts = 1 -- but usb-mode switches between them at runtime, so one image covers both and no reboot is needed. Kernel side, three patches: 0015 sets dr_mode = "otg" in the DTS so dwc3 registers both an xHCI host and a UDC. 0016 makes device mode actually work on a part that has only ever run as a host. Three fixes: re-run dwc3_event_buffers_setup() on gadget start (an xHCI reset wipes GEVNTADR), soft-reset the core before taking the lock so the PHY is re-initialised (without it high-speed chirp fails), and swallow the run_stop timeout on pullup(0), which otherwise permanently poisons cdev->deactivations. 0017 disables the vendor's UVC_SG_REQ path. It hands a whole frame to the UDC as one scatter-gather request; dwc3 chains those sg entries into a single isochronous service interval, which on a full-speed link is one 1023-byte packet, so every frame larger than that reached the host truncated and H.264 decoders reported "bytestream -5" on roughly half the frames. Goke never shipped this path enabled -- CONFIG_USB_CONFIGFS_F_UVC is unset in every vendor defconfig, including gk7605v100's, and the SDK carries no userspace UVC sample -- so it was never exercised. The mainline path (32 plain one-packet requests) is used instead. Userspace side: usb-mode switches roles with the unbind/flip/bind sequencing dwc3_set_mode() does not do on its own, uvc-gadget-setup composes the configfs gadget, and S72usbmode forces a concrete role at boot -- not optional, since dr_mode = "otg" leaves GCTL.PRTCAPDIR in a state this 4.9 dwc3 never moves off by itself. The descriptors advertise exactly the one format, frame size and interval majestic is configured to serve, read from /etc/majestic.yaml. majestic answers every PROBE/COMMIT with its own bFormatIndex/bFrameIndex because it has one stream and cannot follow the host, so anything extra is an invitation to select something that will not arrive: asking for MJPEG got raw H.264 (an SPS where a JPEG SOI belongs, garbage to every consumer), and asking for 1080p got 720p. Profile drops the air-unit packages (wifibroadcast, rtl8812au, mavfwd, mavlink-router, vtund, linux-firmware, wireless-tools) and the whole wireless kernel stack, which pays for usb_f_uvc.ko and for keeping the full sensor list -- the fpv trim to IMX307/IMX335 is keyed on OPENIPC_VARIANT="fpv", and a general-purpose USB webcam should not be sensor-locked. Measured on a gk7205v200 lab board over a full-speed link: H.264 1280x720 at 30 fps with zero decoder errors and no dropped buffers, and MJPEG delivering valid JPEGs at the jpeg channel's own rate. Requires a majestic built with ENABLE_UVC_GADGET to feed the gadget; that is a separate change. |
||
|
|
6c0a45e66d |
gk7205v200: enable USB (UVC) webcam kernel support (fpv) (#112)
Enable the media / V4L2 / uvcvideo kernel stack so a USB camera exposes /dev/videoN for majestic's usbcam second-camera path. To fit the 8M NOR (2M kernel / 5M rootfs) without dropping any sensor library, uvcvideo and the videobuf2 framework are built INTO the kernel (=y) instead of shipped as rootfs modules: the kernel partition had ~55 KB of spare room while the rootfs was tight, so ~44 KB of weight moves from rootfs to kernel. Resulting images: uImage 2022.8 KB / 2048 KB (25 KB spare), rootfs squashfs 5080 KB / 5120 KB (40 KB spare). GSPCA -- a separate driver for non-UVC webcams, pulled in by "default m depends on VIDEO_V4L2" -- is disabled. No kernel patch is carried: mmap capture works on stock uvcvideo once majestic aligns its mmap() prototype with this MMAP_MUSL build's uint32_t-offset shim (a userspace fix), so the earlier read()/fileio uvcvideo patch is not needed. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9d85e993fb |
ci: evict majestic's moving S3 tarball from the dl cache too (#109)
PR #105 added a "Refresh moving-ref package downloads" step that drops *-{HEAD,master,main,dist}.tar.gz before each build so buildroot re-fetches the current ref. That fixed the majestic-webui-dist.tar.gz staleness (the *-dist.tar.gz case), but it never matched majestic itself. majestic downloads from S3 as majestic.<family>.<variant>.master.tar.bz2 (majestic.mk) — a DOT separator, .tar.bz2, and an S3 host the old pattern deliberately spared as "content-addressed". But that S3 object is a moving `master` build, re-uploaded on every majestic CI run, with no .hash to force a refetch. So it stayed frozen in the monthly builder-dl cache while the webui refreshed — the exact desync that shipped a pre-x-groups majestic in a t31 lite image against the current webui, blanking the settings page with "No settings groups in schema." Broaden the eviction to a regex covering both -ref and .ref separators across gz/bz2/xz, so majestic.*.master.tar.bz2 and majestic-webui-dist.tar.gz are both dropped while semver/SHA-pinned tarballs keep their cache. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
edb7251bf3 |
devices: add CP Plus CP-UNC-TA21L2C (gk7205v200_lite, SC223A) (#106)
Dahua-OEM 2 MP camera. The SC223A is on hardware I2C0 routed to the VI-RAW pads (0x112c0060/0x64 func2) rather than the default mipi-sensor pads (0x30/0x34 func1) the SDK probes, and needs a 27 MHz MCLK (0x120100f0=0x19; SDK default 0x11/div4 leaves it unresponsive). S71devmem re-muxes the pads, selects the SENSOR_RSTN reset (GPIO8_7 func2) and fixes MCLK after S70vendor/load_goke; the stock open_sensor_i2c then reaches the sensor over /dev/i2c-0. No kernel module needed. Verified on hardware: chip-id 0xcb3e over HW I2C0, MIPI lock 1920x1080 zero errors, majestic streams correct colour 1080p. Co-authored-by: build <build@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d6a1c32761 |
docs: add CLAUDE.md for Claude Code (#107)
Document the builder overlay model (per-device deltas layered onto a fresh OpenIPC/firmware clone), the build flow in builder.sh, device directory anatomy, the package/ and devices/common/ conventions, the CI workflows (including the moving-ref cache caveat), and a step-by-step "Adding a new device" walkthrough. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
97356e0cfe |
ci: re-fetch moving-ref package downloads each build (#105)
builder caches BR2_DL_DIR (/tmp/builder-dl) under a monthly key (date +%m), and
actions/cache only saves on a key miss, so the first build of the month freezes it for
weeks. Packages pinned to a moving ref (VERSION = HEAD/branch, or the majestic-webui
`dist` release asset) download as a constant <pkg>-<ref>.tar.gz, so buildroot keeps
reusing the stale tarball — defeating the clean firmware-HEAD re-clone builder does for
freshness.
Empirically, a 2026-06-14 builder nightly shipped a ~2-week-old majestic-webui (full
bootstrap.min.css, no mj-settings.js) while the firmware nightly of the same day shipped
the current slim UI. Drop *-{HEAD,master,main,dist}.tar.gz before each build (master.yml
+ build-one.yml) so buildroot re-fetches the current ref. Mirrors the same fix in
OpenIPC/firmware.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
e6d8822517 |
Add TP-Link Kasa KC110 (HI3518EV200 + OV2735) (#99)
* Add TP-Link Kasa KC110 (HI3518EV200 + OV2735)
New device variant: hi3518ev200_ultimate_tplink-kasa-kc110.
Contents:
- kc110-board-support package: PTZ binary (mmap'd PL061), gpio-motors
shim for the openipc web GUI, IR control script, and a boot-time
pinmux init that writes 0x1 to the chip7/chip8 pads (0x200f00e8/ec/
f0/fc/100/104). The OpenIPC kernel pinctrl driver does not auto-set
these for the KC110's pan motor / IR-cut / IR-LED pads — the vendor
firmware writes them explicitly in hn_config.sh. Without this,
PL061 GPIODATA writes succeed silently but the pads never drive
the ULN2803 / solenoid / LED.
- Device overlay:
* etc/sensors/ov2735_i2c_1080p.ini — input_mode=0 (MIPI), 1-lane
(was input_mode=4 / 4-lane in upstream cv200 osdrv, which is
wrong for OV2735).
* usr/bin/load_hisilicon — adds an "ov2735" case that mirrors
ov2710_mipi (same MIPI pinmux + clock).
* usr/share/openipc/customizer.sh — first-boot configuration:
RTL8188FU wlandev, sensor config path, IR-cut pin 64, backlight
pin 63, lightMonitor true (AE recovery after IR-cut toggle).
- README.md row.
Hardware notes:
- SoC: Hi3518EV200; Sensor: OV2735 (MIPI 1-lane, RAW10);
Wi-Fi: RTL8188FU (USB); Flash: W25Q128 16 MB NOR.
- PTZ: 4-phase unipolar steppers via ULN2803 darlington array.
Pan = vendor base-10 GPIO 81/74/73/72 = OpenIPC PL061 65/60/59/58;
tilt = 0/1/2/44 = 0/1/2/36 (vendor's "44" is bank 4 pin 4 — i.e.
pin 36 in OpenIPC's PL061 numbering, NOT 44).
- IR-cut filter: GPIO 64 (vendor 80) active-low; IR-LED: GPIO 63
(vendor 77).
Status: PTZ + GPIO + IR fully functional. Video pipeline currently
needs an LD_PRELOAD shim around HI_MIPI_SET_DEV_ATTR because upstream
majestic builds combo_dev_attr_t with enIntfMode=DC / raw_data_type=0
for MIPI sensors that need RAW10. The shim is downstream-only pending
a real fix to majestic upstream; this PR delivers the rest of the
KC110 board support and will produce a fully-functional camera once
the majestic fix lands.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* tplink-kasa-kc110: use ov2735_mipi_1080p.ini
Follow OpenIPC/firmware #2155, which ships the OV2735 MIPI config as a
separate ov2735_mipi_1080p.ini and leaves ov2735_i2c_1080p.ini (the
parallel/DVP config) untouched.
- Rename the device's bundled sensor .ini to ov2735_mipi_1080p.ini and
carry the full corrected MIPI config: input_mode=0 (MIPI),
data_type=2 (RAW10), lane_id 2-lane, Input_mod=5 (MIPI),
Mask_0=0xFFF00000. The previous bundled copy only had input_mode
fixed (data_type/Input_mod/Mask_0 were still the wrong DVP defaults).
- Point customizer.sh's isp.sensorConfig at the new file.
- Exclude the unused parallel ov2735_i2c_1080p.ini from the image.
Verified on a TP-Link Kasa KC110 (OV2735 2-lane MIPI RAW10): clean
image, correct AE, RTSP H.264 1080p.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: moontwister <moontwister@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
21de4f7b92 |
ci: drop sizes.json URLs from manifest.json (CORS-blocked in browsers) (#103)
Mirror of OpenIPC/firmware#2169. PR #102 extended enrich_manifest.py with SIZES_COMPOUND_RE + SIZES_SIMPLE_RE + parse_sizes() so the public manifest carried `platforms[<plat>].sizes.url` and `@sizes` lines pointing at each release's sizes.json asset. The intent was a browser-side consumer; the GitHub release-asset URLs return no Access-Control-Allow-Origin header, so every browser fetch was blocked by CORS. This commit reverts only the manifest-indexing half of #102. The release-asset upload itself stays in master.yml — server-side consumers can still read the JSON via `gh release download` or any CI workflow. Only the in-manifest URL is removed, because that URL only made sense for an in-browser reader that turns out can't reach it. The resulting enrich_manifest.py is bit-for-bit identical to the pre-#102 state. Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8f67509025 | Strip down some profiles | ||
|
|
17672a0d99 | Update goke-osdrv-gk7205v200.mk | ||
|
|
2fc4bb3737 | [no ci] Update profiles gk7202v300_lite_xg521 gk7205v210_lite_vixand-ivg-g4f-a-w | ||
|
|
da13271f94 |
ci: mirror firmware's size-report sidecar (OpenIPC/firmware#2166) (#102)
Firmware PR #2166 added a `size-report` Makefile target + the `general/scripts/size_report.py` analyser that emits a per-package / per-kernel-module `sizes.<soc>-<variant>.json` next to the .tgz. Builder clones firmware fresh on every run and shells to `make BOARD=...`, so the script + target are already available downstream — this PR adds the CI wiring and manifest extension so the JSON ships as a release asset and surfaces through https://openipc.github.io/builder/manifest.json. Three changes: * `builder.sh`: best-effort `make size-report` after the main build, and copy `sizes.*.json` into the timestamped archive next to `.tgz` / `rootfs.squashfs.*`. Failure is swallowed so a legacy `OPENIPC_FW_REV` pin to a pre-#2166 firmware ref can't sink the build. * `.github/workflows/master.yml`: new `Build size report` step after the existing `Build firmware`. Mirrors the bimodal rename trick the .tgz path already does — compound matrix entries (`<soc>_<variant>_<vendor>-<model>`) share `<soc>_<variant>` and would otherwise collide on the firmware-side `sizes.<soc>-<variant>.json` filename when uploaded to the same release tag. Compound entries get the JSON renamed to `sizes.<NAME>.json`; simple entries keep `sizes.<soc>-<variant>.json`. `${{env.SIZES}}` is added to all three upload `files:` blocks (dated / nightly / latest). * `.github/scripts/enrich_manifest.py`: `SIZES_COMPOUND_RE` + `SIZES_SIMPLE_RE`, a `parse_sizes()` helper that tries compound first (because `[^._-]+` in the simple soc class would otherwise accept the compound form too), the sidecar is recorded at the platform level under a `"sizes"` key in `manifest.json`, and a new `@sizes <build_id> <platform> <size> <url>` line type is emitted in `manifest.flat` alongside the existing `@channel` directive. Schema is unchanged for existing consumers — on-device sysupgrade reads positional 4-column `manifest.flat` lines and stays inert on unknown `@`-prefixed directives. Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |