commit e0bc4785e9011e4197d12e813d0fcd87c6f4d8b3 Author: b0hemia <50309975+b0hemia@users.noreply.github.com> Date: Wed Aug 26 15:26:14 2026 +1000 diskOS installer: initial public beta Flashes the diskOS custom UI onto the FiiO Snowsky Disc over Ingenic mask-ROM USB, building the image from your own stock firmware. Runs from source via install.sh. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..74a6fc0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +ko_fi: b0hemia +# github: [b0hemia] # uncomment once GitHub Sponsors is enabled on the account diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68b6257 --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# === diskOS installer - publication .gitignore === +# Excludes secrets, FiiO-derived artifacts, build outputs, and release binaries. +# The public repo ships SOURCE; large/built artifacts go on GitHub Releases. +# NOTE: gitignore has NO inline comments - every comment is on its own line. + +# --- SECRETS - never publish (private ECC update-signing key lives here) --- +signing/ +*.pem +*.key + +# --- FiiO-derived images (contain FiiO rootfs - never redistribute) --- +diskos_dev*.bin +diskos_public*.bin +diskos_v209_recovery.bin +*_recovery.bin +*.squashfs + +# --- retired unlicensed blob --- +_retired_unlicensed/ + +# --- build outputs / venvs / caches --- +build/venv/ +build/work/ +build/dist/ +.venv/ +__pycache__/ +*.pyc +*.pyo + +# --- prebuilt native binaries (Release artifacts / rebuilt by build/ scripts) --- +# source is in src/usbboot/ ; static builds via build/build-*-static.sh +vendor/*/usbboot +vendor/*/mksquashfs +vendor/*/unsquashfs +vendor/*/*.bak +vendor/*/lib/ +flash/usbboot + +# --- top-level duplicates of payload/* (keep only payload/) --- +/mq_ui +/S97diskos_install +/S99usbserial + +# --- deprecated/unsafe legacy shell installer (superseded by the Python installer; never publish) --- +# Kept on disk for reference but excluded from the public repo AND the release tarball. +INSTALL.md +mkdiskos.sh +flash/flash_diskos.sh +flash/extract_stock_rootfs.sh + +# --- internal process / release-engineering docs (not for the public repo) --- +PUBLICATION_PLAN.md +BETA_CHECKLIST.md +RELEASE.md +RELEASE_READINESS.md +UPDATE2_DRAFT.md +UPDATE_ARCHITECTURE.md +INSTALL_v209_legacy.md + +# --- source-tree checksums are fragile; release checksums are generated at staging --- +SHA256SUMS + +# --- unsupported developer diagnostics (hardcoded paths / no clean public build) --- +flash/qual_lpddr3.sh +flash/scan_check.sh +flash/my_write5_scan_dram.bin + +# --- extra build/release scratch --- +build/release/ +*.sqfs + +# NOTE: the SPL corresponding-source tarball (spl-src/*.tar.gz) is deliberately NOT ignored - +# it is the GPL corresponding source and must ship with the repo. + +# --- scratch / OS noise --- +*.log +*.tmp +.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d87f026 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Contributing to diskOS + +Thanks for wanting to help. diskOS touches real hardware in a way that can brick a device, so a few +rules keep contributions safe and legally clean. + +## Ground rules + +1. **Hardware-affecting changes must be tested on real hardware.** Anything that changes the image + builder, the boot hook, the NAND writer, the SPL, or device targeting has to be flashed and + booted on an actual Snowsky Disc before it is declared working. Say which firmware version and + which DRAM/NAND variant you tested on. +2. **Never weaken the fail-closed contract.** The first-boot hook must always fall back to the stock + UI when it cannot verify the UI against the baked manifest. The flasher must fail closed rather + than commit an unverified or wrong-device write. +3. **Add an error code for every new failure mode** (`E1xx` preflight, `E2xx` build, `E3xx` flash, + `F1xx` device-writer) and document it in `README.md`. +4. **Keep the honest-limitations tone.** If something is blocked by hardware or a toolchain gap, say + so plainly. Do not imply a feature works when it has not been verified. + +## Licensing and provenance + +- Original diskOS files are MIT. By contributing you agree your contribution is under the same + license. Please sign off your commits (`git commit -s`, Developer Certificate of Origin). +- **Do not upload FiiO firmware, stock rootfs images, or generated `diskos_*.bin` images** to issues + or PRs - they contain FiiO's software. The installer builds images locally from a user's own + firmware; that is fine, redistributing them is not. +- If you touch how a GPL/LGPL component is bundled (usbboot, squashfs-tools, the SPL, libusb), + keep its corresponding source and `NOTICE.md` in sync. +- Do not commit device-identifying data (serials, MAC addresses) or secrets. + +## Filing issues + +Include: firmware version + hash, DRAM/NAND variant if known, host OS, the exact error code, the +bad-block count if the flasher printed one, and redacted logs. **Do not** post serial numbers, MAC +addresses, firmware zips, stock rootfs, or generated images. + +## Development + +See [`agents/AGENTS.md`](agents/AGENTS.md) for a project brief (the hardware facts that are easy to +get wrong, the repo layout, and the device-safety rules) - useful whether you work by hand or with +an AI coding agent. Build the installer with `bash build/build.sh`. diff --git a/DEPENDENCY_INVENTORY.md b/DEPENDENCY_INVENTORY.md new file mode 100644 index 0000000..39d4bd2 --- /dev/null +++ b/DEPENDENCY_INVENTORY.md @@ -0,0 +1,53 @@ +# Dependency inventory - generated from a self-built onefile artifact + +> **Scope note:** this inventory describes a **self-built PyInstaller onefile**. The **published +> diskOS release does NOT ship that onefile** - it distributes source and runs from your own Python +> (see [`NOTICE.md`](NOTICE.md) / [`licenses/THIRD_PARTY_BUNDLED.md`](licenses/THIRD_PARTY_BUNDLED.md)), +> so these bundled libraries are not redistributed by this project. This file is retained for anyone +> who chooses to build and redistribute the onefile themselves (they take on these obligations). + + +This is an artifact-derived list from a PyInstaller onefile build: ~93 distinct native `.so` entries +were bundled. Below are the license-bearing shared libraries PyInstaller pulled in transitively +(CPython + Tkinter GUI + pycryptodome + pyusb chains), grouped by license obligation. They matter +**only if you build and redistribute the onefile yourself**; the published source release does not +bundle them. + +## Copyleft - obligations to satisfy (priority) +| Library | Bundled as | License | Obligation | +|---|---|---|---| +| **libudev** (systemd) | `libudev.so.1` | **LGPL-2.1** | ship license text + §6 relink materials (or drop it) | +| **FreeType** | `libfreetype.so.6` | **FTL or GPLv2 (dual)** | ship FTL text (permissive path) + attribution | +| **libcap** | `libcap.so.2` | BSD-3 / GPLv2 (dual) | permissive path: BSD text | + +> NOTE: `libudev` (LGPL) is bundled only because of the **Tkinter GUI**'s X/font chain. Consider a +> `--nowindow`/CLI-only build, or explicitly excluding `libudev`, to remove the LGPL relink burden. + +## Permissive - attribution/notice only (ship license texts) +| Library | Bundled as | License | +|---|---|---| +| OpenSSL 3 | `libssl.so.3`, `libcrypto.so.3` | Apache-2.0 | +| Tcl / Tk 8.6 | `libtcl8.6.so`, `libtk8.6.so` | TCL/Tk (BSD-style) | +| libpng | `libpng16.so.16` | PNG Reference License | +| zlib | `libz.so.1` | zlib | +| xz / liblzma | `liblzma.so.5` | 0BSD / public-domain | +| bzip2 | `libbz2.so.1.0` | bzip2 (BSD-style) | +| libffi | `libffi.so.8` | MIT | +| fontconfig | `libfontconfig.so.1` | MIT-style | +| expat | `libexpat.so.1` | MIT | +| brotli | `libbrotlicommon/dec` | MIT | +| X11 client libs | `libX11/Xext/Xrender/Xft/Xau/Xdmcp/Xss` | MIT (X11) | +| BLT | `libBLT*` | BSD-style | + +## Already in NOTICE.md (direct deps) +CPython (PSF), pyusb (BSD-3), pycryptodome (BSD-2 + public-domain), PyInstaller bootloader +(GPL-2.0 + bootloader exception). The pycryptodome `_raw_*`/`_ghash_*` etc. modules are part of it. + +## If you build and redistribute the onefile yourself + +The published release does **not** ship the onefile, so none of the above is an obligation for it. If +you choose to build one (`build/build.sh`) and hand it to others, **you** become the redistributor of +everything it embeds and must satisfy those licenses - in particular the LGPL `libudev` and the +dual-licensed FreeType pulled in by the Tkinter GUI. The simplest way to avoid that burden is a +CLI-only build that excludes `tkinter`. To enumerate exactly what a given onefile bundles, walk the +extracted binary's shared objects and map each back to its distribution package. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b07d6b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 diskOS contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..e68c50e --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,135 @@ +# diskOS installer - third-party components & licenses + +The diskOS installer ships the third-party programs and libraries below. Each is invoked as a +separate program or loaded as a library; this document provides the required attribution and points +to the corresponding source that ships alongside the binaries. The full verbatim license texts are in +[`licenses/`](licenses/); this file is the component-to-license index. + +## Bundled native programs (called as subprocesses - mere aggregation) + +| Component | Purpose | License | Source | +|---|---|---|---| +| **usbboot** | Ingenic X2000 mask-ROM USB loader (the flasher front-end) | **GPL-2.0-or-later** - © 2021 Aidan MacDonald, © 2015 Amaury Pouly | included: `src/usbboot/` (built from this) | +| **mksquashfs / unsquashfs** (squashfs-tools **4.6.1**) | build/extract the rootfs image | **GPL-2.0** | https://github.com/plougher/squashfs-tools (v4.6.1) | +| **dropbearmulti** (Dropbear SSH **2022.83**) | embedded in the diskOS image; started by the opt-in Debug Mode for SSH-over-WiFi | **MIT-style** (© 2002-2020 Matt Johnston; components under MIT/BSD/public-domain) - text in `licenses/LICENSE-dropbear.txt` | https://matt.ucc.asn.au/dropbear/dropbear.html (v2022.83; **predates the CVE-2023-48795 "Terrapin" Strict-KEX mitigation - an update is planned**. Debug Mode SSH is opt-in and short-lived, and Dropbear's Terrapin exposure is limited.) | + +`usbboot` and squashfs-tools are **GPL-2.0**, so anyone redistributing them must make their +corresponding source available and include the GPL-2.0 license text. (`dropbearmulti` is MIT-style - +no source-availability obligation, only its license text as noted above.) **The complete +corresponding source ships in-tree and in every release tarball**, so it travels from the same place +as the binaries (GPL-2.0 §3(a), plus the "same place" provision in §3's final paragraph): +`usbboot`'s own source is `src/usbboot/usbboot.c`; +**squashfs-tools 4.6.1** source is `corresponding-source/squashfs-tools_4.6.1*`. Both are rebuilt by +`build/build-usbboot-static.sh` / `build/build-squashfs-static.sh`. + +**Static linking (Linux):** the prebuilt Linux `usbboot`, `mksquashfs`, and `unsquashfs` are shipped +**fully statically linked** (built with `gcc -static` on a glibc host). They therefore statically +incorporate: +- **libusb-1.0.27 (LGPL-2.1)** - in `usbboot` +- **liblzo2 2.10 (GPL-2.0-or-later)**, **zlib**, **liblzma** (permissive) - in squashfs-tools +- the **GNU C Library (glibc, LGPL-2.1-or-later)** - the host's system C runtime, in all three + +Because liblzo2 is GPL, the resulting `mksquashfs`/`unsquashfs` binaries are effectively GPL-2.0. The +corresponding source for the statically-linked **GPL/LGPL** libraries is provided so their §3/§6 +obligations are met: +- **liblzo2** (`corresponding-source/lzo2_2.10*`) and **libusb-1.0** (`corresponding-source/libusb-1.0_1.0.27*`) + ship in-tree. +- **glibc** (statically linked, so it travels inside the binary): the **complete corresponding source** + is vendored in `corresponding-source/glibc_2.39*` (the exact version the shipped binaries were built + against). Because it is statically linked it does not qualify for the "system library" exception, so + its source ships alongside the binaries like the others. + +**Relink path (satisfies LGPL-2.1 §6 for libusb *and* glibc):** we ship the **complete source** of +`usbboot` (`src/usbboot/usbboot.c`) and squashfs-tools (`corresponding-source/squashfs-tools_4.6.1*`) +plus the exact build recipes (`build/build-usbboot-static.sh`, `build/build-squashfs-static.sh`). A +user can therefore rebuild and relink these tools against a **modified** libusb, liblzo2, **or glibc** +of their choosing - the freedom §6 exists to protect. See +[`corresponding-source/README.md`](corresponding-source/README.md). + +**Python runtime + libraries - NOT redistributed by this project.** The installer runs **from +source** with **your own Python**. The two Python dependencies (`pyusb`, `pycryptodome`) are fetched +from PyPI by *your* `pip` into a local `.venv` (created by `./install.sh`); CPython, `tkinter`, and +their transitive native libraries (OpenSSL, Tk/Tcl, the X11 stack, freetype, fontconfig, zlib, etc.) +all come from **your system's Python** - this project ships none of them, so their redistribution +notices are not our obligation. Their license texts (for reference) are still listed in +[`licenses/README.md`](licenses/README.md). + +> If you instead build a self-contained PyInstaller onefile yourself (`build/build.sh`), *that* +> binary embeds CPython + ~90 native libraries and you become the redistributor of them - see +> [`licenses/THIRD_PARTY_BUNDLED.md`](licenses/THIRD_PARTY_BUNDLED.md) for the enumeration and +> obligations. The **published release does not do this**; it distributes source + the native flash +> tools below. + +## Bundled device blobs + +| Component | Purpose | Notes | +|---|---|---| +| **my_write5_dram.bin** | the bad-block-aware DRAM NAND writer run on-device | diskOS project code - source: `flash/my_write5.c` | +| **disc_spl_lpddr3.bin** | Ingenic X2000 USB stage-1 SPL (DRAM init) | **GPL-2.0** - built from source: Ingenic-community/uboot-xburst `1060b516` + our LPDDR3 patches. Corresponding source in `spl-src/`; see `SPL_SOURCE.md`. (No vendor/USBCloner binary is redistributed.) | + +## Python dependencies (fetched by your pip, not redistributed by us) + +`./install.sh` installs these from PyPI into a local `.venv`; **CPython** comes from your own system +Python. This project does not ship any of them, so their license texts are listed here only for +reference. (**PyInstaller** is used *only* if you build the optional onefile yourself; it is not +involved in the source release.) + +| Component | License | +|---|---| +| **pyusb** (USB device detection) | BSD-3-Clause | +| **pycryptodome** (AES-decrypt of FiiO OTA chunks) | BSD-2-Clause + public-domain (Unlicense) | +| **CPython** (your system's) | Python Software Foundation License | +| **PyInstaller** runtime bootloader (only for a self-built onefile) | GPL-2.0 **with a bootloader exception** permitting distribution of the packaged app under any license | + +## Bundled shared libraries (macOS build) + +| Component | License | +|---|---| +| **libusb-1.0** (bundled next to usbboot on macOS) | LGPL-2.1 | + +## diskOS's own code + +- **The Python installer, build scripts, and docs** in this repo are licensed **MIT** - + `Copyright (c) 2026 diskOS contributors`. See `LICENSE`. +- **The diskOS UI (`payload/mq_ui`)** ships as a **binary-only** component (static-musl, MIPS): its + source is not yet published and it is **not** covered by the MIT license above. + + **diskOS UI Binary License:** *You may use and redistribute the `mq_ui` binary verbatim - on its own + or as part of the diskOS installer - free of charge. It is provided **AS IS, without warranty of any + kind**. Its source is not published, and no right to modify, decompile, or reverse-engineer it is + granted. The embedded third-party components below retain their own licenses.* © 2026 the diskOS + author (alias **b0hemia**). + + It statically incorporates these third-party components, whose licenses apply to the shipped binary: + + | Embedded in `mq_ui` | License | Text / source | + |---|---|---| + | **musl libc** (static C runtime) | MIT | `licenses/MIT-musl.txt`; source: https://musl.libc.org/ | + | **LVGL** (UI toolkit) | MIT | `licenses/MIT-LVGL.txt` | + | **JSMN** (JSON parser) | MIT | `licenses/MIT-JSMN.txt` | + | **SQLite** (amalgamation) | Public domain | https://sqlite.org/copyright.html | + | **MD5** (Peslyak/Solar Designer) | Public domain | in the (unpublished) UI source | + | **QR Code generator** (Nayuki `qrcodegen`) | MIT | `licenses/MIT-qrcodegen.txt` | + | **TJpgDec** (tiny JPEG decoder, via LVGL) | BSD-style (ChaN) | `licenses/LICENSE-TJpgDec.txt` | + | **Montserrat** font (via LVGL built-in fonts) | SIL OFL 1.1 | `licenses/OFL-1.1-Montserrat.txt` | + | **Source Han Sans SC** (CJK fallback font, subset) | SIL OFL 1.1 | `licenses/OFL-1.1-SourceHanSans.txt` | + | **Font Awesome Free** glyphs (via LVGL `LV_SYMBOL_*`) | OFL 1.1 (fonts) + CC-BY 4.0 (icons) | `licenses/OFL-1.1-FontAwesome.txt`, `licenses/CC-BY-4.0.txt` | + +## NOT distributed by this installer + +- **FiiO's stock rootfs** - you supply your own official firmware; the installer only reads it locally + and never redistributes it. +- **ffmpeg** - used at runtime for album-art decoding, but it is part of FiiO's stock firmware already + on the device (`/usr/bin/ffmpeg`); the installer does **not** ship it. + +--- + +### Compliance checklist (done) +- ✅ Full license texts shipped in [`licenses/`](licenses/) (GPL-2.0, LGPL-2.1, BSD, PSF, LVGL-MIT, + dropbear, Font Awesome OFL/CC-BY) - see [`licenses/README.md`](licenses/README.md). +- ✅ diskOS installer license stated: **MIT** (`LICENSE`). The `mq_ui` binary is documented as + binary-only (source unpublished, not MIT) with its embedded LVGL/JSMN/Font Awesome notices shipped. +- ✅ Exact upstream versions pinned and their **complete corresponding source shipped in-tree** for the + GPL/LGPL native binaries: squashfs-tools 4.6.1, liblzo2 2.10, libusb-1.0 1.0.27, the SPL, and + usbboot - in [`corresponding-source/`](corresponding-source/) and [`spl-src/`](spl-src/). Source + travels from the same place as the binaries; no separate written offer is needed. diff --git a/README.md b/README.md new file mode 100644 index 0000000..85676ef --- /dev/null +++ b/README.md @@ -0,0 +1,288 @@ +# diskOS + +**diskOS** is a custom player UI/firmware for the **FiiO Snowsky Disc** digital audio player +(Ingenic X2000). It replaces the stock interface, and ships with an installer (run from source with +your own Python; a setup script sets up a local virtual environment) that flashes it onto the device +over the chip's mask-ROM USB mode - building the image **from your own stock firmware**, so no FiiO +rootfs is redistributed. The installer tooling in this repo is open +source; the diskOS UI itself (`payload/mq_ui`) currently ships as a **binary-only component** +(see [License](#license)). + +> ⚠️ **Unsupported beta tool - read this.** Installing **erases and rewrites the device's main +> root filesystem**. Power loss, host sleep, a bad cable, a software defect, an unsupported +> DRAM/NAND variant, or running it on the wrong X2000 device can leave the player unbootable, lose +> data, or require hardware recovery. Mask-ROM reflashing recovered the tested unit(s), but +> **recovery is not guaranteed** for every unit or every failure. This may void your warranty. +> Not affiliated with or endorsed by FiiO, Snowsky, or Ingenic. **Proceed at your own risk; no +> warranty or support is promised.** Back up your data and keep the installer's saved stock image +> on separate storage. + +> 🧪 **BETA.** Validated on the **Winbond W63AH6NKB (LPDDR3)** DRAM - the chip the Snowsky Disc +> uses. The Disc's own stock bootloader initializes only this chip, so every Disc that runs stock +> firmware has it; this is the Disc's DRAM, not one of several variants. We have flashed and booted +> our own unit(s), but wide field-testing is still ongoing, so treat it as beta. In the unlikely +> event a future hardware revision ever ships different DRAM, the flash fails safe at memory init +> (the device stays mask-ROM-recoverable) rather than completing. Report your results. + +## Documentation + +| Doc | What's in it | +|---|---| +| [`docs/HARDWARE.md`](docs/HARDWARE.md) | Live-probed hardware capability map: SoC, the quad CS43131 balanced DACs, display planes, power ICs, wireless, USB, and the stock-vs-hardware gap list. | +| [`docs/COMMAND_MAP.md`](docs/COMMAND_MAP.md) | The player IPC command surface: ~221 `mq_player` tags, reply frames, and the MCU/SPI command set - what you drive to build features. | +| [`docs/RE_CATALOGUE.md`](docs/RE_CATALOGUE.md) | Reverse-engineering of the `mq_player`/`mq_ui` binaries: structure, tables, string maps, network receivers. | +| [`NOTICE.md`](NOTICE.md) | Third-party components and their licenses (GPL usbboot, squashfs-tools, the SPL, etc.). | +| [`SPL_SOURCE.md`](SPL_SOURCE.md) | GPL corresponding source + build recipe for the stage-1 DRAM bring-up bootloader. | +| [`DEPENDENCY_INVENTORY.md`](DEPENDENCY_INVENTORY.md) | Libraries a *self-built* onefile would bundle + their obligations (the source release does not bundle these). | +| [`docs/PRIVACY.md`](docs/PRIVACY.md) | What the installer and the on-device UI send over the network. | +| [`agents/`](agents/) | A project brief you can drop into Claude Code or Codex to work on diskOS with an AI agent. | +| [`SECURITY.md`](SECURITY.md) / [`CONTRIBUTING.md`](CONTRIBUTING.md) | How to report vulnerabilities; how to contribute safely. | +| [`licenses/`](licenses/) | Verbatim license texts for the shipped third-party components. | + +## First-time setup + +The installer runs from source with your own Python. A one-time setup script builds a local +virtual environment and installs the two Python dependencies into it (nothing is installed +system-wide): + +```bash +./install.sh +``` + +You need **Python 3.8+**. The script also checks for two optional system components and prints the +exact package to install if either is missing: +- **Tk / tkinter** - only for the *graphical* installer (the command line works without it). + Debian/Ubuntu: `sudo apt install python3-tk`. +- **libusb-1.0** - to detect the device in mask-ROM mode. + Debian/Ubuntu: `sudo apt install libusb-1.0-0`. + +After setup, run everything through `./diskos-installer` (it uses the `.venv` automatically). + +## Install (graphical) + +1. Launch the graphical installer: `./diskos-installer gui`. +2. Choose **Install diskOS**, pick your FiiO firmware `.zip`, and a variant: + - **Public** - no *always-on* root shell (recommended). Debug Mode can still enable SSH on + demand from the UI (opt-in, off by default - see below). + - **Dev** - adds an always-on USB-serial **root shell**. ⚠️ **This is a passwordless root shell + available to anyone with physical USB access, on every boot; it bypasses normal device + security.** Only use it on a development device you control, never an everyday or untrusted one. +3. Put the device in **mask-ROM**: power it **off**, hold **Volume-Down**, and plug in USB (the + screen stays **black** - that's correct). +4. Click **Install**, tick the acknowledgement, and **Begin**. Don't disconnect or let the computer + sleep during the flash (~**60-90 minutes**). +5. When it verifies, **power-cycle** the device. The UI is embedded in the flashed image, so first + boot installs diskOS automatically - **no microSD step needed**. + +## Install (command line) + +```bash +./install.sh # one-time: build the .venv + install Python deps +./diskos-installer doctor # check host + bundled tools + device +./diskos-installer install --firmware SNOWSKY_DISC_update_*.zip --variant public +./diskos-installer restore-stock # deactivate diskOS -> reflash your saved stock rootfs +./diskos-installer remove # delete the installer's saved files from this computer +``` + +## Requirements + +- Your device's **official FiiO firmware** as a `.zip` (we never ship FiiO's rootfs - you supply + it; download it from FiiO's firmware page for the Snowsky Disc). The installer decrypts and + extracts the stock rootfs from it locally. +- A USB cable and ~**60-90 minutes** for the flash. +- **Python 3.8+** and the two pip dependencies (installed into a local `.venv` by `./install.sh`). +- USB access to the mask-ROM device (`a108:eaef`). **Don't run the installer as root** - it keeps your + saved recovery image and state under your home directory, and `sudo` would misplace them. Instead + install the bundled udev rule once so your normal user has access: + ```bash + sudo cp udev/70-diskos-maskrom.rules /etc/udev/rules.d/ + sudo udevadm control --reload-rules && sudo udevadm trigger + ``` + (The whole build+save+flash runs as one user process; there is no separate "flash step" to elevate.) +- **Linux (x86_64):** this is the released platform - the tarball ships the native flash tools + prebuilt in `vendor/linux-x86_64/`. +- **macOS (Apple Silicon + Intel):** no release artifact yet; it's a **build-from-source** path - + clone the repo and run `build/build-macos.sh` first (`libusb` via `brew install libusb`). See + *For developers*. Treat macOS as unverified until built and flashed. + +## Remove diskOS / restore stock + +`restore-stock` reflashes the stock rootfs the installer built and saved during install - a +checksum-verified reconstruction of your firmware's rootfs (not a dump of the device's original +partition). This **deactivates diskOS**; inert diskOS files under `/usr/data` remain until you +delete them - it is not a factory wipe. You can also switch back temporarily: +**Settings → System → Default UI → Stock**, or hold **Vol-Up at power-on** to boot the other UI once. + +If a flash fails or is interrupted, the main rootfs may be **partially written** and the device +may not boot normally. In the tested cases you can return to mask-ROM (power off, hold Vol-Down, +replug) and re-flash or restore your saved stock image. Mask-ROM is normally reachable because it +lives in on-chip ROM and is entered by a button combo before any flashed code runs, but recovery +is **not guaranteed** on untested hardware or every failure mode. + +## What's proven vs. beta (honest) + +- ✅ **Flash mechanism + image build** - proven; it's how diskOS was flashed to real hardware, + including the bad-block-aware writer skipping factory bad blocks and verifying every block. +- ✅ **Firmware extraction** - reproduces the stock rootfs byte-for-byte from FiiO's zip. +- ✅ **Linux** - the app builds and flashes end-to-end. +- ⚠️ **macOS** - code is macOS-aware and the build recipe is provided, but the macOS artifact has + not yet been produced/tested on Apple hardware. Treat as unverified until built + flashed. +- ⚠️ Each stock firmware version should be flash-tested before it's declared supported + (V2.09 and V2.28 so far). + +## Known issues (this beta) + +diskOS is an early beta. What we know about: + +- **Firmware coverage.** Only **V2.09** and **V2.28** are flash-tested. Newer stock versions use + different internal command codes and are refused by default (override at your own risk). +- **~90-minute flash.** A full install writes **and verifies** the whole root filesystem over USB, so + it takes roughly **60-90 minutes**. Don't disconnect or let the host sleep. (Most of that time is a + conservative fixed wait; a faster writer is planned.) +- **No on-device updates.** Updating diskOS means re-flashing with the installer; there is no + over-the-air path yet. +- **microSD at cold boot.** The card is auto-mounted a few seconds into boot. If your library is + empty right after a **cold** boot, reinsert the card once and it should mount. +- **Output/work modes.** USB-DAC, Bluetooth-receiver, and USB-storage modes are wired but lightly + tested; local playback is the well-worn path. +- **USB-serial debug is unreliable.** The optional serial shell can be finicky (a USB CDC-ACM + flow-control quirk); prefer **Debug Mode's SSH over WiFi** for remote access (see below). +- **Last.fm scrobbling is unverified beta.** The building blocks (HTTPS transport, request signing, + parsing) are tested, but the full connect-and-scrobble round-trip against a live Last.fm account has + not yet been run end-to-end. It is **off by default** and needs your own Last.fm API key; treat it as + experimental. Credential setup transfers your API key over your **local network in plain HTTP** - see + [`docs/PRIVACY.md`](docs/PRIVACY.md). + +Found something else? Open an issue with your device's **firmware version** and any on-screen +**error code**. + +## Debug Mode (optional remote access) + +For development or troubleshooting, diskOS can expose a remote shell **on demand** - it is **OFF by +default**. Open **Settings → System → Debug Mode → Enable Debug**. The screen then shows: + +- an **SSH** command (`ssh root@`) reachable over WiFi, and +- a **fresh random password**, generated per-enable and shown on that screen. + +> ⚠️ **This grants root access to the device over your network while it is on.** Only enable it on a +> network you trust, and turn it **off** when you are done. The password is random and rotates each +> time you enable it; the device's stock password is never used or exposed. +> +> **Where the password lives:** while Debug Mode is on, the current password is also written in +> **plaintext** to `/usr/data/sshd/current_pw` (mode 0600, root-only) so the screen can show it again +> after a UI restart. Disabling Debug Mode deletes it. A **reboot while it is still on** can leave a +> stale copy - harmless, because a reboot drops the SSH overlay (so that password no longer works +> until you re-enable), but you can delete the file manually if you want it gone. It is only as +> protected as physical/root access to the device's storage. +> +> **SSH server:** Debug Mode uses **Dropbear 2022.83**, which predates the CVE-2023-48795 "Terrapin" +> Strict-KEX mitigation (an update is planned). Because Debug Mode is opt-in and short-lived, exposure +> is limited, but keep it off on untrusted networks. Serial (USB) is available only on **dev** builds +> and is unreliable (see the known issue above); public builds expose no serial shell. + +## How it works (in brief) + +diskOS runs by a small hook in the stock `fiio_init.sh` that launches our UI (`mq_ui`) instead of +the stock one. Because the rootfs is a **read-only squashfs**, enabling that hook means rewriting +the rootfs partition (mtd2) - hence the flash. The UI itself is embedded in the image and installed +to the writable `/usr/data` on first boot, verified against a baked SHA-256 manifest; if it does +not match, the stock UI runs instead (**fail-closed**). You **cannot** install from the device's own +"System updates" menu - that path checks a signature we can't forge, so mask-ROM USB is the only +way in. See [`docs/HARDWARE.md`](docs/HARDWARE.md) for the partition map and the rest of the hardware. + +## For developers + +The installer already **runs from source** - `./install.sh` then `./diskos-installer` (see +*First-time setup*). The release tarball ships the native flash tools prebuilt in +`vendor/-/`; a fresh **git clone** does not include them (they are large binaries kept out +of git), so build them once: + +```bash +# Build the native flash tools into vendor/-/ : +bash build/build-usbboot-static.sh # static usbboot (Linux) +bash build/build-squashfs-static.sh # static mksquashfs/unsquashfs (Linux) +bash build/build-macos.sh # usbboot + libusb (run on a Mac) +``` + +Optionally, you can package everything into a single self-contained binary (this is **not** how the +release ships, and it bundles ~90 system libraries - see +[`licenses/THIRD_PARTY_BUNDLED.md`](licenses/THIRD_PARTY_BUNDLED.md) before redistributing one): + +```bash +bash build/build.sh # -> build/dist/diskos-installer (optional onefile) +``` + +See [`build/README-vendor.md`](build/README-vendor.md) for how the native tools are produced and the +portability requirements. Working on diskOS with an AI agent? Start with +[`agents/AGENTS.md`](agents/AGENTS.md). + +## Error codes (for bug reports) + +If the installer stops with an error, it prints a short code like `[E301]`. **Quote that code** +when reporting an issue - it tells us exactly where it stopped. In the tested cases the device +stays reachable in mask-ROM and your saved stock image can be re-flashed, but recovery is not +guaranteed on untested hardware or every failure mode. + +| Code | Meaning | +|---|---| +| **E1xx** | **Environment / preflight - nothing was written to the device** | +| E101 | Unsupported host OS/arch (Linux/macOS only) | +| E102 | A bundled component is missing - re-download the installer | +| E103 | A bundled tool can't run (its directory is mounted `noexec`, or a missing library) | +| E110 | No device in mask-ROM mode (power off, hold Vol-Down, plug USB) | +| E111 | More than one device in mask-ROM mode - unplug the others | +| E112 | Can't confirm exactly one device (USB permissions / no libusb) | +| E120 / E121 / E122 | Image not found / wrong size / not a squashfs | +| E140 / E141 | No firmware `.zip` provided / no saved stock image to restore | +| E142 | `DISKOS_INSTALLER_HOME` points at a non-empty, non-state directory | +| **E2xx** | **Firmware extract & image build** | +| E201 / E202 | Input isn't a zip / unsafe zip (path escape or bomb) | +| E210 | FiiO OTA manifest missing, ambiguous, or unsafe | +| E211 | AES-decrypt failed (wrong key or not a FiiO OTA) | +| E212 / E213 | Rootfs chunks missing/duplicate / assembled image invalid | +| E220 / E221 | Not a Snowsky Disc rootfs / untested firmware version | +| E222 / E223 | diskOS UI binary invalid / boot-hook anchor problem | +| E224 | Stock rootfs doesn't match the known-good pinned hash (modified/corrupt firmware) | +| E230 / E231 / E232 | squashfs pack/unpack failed / image too large / output failed validation | +| E233 | A file in the stock rootfs resolves outside it via a symlink (crafted/corrupt firmware) | +| E240 | Stock image larger than the partition (refusing to truncate) | +| E250 | Invalid variant (must be `public` or `dev`) | +| **E3xx** | **Flashing (host side)** | +| E301 | No result read back - flash outcome UNKNOWN (assume failed, re-flash) | +| E302 | Short/truncated result readback - flash outcome UNKNOWN (assume failed, re-flash) | +| E303 | **Flash timed out - the device stopped responding (likely reset mid-flash)** | +| E310 | Flash verify failed - see the device code below | +| **F1xx** | **Device writer aborted and reported a coded reason.** It fails closed rather than committing a bad block mapping, but blocks erased/written before the abort may already be modified - the rootfs can be partially written. Return to mask-ROM and re-flash or restore stock. | +| F101…F106 | init/ECC, out-of-space, block-write fail, too many bad blocks, ECC re-enable, bad-block marker | + +Exit codes: `0` success, `1` error, `2` usage/preflight refusal (unsupported host, missing +`--firmware`, bad arguments, or Tk unavailable for the GUI), `3` you cancelled at a prompt, +`130` interrupted. + +## Support + +diskOS is a solo, open-source hobby project. If it's useful to you and you'd like to help keep the +work going, you can leave a tip: + +[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support%20diskOS-FF5E5B?logo=ko-fi&logoColor=white)](https://ko-fi.com/b0hemia) + +**[ko-fi.com/b0hemia](https://ko-fi.com/b0hemia)** - entirely optional, and hugely appreciated. + +## License + +- **Original diskOS installer files** in this repo (the Python installer, build scripts, docs) + are **MIT** - see [`LICENSE`](LICENSE). SPDX: `MIT`. +- **Third-party components** retain their own licenses and ship with corresponding source: the native + flash tools **usbboot** and **squashfs-tools** (**GPL-2.0**), the libraries they statically link + (**liblzo2** GPL-2.0, **libusb-1.0** LGPL-2.1, zlib/liblzma), and the stage-1 **SPL** (**GPL-2.0**). + The corresponding source for all of these ships in [`corresponding-source/`](corresponding-source/) + (SPL also in [`spl-src/`](spl-src/) + [`SPL_SOURCE.md`](SPL_SOURCE.md)); see + [`NOTICE.md`](NOTICE.md) for the full mapping and the relink path for the LGPL libusb. +- **The diskOS UI (`payload/mq_ui`)** currently ships as a **binary-only** component. It is not + covered by the MIT license above and its source is not yet published; treat it as a redistributable + binary whose provenance, version, and third-party content are still being documented. If/when the + UI source is published this note will be updated. + +> **Do not redistribute the `diskos_*.bin` images the installer builds** - they contain FiiO's +> rootfs. The installer produces them locally from *your* firmware; that is fine for your own use, +> but sharing them would redistribute FiiO's software. Share the installer, not the built image. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..eed549d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,64 @@ +# Security policy + +diskOS is an **unsupported beta** that flashes firmware over a device's mask-ROM USB mode. Because +a defect here can brick a device or write to the wrong hardware, security and safety reports are +taken seriously. + +## Reporting a vulnerability + +**Do not open a public issue for a security or device-safety vulnerability.** Instead, use GitHub's +private vulnerability reporting for this repository (the "Report a vulnerability" button under the +**Security** tab), which opens a private advisory visible only to the maintainer. + +Please include, where relevant: + +- What the issue is and how to reproduce it. +- The impact (e.g. writes outside the intended partition, wrong-device targeting, host privilege + issues, data exposure). +- Your host OS, the installer version, and the device firmware version. + +Please redact anything device-identifying from logs before sending: serial numbers, Bluetooth/Wi-Fi +MAC addresses, and network credentials. + +## Especially in scope + +- **Wrong-device / wrong-partition writes** - anything that could let the flasher erase or write a + device it should have refused, or write outside the intended rootfs region. +- **Host privilege issues** - the installer is designed to run as your **normal (non-root) user** + (USB access via the bundled udev rule); it should not need `sudo`. Path-traversal, symlink-follow, + arbitrary-delete, or predictable-temp-file issues matter. +- **Unauthenticated input** - the installer extracts a squashfs and decrypts a firmware package; + report anything that lets crafted input escape the work directory or run code. +- **Bricking-class bugs** - a report that a specific sequence leaves a device unrecoverable. + +## Debug Mode (on-device remote access) + +diskOS has an opt-in **Debug Mode** (Settings → System → Debug Mode), **off by default**, that starts +an SSH server over WiFi. It uses a **random password generated per enable**, placed into a private +shadow file bind-mounted over `/etc/shadow`; the device's stock password is never used or exposed. +While it is on, it grants **root access over the network**. + +- **Password storage:** while Debug Mode is on, the current password is also stored in **plaintext** at + `/usr/data/sshd/current_pw` (mode 0600, root-only) so the UI can redisplay it after a restart; + disabling Debug Mode deletes it. A reboot while still enabled can leave a stale copy - the SSH + overlay is dropped on reboot so that password no longer authenticates until re-enabled. It is only + as protected as root/physical access to the device. +- **SSH server:** Dropbear **2022.83**, which predates the CVE-2023-48795 "Terrapin" Strict-KEX + mitigation (update planned); exposure is limited by Debug Mode being opt-in and short-lived. +- **Serial:** the local USB-serial root shell exists only on **dev** builds and is passwordless-root by + design (physical-USB access only); **public builds have no serial shell**. Report only if it is + reachable without physical access. + +In scope: anything that exposes the debug password, leaves SSH running (or the shadow overlay mounted) +after it should be off, lets the overlay corrupt/leak the on-disk credentials, or reaches the stock +`/etc/shadow` over the network. + +## Response + +This is a hobbyist project with no SLA, but security and bricking reports are prioritised over +features. Expect an initial acknowledgement within a week or so. Fixes for confirmed +device-safety issues will be called out clearly in the release notes. + +## Supported versions + +Only the latest release is supported. There are no backported fixes for older builds. diff --git a/SPL_SOURCE.md b/SPL_SOURCE.md new file mode 100644 index 0000000..0c36c7d --- /dev/null +++ b/SPL_SOURCE.md @@ -0,0 +1,94 @@ +# Stage-1 SPL - corresponding source & build (GPL-2.0) + +The installer's USB stage-1 loader (`flash/disc_spl_lpddr3.bin`) that brings up DRAM before +the on-device NAND writer runs is **built entirely from GPL source** - no vendor/USBCloner +binary is redistributed. This file is the GPL-2.0 "corresponding source" pointer + build recipe. + +## Source + +- **Upstream:** Ingenic-community **uboot-xburst**, U-Boot 2013.07, commit `1060b516` (GPL-2.0, + `COPYING` in-tree). https://github.com/Ingenic-community/uboot-xburst +- **Our patches** (a small, self-contained delta adding W63AH6NKB LPDDR3 support to the X2000 + USB "burner" SPL, ported from the GPL tobunto/u-boot X2000 tree): + 1. `-fcommon` + a mask-ROM return shim in `arch/mips/cpu/xburst2/x2000/start.S` (DDR-only + stage-1 returns to the mask ROM after DRAM init). + 2. `ddr_mr11` field + macro in `ddr_reg_data.h`. + 3. `case LPDDR3:` MR sequence (MR63,10,1,2,3,11) in `ddrc_dfi_init()`. + 4. Force `type = LPDDR3` in `sdram_init()` (this part's controller CFG.TYPE reads as LPDDR2). + 5. **PHY PLL divisors `FBDIV=8, PDIV=4`** for W63AH6NKB LPDDR3 @400 MHz (the tree's stock + 20/5 sets the wrong PHY clock and DDR training never converges). + 6. Bounded every hardware-poll (no infinite hang on a bad unit) + a small TCSM breadcrumb + record for field diagnostics. + 7. **Robust watchdog disable** (clear `WDT_TCER.TCEN` then stop the WDT clock via `TCU_TSSR`) + **and dropped the redundant dynamic-calibration sweep** - the stock code stopped only the WDT + clock (leaving the counter armed, which reset the device ~36 min into a flash), and the sweep + left a marginal PHY calibration. Device-verified: survives 25 min idle + full 76 MB DRAM + round-trip intact (the 2026-08-25 flash-failure fix). + +The complete patched source is shipped as `spl-src/uboot-xburst-lpddr3-src.tar.gz`, with the +delta over upstream also provided as a readable patch series in `spl-src/patches/`. Frozen at the +commit the released binary was built from: `7caf048` / tag `lpddr3-working-v2` (Ingenic-community/uboot-xburst `1060b516` + 8 patches). + +## Toolchain + +- `gcc-10-mipsel-linux-gnu` (10.5.0) + binutils for mipsel. (The tree's compiler-compat headers + stop at gcc-10; newer GCC fails. Pin GCC 10.) + +## Build + assemble + +```sh +export ARCH=mips CROSS_COMPILE=mipsel-linux-gnu- # gcc-10 on PATH as mipsel-linux-gnu-gcc +make distclean +make burner_x2000_lpddr2_config +make -j # -> spl/u-boot-spl.bin +# USB stage-1 = DDR param block + zero-pad to 0x800 + spl/u-boot-spl.bin +# load 0xb2401000, exec 0xb2401800; see flash/assemble_stage1.py +``` + +## DDR parameter block + +The stage-1 prepends a 0x800-byte parameter region the SPL reads at `0xb2401000`: +a **BDIF** (board-info) record + a **DDR** record of 35 Winbond W63AH6NKB-BI register words +(controller/PHY/MR/timing values - hardware register settings for LPDDR3 @400 MHz, 24 MHz +EXTAL, 800 MHz PLL). + +**Provenance (honest, and an OPEN item):** these values were **captured from the device's factory +configuration** (they match what the vendor SPL uses), NOT independently re-derived from the +W63AH6NKB datasheet. `flash/assemble_stage1.py` copies roughly **332 bytes** of this captured +block into the shipped stage-1 binary. These are low-level hardware register settings, but we do +**not** claim, as a legal conclusion, that "they are physical facts, therefore unrestricted." The +provenance of these vendor-origin bytes is an **unresolved compliance item**. Before any wide/public +binary distribution it should be resolved by one of: (a) independently regenerating the complete +parameter/BDIF block from documented datasheet values; (b) obtaining and documenting redistribution +permission; or (c) having counsel review and approve the provenance with an accurate disclosure of +the copied vendor-origin bytes. Until then the captured block ships as both the working block and a +regression oracle, and this limitation is disclosed here. + +## Supported hardware + +This stage-1 is built for the **Winbond W63AH6NKB-BI LPDDR3** DRAM and configures that chip. + +**The Snowsky Disc uses this one DRAM chip.** The Disc's own stock SPL contains exactly one DRAM +config - `LPDDR3_W63AH6NKB-BI` - and initializes only that chip. Because a Disc must run FiiO's +stock firmware (whose SPL brings up only W63AH6NKB), any Disc in the field necessarily has this +chip; FiiO cannot ship a unit with DRAM its own bootloader can't initialize. So supporting +W63AH6NKB covers every current Disc, not "one of several." (The chip names `W97BV6MK` and +`NK6CL256M16` that appear in Ingenic's cloner configs are for *other* X2000-family boards - +x2000e/x2000h/m300 - not the Disc.) + +**Residual / tail risk:** we have physically verified our own unit(s); "every Disc has W63AH6NKB" +is a strong inference from the stock SPL, not a survey of many units, so a hypothetical future +hardware revision with different DRAM cannot be ruled out. The SPL **fails safe** on DRAM it cannot +bring up: bounded polls return to the mask ROM instead of hanging, and a TCSM breadcrumb +@0xb24017c0 reports the failing stage - no brick, still mask-ROM-recoverable. Note the installer +does **not** run a separate DRAM verify-before-erase gate on each flash, so DRAM that *initialises +but is marginal* is a residual risk (a bad read-back would be written to NAND and would verify +against that same DRAM). A future revision could add a pre-erase DRAM round-trip and/or multi-chip +auto-detect if a different Disc DRAM ever turns up. + +## Verification + +Device-qualified over Ingenic mask-ROM USB boot **during development**: the stage-1 loads, +initialises LPDDR3, returns to the mask ROM, and a DRAM write/read round-trips byte-for-byte. This +was the bring-up qualification of the SPL itself; it is **not** a gate the installer re-runs on +every flash (see the residual-risk note above). diff --git a/agents/AGENTS.md b/agents/AGENTS.md new file mode 100644 index 0000000..19485eb --- /dev/null +++ b/agents/AGENTS.md @@ -0,0 +1,86 @@ +# diskOS - working rules for AI coding agents + +This file is written for an AI coding agent (Claude Code, Codex, or similar) helping someone +hack on **diskOS** - a custom UI/firmware for the FiiO Snowsky Disc digital audio player +(Ingenic X2000 SoC). Drop it in as your `CLAUDE.md` or `AGENTS.md`, or paste it into the +session, and follow it. It encodes the discipline that keeps this project honest and the +device un-bricked. + +## The one rule that matters most: verify, do not guess + +This device's hardware, firmware, audio path, and command surface are **non-obvious and easy +to hallucinate**. Before you assert anything about how the device behaves: + +- **Check `docs/HARDWARE.md`** (live-probed hardware capability map) for chip part numbers, + sample rates, dev nodes, partition layout, I2C addresses. +- **Read the actual binary / source** for what code does - never state it from training priors + or from earlier in the session. +- If a claim is not in a doc and you cannot verify it against the device or a binary, **say it + is unverified** rather than stating it confidently. + +"Probably", "should be", "likely already" are hallucination flags. Resolve them by reading +first. + +## Hardware facts that are easy to get wrong + +- Wireless chip is **BCM43438 / AP6212 (2.4 GHz only, BT over UART)**. There is **no 5 GHz**. + (Old notes saying "BCM4345C5" are wrong - those `.hcd` files are leftovers for other models.) +- **Four CS43131 DACs**, fully balanced (L+/L-/R+/R-), driven by a kernel driver via **ioctl** + on `/dev/cs43131{,b,c,d}` - **not** via ALSA controls. +- **No physical LED** on the unit. `/sys/class/leds` is empty; `RGB_*` config fields are + vestigial. Plan no LED features. +- **No GPU / VPU / hardware JPEG / DVFS / thermal sensors.** MSA SIMD is the only accel. +- The rootfs partition (`mtd2`) is a **read-only squashfs**, which is why the boot hook cannot + be edited in place; persistent state lives in the writable `/usr/data` (`mtd7`). + +See `docs/HARDWARE.md` for the full map and the reproducible probe commands. + +## Repo layout + +- `diskos_installer/` - the Python installer (GUI + CLI) that builds a diskOS image from the + user's own stock firmware and flashes it over mask-ROM USB. +- `flash/` - the low-level flashing pieces: the mask-ROM writer source (`my_write5.c`), the + stage-1 SPL, and helper scripts. +- `spl-src/` - GPL corresponding source for the stage-1 DRAM bring-up SPL (patch series over + upstream U-Boot). See `SPL_SOURCE.md`. +- `src/usbboot/` - the Ingenic mask-ROM USB loader (third-party GPL; do not rewrite). +- `payload/` - the on-device first-boot hook and the diskOS UI binary (`mq_ui`). +- `docs/` - hardware map and other reference docs. +- `licenses/`, `NOTICE.md` - third-party attribution and license texts. + +## Device-safety rules (read before touching the device) + +- A flash **rewrites the read-only rootfs**. It is **normally recoverable** because the Ingenic + mask-ROM USB mode lives in on-chip ROM and is reached by a button combo *before* any flashed + code runs - so a bad flash can usually be re-flashed. This is the safety net, not an A/B slot, and + recovery is **not guaranteed** on every unit or failure mode (match the README's wording). +- The first-boot install is **fail-closed**: if the UI cannot be verified against the baked + manifest (SHA-256), the stock UI runs instead. Never weaken that contract. +- **You cannot install from the device's own "System updates" menu** - it checks an ECDSA + signature against FiiO's public key, which a custom image will not have. Mask-ROM USB is the + only way in. +- The mask-ROM USB link is **flaky** - it allows roughly one `usbboot` run per power-cycle and + hangs early ~1 in 3. If a run hangs at the download-to-start transition, power-cycle back + into mask-ROM and retry rather than fighting it. + +## Working habits + +- **Honest limitations.** When something is blocked by hardware (no 5 GHz, no GPU, DAC ioctls + not yet reverse-engineered) or a toolchain gap, say so plainly instead of implying it works. +- **Test before claiming success.** A change to the image builder or the boot hook is not + "done" until it is validated against a real build / a real device, not just a passing + typecheck. The installer carries offline validation (independent squashfs extraction, hash + round-trips) precisely so the expensive on-device flash only tests what it must. +- **Fail closed, log loudly.** Every error path in the installer has a code (`E1xx` preflight, + `E2xx` build, `E3xx` flash, `F1xx` device-writer). If you add a failure mode, give it a code + and document it in `README.md`. +- **Cite file:line** when you summarize what changed, so a human can verify it. + +## Contributing + +- Keep the flashed image = stock FiiO rootfs + the minimal diskOS patch. We do **not** + redistribute FiiO's rootfs; the installer builds it from the user's own firmware zip. +- GPL components (usbboot, squashfs-tools, the SPL) ship with corresponding source. If you + touch how they are bundled, keep the source and `NOTICE.md` in sync. +- New firmware versions must be **flash-tested on real hardware** before being declared + supported - command-tag meanings differ across versions and a wrong command can misbehave. diff --git a/agents/README.md b/agents/README.md new file mode 100644 index 0000000..46c90b1 --- /dev/null +++ b/agents/README.md @@ -0,0 +1,26 @@ +# agents/ + +Instructions for working on diskOS with an AI coding agent. + +`AGENTS.md` in this folder is a ready-made project brief: the hardware facts that are easy to +get wrong, the repo layout, the device-safety rules, and the "verify, do not guess" discipline +that keeps this project honest and the device un-bricked. + +## How to use it + +- **Claude Code:** copy `agents/AGENTS.md` to `CLAUDE.md` at the repo root (or into + `~/.claude/`), or start a session and tell Claude to read `agents/AGENTS.md` first. +- **Codex:** copy it to `AGENTS.md` at the repo root, or paste it in at the start of a session. +- **Anything else:** paste the contents in as system/context before you start. + +The point is to give the agent the same guardrails we use: check `docs/HARDWARE.md` before +claiming anything about the hardware, never assert device behavior from memory, keep the +fail-closed boot contract intact, and treat a flash as recoverable-but-serious. + +## Good first tasks + +- Read `docs/HARDWARE.md` and the installer's `README.md`, then ask the agent to explain the + install flow back to you - a quick check that it has the right mental model. +- Add support/validation for a new stock firmware version (must be flash-tested on real + hardware before it is declared supported). +- Improve error messages / add error codes for failure modes not yet covered. diff --git a/build/README-vendor.md b/build/README-vendor.md new file mode 100644 index 0000000..37ba592 --- /dev/null +++ b/build/README-vendor.md @@ -0,0 +1,52 @@ +# Vendored native tools - portability requirements + +The installer bundles native binaries under `vendor/-/` and calls them +via subprocess. For a distributable build these MUST be portable across the user's +machines - not just copies of the build host's dynamically-linked binaries. + +| tool | why bundled | portability requirement | +|---|---|---| +| `usbboot` | Ingenic mask-ROM USB loader (the flasher) | Build **static** against libusb compiled `--disable-udev` (drops libudev + libcap; libusb falls back to sysfs enumeration). `usbboot` has **no direct udev symbols** - they were only transitive via libusb - so this yields a binary needing only libc. Prefer full-static (musl) or, at minimum, `$ORIGIN/lib` rpath + a bundled `libusb-1.0.so` in `vendor//lib/`. Do **not** ship arbitrary host glibc `.so`s or rely on `LD_LIBRARY_PATH` as the primary strategy. | +| `mksquashfs`, `unsquashfs` | build/extract the rootfs image | Ship **static** squashfs-tools (with lzo support - the stock image uses `-comp lzo`). | +| `my_write5_dram.bin` | the DRAM NAND writer run on-device | Architecture-neutral blob (runs on the device, not the host). Copy as-is. | +| `disc_spl_lpddr3.bin` | X2000 SPL | Same - device blob, copy as-is. | + +## Status + +- `vendor/linux-x86_64/` holds **fully static** `usbboot`, `mksquashfs`, and + `unsquashfs`, produced by `build/build-usbboot-static.sh` and + `build/build-squashfs-static.sh` (each verifies the binary is `statically linked`; + the squashfs build additionally runs an LZO round-trip self-test). They are static + against glibc (built with `gcc -static`), so no runtime `.so` is needed; note the + static glibc/liblzo2/libusb licensing in `../NOTICE.md` and the corresponding source + in `../corresponding-source/`. `my_write5_dram.bin` and `disc_spl_lpddr3.bin` are + device blobs, copied as-is. +- `vendor/macos-*/` is produced on a Mac by **`vendor/setup-macos.sh`** (compiles + `usbboot` from `src/usbboot/usbboot.c` against Homebrew libusb, gathers + lzo-capable squashfs-tools, copies the device blobs, and runs `dylibbundler` so + every tool is self-contained under `vendor/macos-/lib/` - no Homebrew at + runtime). `usbboot.c` is a single libusb-only file (no udev / no Linux-isms), so + it builds on macOS as-is. Then `build/build-macos.sh` packages the app. + +## macOS: Intel + Apple Silicon coverage + +`usbboot.c` and pyusb are portable; only the *build* is per-arch. Options: + +1. **Native per-arch (most robust):** run `build/build-macos.sh` on an Intel Mac + and on an Apple Silicon Mac → two binaries. Recommended. +2. **Single file via Rosetta:** build **x86_64** only (on an Intel Mac, or with an + x86_64 Homebrew under `arch -x86_64`). The x86_64 app runs natively on Intel and + under Rosetta 2 on M-series (macOS offers to install Rosetta on first run). + One file covers both, at a small speed cost - fine for a flasher. +3. **Universal2 (advanced):** build both arches, `lipo` the vendor tools into fat + binaries, and set `target_arch='universal2'` in the spec with a universal2 + Python. One native file for both, most work. + +## Building portable usbboot (sketch) + +1. Get the Ingenic `usbboot` source (the X2000 burn tool). +2. Build libusb static: `./configure --disable-udev --enable-static --disable-shared` + → `libusb-1.0.a`. +3. Link `usbboot` against the static libusb; confirm with `ldd` that libudev/libcap + are gone and only libc (or nothing, if fully static) remains. +4. Verify it still enters mask-ROM and flashes on a real unit before shipping. diff --git a/build/build-macos.sh b/build/build-macos.sh new file mode 100755 index 0000000..6c60ab3 --- /dev/null +++ b/build/build-macos.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# build-macos.sh - build the standalone diskos-installer app on macOS. +# RUN ON A MAC. Produces build/dist/diskos-installer for this Mac's arch. +# +# Intel + Apple Silicon coverage (pick one): +# * Simplest single file: build x86_64 (below, on an Intel Mac OR via an +# x86_64 Homebrew under Rosetta) - it runs natively on Intel and under +# Rosetta 2 on M-series. Needs Rosetta on M-series (macOS offers to install it). +# * Best native: build on each arch -> two binaries (diskos-installer-arm64 / +# -x86_64). Run this script on an Intel Mac and an Apple Silicon Mac. +# * Universal2: build both arches, lipo the vendor tools + use PyInstaller +# target_arch=universal2 (advanced; see build/README-vendor.md). +set -euo pipefail +cd "$(dirname "$0")/.." # installer/ + +[ "$(uname)" = "Darwin" ] || { echo "run this on macOS." >&2; exit 2; } + +if [ "${1:-}" != "--skip-setup" ]; then + echo ">> populating vendor/ for this Mac" + bash vendor/setup-macos.sh +fi + +VENV=build/venv +[ -d "$VENV" ] || python3 -m venv "$VENV" +"$VENV/bin/pip" install --quiet --upgrade pip pyinstaller pyusb pycryptodome + +"$VENV/bin/pyinstaller" --clean --noconfirm \ + --distpath build/dist --workpath build/work \ + build/diskos-installer.spec + +APP=build/dist/diskos-installer +# ad-hoc sign so it runs locally without a Developer ID (enthusiast tool). +# For distribution you'd sign + notarize with a real identity. +codesign --force --deep --sign - "$APP" 2>/dev/null || \ + echo " (codesign ad-hoc skipped/failed - 'xattr -dr com.apple.quarantine $APP' if Gatekeeper blocks it)" + +echo +echo "Built: $APP ($(uname -m))" +echo "Smoke test: $APP doctor" +echo "If Gatekeeper blocks it: xattr -dr com.apple.quarantine $APP" diff --git a/build/build-squashfs-static.sh b/build/build-squashfs-static.sh new file mode 100755 index 0000000..3532454 --- /dev/null +++ b/build/build-squashfs-static.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# build-squashfs-static.sh - build portable, fully-static mksquashfs + unsquashfs +# for Linux, with the compressors the installer actually uses. +# +# The installer repacks the rootfs with `-comp lzo -b 131072` and unpacks the +# stock (lzo) rootfs, so LZO is mandatory; gzip + xz are included for reading +# other firmware. Built static (liblzo2.a/libz.a/liblzma.a) so there are no +# liblzo2/libz/liblzma/glibc-version runtime deps on the user's machine. +# +# Output: installer/vendor/linux-x86_64/{mksquashfs,unsquashfs} (static, stripped). +# A cosmetic getpwuid NSS warning at link time is expected (owner-name display in +# the summary only; packing uses numeric uid/gid) and does not affect output. +set -euo pipefail +cd "$(dirname "$0")/.." # installer/ +OUTDIR="$(pwd)/vendor/linux-x86_64" + +WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT +cd "$WORK" +echo "==> fetching squashfs-tools source" +apt-get source squashfs-tools >/dev/null 2>&1 || { + echo "ERROR: 'apt-get source squashfs-tools' failed - enable deb-src or fetch squashfs-tools 4.x." >&2 + exit 3; } +cd squashfs-tools-*/squashfs-tools + +echo "==> building static (gzip+lzo+xz)" +make clean >/dev/null 2>&1 || true +make GZIP_SUPPORT=1 LZO_SUPPORT=1 XZ_SUPPORT=1 \ + EXTRA_CFLAGS="-static" LDFLAGS="-static" -j"$(nproc)" mksquashfs unsquashfs >/dev/null +strip mksquashfs unsquashfs + +for b in mksquashfs unsquashfs; do + file "$b" | grep -q "statically linked" || { echo "ERROR: $b not static" >&2; exit 5; } +done + +# prove the installer's exact LZO path round-trips before installing +t="$WORK/smoke"; mkdir -p "$t/src"; echo diskos > "$t/src/f" +./mksquashfs "$t/src" "$t/o.sqsh" -comp lzo -b 131072 -noappend >/dev/null 2>&1 +./unsquashfs -d "$t/u" "$t/o.sqsh" >/dev/null 2>&1 +diff -r "$t/src" "$t/u" >/dev/null || { echo "ERROR: LZO round-trip failed" >&2; exit 6; } +echo "==> LZO round-trip OK" + +for b in mksquashfs unsquashfs; do + install -m 0755 "$b" "$OUTDIR/$b" + echo "==> installed $OUTDIR/$b ($(sha256sum "$OUTDIR/$b" | cut -d' ' -f1))" +done diff --git a/build/build-usbboot-static.sh b/build/build-usbboot-static.sh new file mode 100755 index 0000000..8f8fa73 --- /dev/null +++ b/build/build-usbboot-static.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# build-usbboot-static.sh - build a portable, fully-static `usbboot` for Linux. +# +# The distro's libusb-1.0.a is built WITH udev, so linking against it drags in +# libudev + libcap dynamically - which defeats portability (missing/old libudev +# on other distros). This rebuilds libusb with --disable-udev --enable-static so +# usbboot links fully static: no libusb.so, no libudev, no glibc-version pin. +# udev is only needed for hotplug events; usbboot does a one-shot vid/pid open, +# which uses libusb's sysfs backend and works without udev. +# +# Output: installer/vendor/linux-x86_64/usbboot (statically linked, stripped). +# Run on an x86_64 Linux host with build-essential + apt source access. +set -euo pipefail +cd "$(dirname "$0")/.." # installer/ +ROOT=$(pwd) +SRC="$ROOT/src/usbboot/usbboot.c" +OUT="$ROOT/vendor/linux-x86_64/usbboot" +[ -f "$SRC" ] || { echo "ERROR: $SRC missing" >&2; exit 2; } + +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT +cd "$WORK" + +echo "==> fetching libusb source" +apt-get source libusb-1.0 >/dev/null 2>&1 || { + echo "ERROR: 'apt-get source libusb-1.0' failed - enable deb-src or fetch libusb 1.0.x manually." >&2 + exit 3; } +cd libusb-1.0-*/ + +echo "==> configuring libusb (static, no udev)" +./configure --disable-udev --enable-static --disable-shared \ + --disable-examples-build --disable-tests-build CFLAGS="-O2" >/dev/null +echo "==> building libusb" +make -j"$(nproc)" >/dev/null +LIBA=$(find "$PWD" -name libusb-1.0.a | head -1) +[ -f "$LIBA" ] || { echo "ERROR: libusb-1.0.a not produced" >&2; exit 4; } +INC=$(dirname "$LIBA")/../.. ; INC="$PWD/libusb" + +echo "==> linking usbboot (fully static)" +gcc -O2 -std=c99 -I"$INC" -static -o usbboot "$SRC" "$LIBA" -lpthread +strip usbboot +file usbboot | grep -q "statically linked" || { echo "ERROR: not static" >&2; exit 5; } + +install -m 0755 usbboot "$OUT" +echo "==> installed: $OUT" +echo " sha256: $(sha256sum "$OUT" | cut -d' ' -f1)" +echo " $(file -b "$OUT")" +echo "NOTE: confirm a mask-ROM GET_CPU_INFO handshake on a real device before shipping." diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000..84e5fcd --- /dev/null +++ b/build/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# build.sh - produce the standalone `diskos-installer` executable for THIS host +# (Linux or macOS). PyInstaller can't cross-build, so run this on each target OS. +# +# Prereqs: python3. Everything else is created in a local venv; nothing is +# installed system-wide. +# +# Before building, populate vendor/-/ with the native tools for this +# host (see build/README-vendor.md): usbboot, mksquashfs, unsquashfs, +# my_write5_dram.bin, disc_spl_lpddr3.bin (+ lib/ for any bundled .so/.dylib). +set -euo pipefail +cd "$(dirname "$0")/.." # installer/ + +VENV=build/venv +[ -d "$VENV" ] || python3 -m venv "$VENV" +"$VENV/bin/pip" install --quiet --upgrade pip pyinstaller pyusb pycryptodome + +# sanity: the vendor dir for this host must exist +tag=$("$VENV/bin/python" - <<'PY' +import platform +s=platform.system().lower(); o={"darwin":"macos","linux":"linux"}.get(s,s) +m=platform.machine().lower() +a={"x86_64":"x86_64","amd64":"x86_64","arm64":"arm64","aarch64":"arm64"}.get(m,m) +print(f"{o}-{a}") +PY +) +[ -d "vendor/$tag" ] || { echo "ERROR: vendor/$tag missing - add native tools for this host first." >&2; exit 2; } + +"$VENV/bin/pyinstaller" --clean --noconfirm \ + --distpath build/dist --workpath build/work \ + build/diskos-installer.spec + +echo +echo "Built: build/dist/diskos-installer ($(du -h build/dist/diskos-installer | cut -f1))" +echo "Smoke test: build/dist/diskos-installer doctor" diff --git a/build/diskos-installer.spec b/build/diskos-installer.spec new file mode 100644 index 0000000..69bffd5 --- /dev/null +++ b/build/diskos-installer.spec @@ -0,0 +1,70 @@ +# PyInstaller spec - one self-contained diskos-installer executable. +# Bundles the native flashing tools (vendor/-/) and the payload +# (mq_ui + on-device init scripts) as data, plus pyusb. The result needs no +# system Python and no system packages: "remove the tool" = delete this file. +import os + +ROOT = os.path.abspath(os.path.join(os.getcwd())) # run from installer/ +# host tag for the vendor dir to bundle (build on each target OS) +import platform +_sys = platform.system().lower() +_os = {"darwin": "macos", "linux": "linux"}.get(_sys, _sys) +_arch = {"x86_64": "x86_64", "amd64": "x86_64", "arm64": "arm64", + "aarch64": "arm64"}.get(platform.machine().lower(), platform.machine().lower()) +TAG = f"{_os}-{_arch}" + +# files under vendor/ that must NOT be bundled into the app (dev-only backups, notes) +def _skip_vendor(fn): + return fn.endswith(".bak") or fn.endswith(".dynamic.bak") or fn in ("README.md",) + +datas = [] +vend = os.path.join(ROOT, "vendor", TAG) +if os.path.isdir(vend): + for fn in os.listdir(vend): + if _skip_vendor(fn) or os.path.isdir(os.path.join(vend, fn)): + continue + datas.append((os.path.join(vend, fn), f"vendor/{TAG}")) + libdir = os.path.join(vend, "lib") + if os.path.isdir(libdir): + for fn in os.listdir(libdir): + datas.append((os.path.join(libdir, fn), f"vendor/{TAG}/lib")) +payload = os.path.join(ROOT, "payload") +if os.path.isdir(payload): + for fn in os.listdir(payload): + datas.append((os.path.join(payload, fn), "payload")) + +# FAIL the build (don't ship an unusable artifact) if any required native tool or +# payload for this host is missing. +_required_native = ["usbboot", "mksquashfs", "unsquashfs", + "my_write5_dram.bin", "disc_spl_lpddr3.bin"] +_required_payload = ["mq_ui", "S97diskos_install", "S99usbserial", "diskos-debug.sh", "dropbearmulti"] +_missing = [n for n in _required_native if not os.path.exists(os.path.join(vend, n))] +_missing += [f"payload/{n}" for n in _required_payload if not os.path.exists(os.path.join(payload, n))] +if _missing: + raise SystemExit(f"REFUSING to build: missing bundled files for {TAG}: {_missing}. " + f"Populate vendor/{TAG}/ and payload/ first.") + +# pycryptodome loads C submodules dynamically; collect them all so the frozen +# app can AES-decrypt the FiiO OTA chunks. +from PyInstaller.utils.hooks import collect_submodules +hidden = ["usb", "usb.core", "usb.backend.libusb1"] + collect_submodules("Crypto") + +a = Analysis( + [os.path.join(ROOT, "diskos_installer", "__main__.py")], + pathex=[ROOT], + binaries=[], + datas=datas, + hiddenimports=hidden, + hookspath=[], + runtime_hooks=[], + excludes=[], + noarchive=False, +) +pyz = PYZ(a.pure) +# onefile: pass binaries + datas straight into EXE with no COLLECT step. +exe = EXE( + pyz, a.scripts, a.binaries, a.datas, [], + name="diskos-installer", + debug=False, bootloader_ignore_signals=False, strip=False, upx=False, + console=True, +) diff --git a/build/stage-release.sh b/build/stage-release.sh new file mode 100755 index 0000000..a153876 --- /dev/null +++ b/build/stage-release.sh @@ -0,0 +1,114 @@ +#!/bin/bash +# stage-release.sh - assemble the release tarball(s) into build/release/ and generate SHA256SUMS. +# +# The release ships as SOURCE (run with your own Python via ./install.sh) plus the native flash +# tools for the target platform - NOT a bundled onefile binary. Each tarball is self-contained: +# extract it, run ./install.sh once, then ./diskos-installer. +# +# Usage: +# build/stage-release.sh [TAG ...] # default TAG: the host's (e.g. linux-x86_64) +# Build the native tools for each TAG first (Linux: build/build-*-static.sh; macOS: build-macos.sh). +set -euo pipefail +cd "$(dirname "$0")/.." # installer/ +ROOT=$(pwd) +REL="$ROOT/build/release" +mkdir -p "$REL" + +# default to the host platform tag if none given +if [ "$#" -eq 0 ]; then + _os=$(uname -s | tr '[:upper:]' '[:lower:]'); case "$_os" in darwin) _os=macos;; esac + _arch=$(uname -m); case "$_arch" in amd64) _arch=x86_64;; aarch64|arm64) _arch=arm64;; esac + set -- "${_os}-${_arch}" +fi + +# files/dirs that must NEVER go in a public tarball (secrets, FiiO-derived images, build scratch, +# internal docs). Mirrors .gitignore; kept here so staging works even outside a git checkout. +EXCLUer=( + --exclude='./.git' --exclude='./.venv' --exclude='./build/venv' --exclude='./build/work' + --exclude='./build/dist' --exclude='./build/release' --exclude='*/__pycache__/*' + --exclude='__pycache__' --exclude='*.pyc' --exclude='*.pyo' --exclude='*.bak' + --exclude='./signing' --exclude='*.pem' --exclude='*.key' + --exclude='./diskos_dev*.bin' --exclude='./diskos_public*.bin' --exclude='*_recovery.bin' + --exclude='*.squashfs' --exclude='*.sqfs' --exclude='./_retired_unlicensed' + --exclude='./flash/qual_lpddr3.sh' --exclude='./flash/scan_check.sh' + --exclude='./flash/my_write5_scan_dram.bin' --exclude='./flash/usbboot' + --exclude='./mq_ui' --exclude='./S97diskos_install' --exclude='./S99usbserial' + --exclude='./INSTALL.md' --exclude='./mkdiskos.sh' --exclude='./flash/flash_diskos.sh' + --exclude='./flash/extract_stock_rootfs.sh' + --exclude='./PUBLICATION_PLAN.md' --exclude='./BETA_CHECKLIST.md' --exclude='./RELEASE.md' + --exclude='./RELEASE_READINESS.md' --exclude='./UPDATE2_DRAFT.md' --exclude='./UPDATE_ARCHITECTURE.md' + --exclude='./INSTALL_v209_legacy.md' --exclude='./SHA256SUMS' --exclude='*.log' --exclude='*.tmp' + --exclude='.DS_Store' +) + +# native tools + payload that MUST be present for a TAG (fail rather than ship an unusable tarball) +REQ_VENDOR=(usbboot mksquashfs unsquashfs my_write5_dram.bin disc_spl_lpddr3.bin) +REQ_PAYLOAD=(mq_ui S97diskos_install S99usbserial diskos-debug.sh dropbearmulti) + +made=() +for TAG in "$@"; do + echo "== staging $TAG ==" + miss=() + for f in "${REQ_VENDOR[@]}"; do [ -f "vendor/$TAG/$f" ] || miss+=("vendor/$TAG/$f"); done + for f in "${REQ_PAYLOAD[@]}"; do [ -f "payload/$f" ] || miss+=("payload/$f"); done + if [ "${#miss[@]}" -gt 0 ]; then + echo " SKIP $TAG - missing: ${miss[*]}" >&2 + echo " (build the native tools for $TAG first)" >&2 + continue + fi + + # Stage into a temp dir named diskos-installer/, keeping ONLY this TAG's vendor dir, then tar it. + stage=$(mktemp -d) + dest="$stage/diskos-installer" + mkdir -p "$dest" + tar -c "${EXCLUer[@]}" --exclude='./vendor/*' -C "$ROOT" . | tar -x -C "$dest" + mkdir -p "$dest/vendor/$TAG" + cp -a "vendor/$TAG/." "$dest/vendor/$TAG/" + rm -f "$dest/vendor/$TAG/"*.bak 2>/dev/null || true + # macOS build helper that build/build-macos.sh calls - ship it so that path isn't broken + [ -f vendor/setup-macos.sh ] && cp -a vendor/setup-macos.sh "$dest/vendor/setup-macos.sh" + + out="$REL/diskos-installer-$TAG.tar.gz" + tar -czf "$out" -C "$stage" diskos-installer + rm -rf "$stage" + echo " -> $out" + made+=("diskos-installer-$TAG.tar.gz") +done + +[ "${#made[@]}" -gt 0 ] || { echo "nothing staged." >&2; exit 1; } + +echo "== generating SHA256SUMS ==" +( cd "$REL" && sha256sum "${made[@]}" > SHA256SUMS ) && cat "$REL/SHA256SUMS" + +NOTES="$REL/RELEASE_NOTES.md" +[ -f "$NOTES" ] || cat > "$NOTES" <<'EOF' +# diskOS installer - release + +Installer for diskOS on the FiiO Snowsky Disc. Runs from source with your own Python (GUI + CLI). It +builds a diskOS image *from your own official FiiO firmware zip* (FiiO's rootfs is never +redistributed), flashes over mask-ROM with a bad-block-aware verify-every-block writer, and keeps a +checksum-verified stock image so **restore/remove is one action**. + +## What's in the tarball +Source + the native flash tools for the platform. **No bundled Python** - you run it with your own. + +## Setup +``` +tar -xzf diskos-installer-.tar.gz +cd diskos-installer +./install.sh # builds a local .venv, installs pyusb + pycryptodome +./diskos-installer doctor +``` +Needs Python 3.8+. The GUI additionally needs Tk (`apt install python3-tk`); device detection needs +`libusb-1.0` (`apt install libusb-1.0-0`). `install.sh` checks for both and tells you what to install. + +## Install / restore / remove +See the bundled `README.md`. Requires putting the device in mask-ROM (power off, hold Vol-Down, plug +USB). ~60-90 min; normally recoverable via mask-ROM, but not guaranteed. + +## Honest status +Enthusiast flasher. Linux tested end-to-end on real hardware (V2.09 + V2.28). macOS build validation +in progress. Not affiliated with FiiO. +EOF +echo " -> $NOTES" +echo "Done. Attach $REL/* to the GitHub Release." diff --git a/corresponding-source/README.md b/corresponding-source/README.md new file mode 100644 index 0000000..514850f --- /dev/null +++ b/corresponding-source/README.md @@ -0,0 +1,42 @@ +# Corresponding source for the shipped GPL/LGPL native binaries + +The installer ships prebuilt native tools under `vendor/-/`. Some of them are, or +statically link, **GPL-2.0** / **LGPL-2.1** code. To satisfy those licenses this directory carries the +**complete corresponding source** for the exact upstream versions those binaries were built from - so +the source travels **from the same place** as the binaries (GPL-2.0 §3(a) + the "same place" clause +in §3's final paragraph; LGPL-2.1 §6(a)/(d)). + +Each item below is a full Debian source package (`.orig` upstream tarball + `.debian` packaging + +`.dsc`); extract with `dpkg-source -x .dsc` (or unpack the `.orig` tarball directly). + +| Shipped binary | Component here | Version | License | Source files | +|---|---|---|---|---| +| `mksquashfs` / `unsquashfs` | **squashfs-tools** | 4.6.1-1build1 | GPL-2.0 | `squashfs-tools_4.6.1*` | +| (statically linked into the above) | **liblzo2** (lzo2) | 2.10-2build4 | GPL-2.0-or-later | `lzo2_2.10*` | +| `usbboot` | **libusb-1.0** (statically linked) | 1.0.27-1 | LGPL-2.1 | `libusb-1.0_1.0.27*` | +| all three tools (static C runtime) | **glibc** | 2.39-0ubuntu8.8 | LGPL-2.1-or-later | `glibc_2.39*` | +| `usbboot` | its own C source | - | GPL-2.0-or-later | `../src/usbboot/usbboot.c` | +| `disc_spl_lpddr3.bin` | stage-1 SPL (u-boot-xburst) | see `../SPL_SOURCE.md` | GPL-2.0 | `../spl-src/` | + +Also statically linked into `mksquashfs`/`unsquashfs`: **zlib** and **liblzma/xz** - both permissive +(zlib / 0BSD-public-domain), needing attribution only (see `../NOTICE.md`), not corresponding source. + +## Rebuilding the binaries from this source + +The build scripts that produced the shipped binaries are in `../build/`: +- `build/build-squashfs-static.sh` - static `mksquashfs`/`unsquashfs` (gzip+lzo+xz), with an LZO + round-trip self-test. Uses squashfs-tools + liblzo2 above. +- `build/build-usbboot-static.sh` - fully static `usbboot`, rebuilding libusb (above) with + `--disable-udev --enable-static` so no libudev/libcap is pulled in. + +Those scripts fetch the same upstream source via `apt-get source`; the tarballs here pin the exact +versions used so you can rebuild - or substitute your own modified libusb and relink `usbboot` +(the LGPL-2.1 §6 relink path) - without depending on a distribution's source availability. + +## The LGPL-2.1 relink path for libusb (usbboot) + +`usbboot` statically links `libusb-1.0.a`. To exercise your LGPL right to run `usbboot` against a +modified libusb: extract `libusb-1.0_1.0.27*`, modify it, `./configure --disable-udev --enable-static +--disable-shared && make`, then relink with the app source and the same command +`build/build-usbboot-static.sh` uses (`gcc -O2 -std=c99 -I -static -o usbboot +../src/usbboot/usbboot.c -lpthread`). diff --git a/corresponding-source/glibc_2.39-0ubuntu8.8.debian.tar.xz b/corresponding-source/glibc_2.39-0ubuntu8.8.debian.tar.xz new file mode 100644 index 0000000..24c48a0 Binary files /dev/null and b/corresponding-source/glibc_2.39-0ubuntu8.8.debian.tar.xz differ diff --git a/corresponding-source/glibc_2.39-0ubuntu8.8.dsc b/corresponding-source/glibc_2.39-0ubuntu8.8.dsc new file mode 100644 index 0000000..10f30dc --- /dev/null +++ b/corresponding-source/glibc_2.39-0ubuntu8.8.dsc @@ -0,0 +1,94 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +Format: 3.0 (quilt) +Source: glibc +Binary: libc-bin, libc-dev-bin, libc-devtools, libc-l10n, glibc-doc, glibc-source, locales, locales-all, nscd, libc6, libc6-dev, libc6-dbg, libc6-udeb, libc6.1, libc6.1-dev, libc6.1-dbg, libc6.1-udeb, libc0.3, libc0.3-dev, libc0.3-dbg, libc0.3-udeb, libc6-i386, libc6-dev-i386, libc6-sparc, libc6-dev-sparc, libc6-sparc64, libc6-dev-sparc64, libc6-s390, libc6-dev-s390, libc6-amd64, libc6-dev-amd64, libc6-powerpc, libc6-dev-powerpc, libc6-ppc64, libc6-dev-ppc64, libc6-mips32, libc6-dev-mips32, libc6-mipsn32, libc6-dev-mipsn32, libc6-mips64, libc6-dev-mips64, libc6-x32, libc6-dev-x32 +Architecture: any all +Version: 2.39-0ubuntu8.8 +Maintainer: Ubuntu Developers +Uploaders: Clint Adams , Aurelien Jarno , Samuel Thibault +Homepage: https://www.gnu.org/software/libc/libc.html +Standards-Version: 4.6.2 +Vcs-Browser: https://git.launchpad.net/~ubuntu-core-dev/ubuntu/+source/glibc +Vcs-Git: https://git.launchpad.net/~ubuntu-core-dev/ubuntu/+source/glibc +Testsuite: autopkgtest +Testsuite-Triggers: @builddeps@, binutils, fakeroot, gcc, linux-libc-dev +Build-Depends: gettext, dpkg (>= 1.18.7), dpkg-dev (>= 1.17.14), xz-utils, file, quilt, autoconf, gawk, debhelper-compat (= 13), rdfind, symlinks, netbase, gperf, bison, linux-libc-dev (>= 3.9) [linux-any], systemtap-sdt-dev [linux-any], libaudit-dev [linux-any], libcap-dev [linux-any] , libselinux1-dev [linux-any] , mig-for-host (>= 1.8+git20200618-7~) [hurd-any], gnumach-dev (>= 2:1.8+git20200710-2~) [hurd-any], hurd-dev (>= 1:0.9.git20201127-4~) [hurd-any] | hurd-headers-dev [hurd-any], binutils-for-host (>= 2.38), g++-multilib [amd64 i386 mips mipsel mipsn32 mipsn32el mips64 mips64el mipsr6 mipsr6el mipsn32r6 mipsn32r6el mips64r6 mips64r6el powerpc ppc64 s390x sparc sparc64 x32] , g++-x86-64-linux-gnu [amd64] , g++-arc-linux-gnu [arc] , g++-aarch64-linux-gnu [arm64] , g++-arm-linux-gnueabi [armel] , g++-arm-linux-gnueabihf [armhf] , g++-hppa-linux-gnu [hppa] , g++-i686-linux-gnu [i386] , g++-m68k-linux-gnu [m68k] , g++-mips-linux-gnu [mips] , g++-mipsel-linux-gnu [mipsel] , g++-mips64-linux-gnuabin32 [mipsn32] , g++-mips64el-linux-gnuabin32 [mipsn32el] , g++-mips64-linux-gnuabi64 [mips64] , g++-mips64el-linux-gnuabi64 [mips64el] , g++-mipsisa32r6-linux-gnu [mipsr6] , g++-mipsisa32r6el-linux-gnu [mipsr6el] , g++-mipsisa64r6-linux-gnuabin32 [mipsn32r6] , g++-mipsisa64r6el-linux-gnuabin32 [mipsn32r6el] , g++-mipsisa64r6-linux-gnuabi64 [mips64r6] , g++-mipsisa64r6el-linux-gnuabi64 [mips64r6el] , g++-nios2-linux-gnu [nios2] , g++-powerpc-linux-gnu [powerpc] , g++-powerpc64-linux-gnu [ppc64] , g++-powerpc64le-linux-gnu [ppc64el] , g++-riscv64-linux-gnu [riscv64] , g++-sparc-linux-gnu [sparc] , g++-sparc64-linux-gnu [sparc64] , g++-s390x-linux-gnu [s390x] , g++-sh3-linux-gnu [sh3] , g++-sh4-linux-gnu [sh4] , g++-x86-64-linux-gnux32 [x32] , g++-alpha-linux-gnu [alpha] , g++-ia64-linux-gnu [ia64] , python3:native, libidn2-0 (>= 2.0.5~) , libc-bin (>= 2.39) , libgd-dev +Build-Depends-Indep: perl, po-debconf (>= 1.0) +Package-List: + glibc-doc deb doc optional arch=all profile=!stage1 + glibc-source deb devel optional arch=all profile=!stage1 + libc-bin deb libs required arch=any profile=!stage1 essential=yes + libc-dev-bin deb libdevel optional arch=any profile=!stage1 + libc-devtools deb devel optional arch=any profile=!stage1+!stage2 + libc-l10n deb localization standard arch=all profile=!stage1 + libc0.3 deb libs optional arch=hurd-i386,hurd-amd64 profile=!stage1 + libc0.3-dbg deb debug optional arch=hurd-i386,hurd-amd64 profile=!stage1 + libc0.3-dev deb libdevel optional arch=hurd-i386,hurd-amd64 + libc0.3-udeb udeb debian-installer optional arch=hurd-i386,hurd-amd64 profile=!noudeb,!stage1 + libc6 deb libs optional arch=amd64,arc,arm64,armel,armhf,hppa,i386,m68k,mips,mipsel,mipsn32,mipsn32el,mips64,mips64el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el,nios2,powerpc,ppc64,ppc64el,riscv64,sparc,sparc64,s390x,sh3,sh4,x32 profile=!stage1 + libc6-amd64 deb libs optional arch=i386,x32 profile=!stage1,!nobiarch + libc6-dbg deb debug optional arch=amd64,arc,arm64,armel,armhf,hppa,i386,m68k,mips,mipsel,mipsn32,mipsn32el,mips64,mips64el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el,nios2,powerpc,ppc64,ppc64el,riscv64,sparc,sparc64,s390x,sh3,sh4,x32 profile=!stage1 + libc6-dev deb libdevel optional arch=amd64,arc,arm64,armel,armhf,hppa,i386,m68k,mips,mipsel,mipsn32,mipsn32el,mips64,mips64el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el,nios2,powerpc,ppc64,ppc64el,riscv64,sparc,sparc64,s390x,sh3,sh4,x32 + libc6-dev-amd64 deb libdevel optional arch=i386,x32 profile=!nobiarch + libc6-dev-i386 deb libdevel optional arch=amd64,x32 profile=!nobiarch + libc6-dev-mips32 deb libdevel optional arch=mipsn32,mipsn32el,mips64,mips64el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el profile=!nobiarch + libc6-dev-mips64 deb libdevel optional arch=mips,mipsel,mipsn32,mipsn32el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el profile=!nobiarch + libc6-dev-mipsn32 deb libdevel optional arch=mips,mipsel,mips64,mips64el,mipsr6,mipsr6el,mips64r6,mips64r6el profile=!nobiarch + libc6-dev-powerpc deb libdevel optional arch=ppc64 profile=!nobiarch + libc6-dev-ppc64 deb libdevel optional arch=powerpc profile=!nobiarch + libc6-dev-s390 deb libdevel optional arch=s390x profile=!nobiarch + libc6-dev-sparc deb libdevel optional arch=sparc64 profile=!nobiarch + libc6-dev-sparc64 deb libdevel optional arch=sparc profile=!nobiarch + libc6-dev-x32 deb libdevel optional arch=amd64,i386 profile=!nobiarch + libc6-i386 deb libs optional arch=amd64,x32 profile=!stage1,!nobiarch + libc6-mips32 deb libs optional arch=mipsn32,mipsn32el,mips64,mips64el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el profile=!stage1,!nobiarch + libc6-mips64 deb libs optional arch=mips,mipsel,mipsn32,mipsn32el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el profile=!stage1,!nobiarch + libc6-mipsn32 deb libs optional arch=mips,mipsel,mips64,mips64el,mipsr6,mipsr6el,mips64r6,mips64r6el profile=!stage1,!nobiarch + libc6-powerpc deb libs optional arch=ppc64 profile=!stage1,!nobiarch + libc6-ppc64 deb libs optional arch=powerpc profile=!stage1,!nobiarch + libc6-s390 deb libs optional arch=s390x profile=!stage1,!nobiarch + libc6-sparc deb libs optional arch=sparc64 profile=!stage1,!nobiarch + libc6-sparc64 deb libs optional arch=sparc profile=!stage1,!nobiarch + libc6-udeb udeb debian-installer optional arch=amd64,arc,arm64,armel,armhf,hppa,i386,m68k,mips,mipsel,mipsn32,mipsn32el,mips64,mips64el,mipsr6,mipsr6el,mipsn32r6,mipsn32r6el,mips64r6,mips64r6el,nios2,powerpc,ppc64,ppc64el,riscv64,sparc,sparc64,s390x,sh3,sh4,x32 profile=!noudeb,!stage1 + libc6-x32 deb libs optional arch=amd64,i386 profile=!stage1,!nobiarch + libc6.1 deb libs optional arch=alpha,ia64 profile=!stage1 + libc6.1-dbg deb debug optional arch=alpha,ia64 profile=!stage1 + libc6.1-dev deb libdevel optional arch=alpha,ia64 + libc6.1-udeb udeb debian-installer optional arch=alpha,ia64 profile=!noudeb,!stage1 + locales deb localization standard arch=all profile=!stage1 + locales-all deb localization optional arch=any profile=!stage1 + nscd deb admin optional arch=any profile=!stage1 +Checksums-Sha1: + 4b043eaba31efbdfc92c85d062e975141870295e 18520988 glibc_2.39.orig.tar.xz + 2537168c6bdd25c3d001c9bb1d8dbf0db6f865c0 833 glibc_2.39.orig.tar.xz.asc + d22a241cb8ca1a1e842bedc01f29fdf3951ca264 483068 glibc_2.39-0ubuntu8.8.debian.tar.xz +Checksums-Sha256: + f77bd47cf8170c57365ae7bf86696c118adb3b120d3259c64c502d3dc1e2d926 18520988 glibc_2.39.orig.tar.xz + 2cce427ef7933c17379f5514e4f0ccf8dffae5bf8c72f0f7e0bf8b8401f34be5 833 glibc_2.39.orig.tar.xz.asc + ff1aa3bb3eba4f302a99c9b957cacff6d79f26e9b0e663e379bf70a4f39d4283 483068 glibc_2.39-0ubuntu8.8.debian.tar.xz +Files: + be81e87f72b5ea2c0ffe2bedfeb680c6 18520988 glibc_2.39.orig.tar.xz + efe15221b9b609b2c7b96cf5f6743bf0 833 glibc_2.39.orig.tar.xz.asc + 741cfbf2894fca266078acdc1a3cd65a 483068 glibc_2.39-0ubuntu8.8.debian.tar.xz +Original-Maintainer: GNU Libc Maintainers +Original-Vcs-Browser: https://salsa.debian.org/glibc-team/glibc +Original-Vcs-Git: https://salsa.debian.org/glibc-team/glibc.git + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEUMSg3c8x5FLOsZtRZWnYVadEvpMFAmpieQcACgkQZWnYVadE +vpN5wQ/+MZDhJrtPO651+NCFtukjgZCTBr7CiiGYjk1QhUEBuSC2MMfSFALayIFK +Ziu5ZkasPsnn3WfmuT7ZFictCjSJ3jVWfVH/juiXMoCY8vcS+2CUn/XGfX7cfDcP +E+xFFyBsr0EmenV7pFG867sGp4wKjiw1htq8/on5r59yqAnFgWirjPY1dRSSGGGK +DBz5tC+5Ek0ncIPh6EyceW4d34g2+u48v3PlDZXIZXF9T43mHqXuSOnHZ9+uN5+3 +7YonupLm9F5TgH4d8qiqhQaq3mACZMAW8we8WAZqFa2leqYkRfozdzwTwKJCfgFN +cDw14sOQ8GoWjRrO8NkAPzWs2PmXsROgfUvdXa/KGC/ux6n40u7NI9NhfrzhE2B2 +PQpDjrdypViODaPTHQdxX+vQtaVOuqMr+L4zMEKSVw0eR1l6OukarC15ZoRgWZEq +M75weFfLktg4oDmgLv2zb/QmCwQZj1fBCwwRXH6surviEL4+rCzoX7+Xe5ZwQ/jp +Oj1iXrUVT+1Wsq35ZnJXHsmf1t3Vd2RuWrCYZ2kRDVLrKds5k1OsqHOW3xoNQOtq +hR6VCb92DWzNgTgsmhWpUtzBJahondQlilqg+20sJuF7mpZo5QzDS86i+wEuuhUq +46mjJV689J534yLBqZWBXdEuqK+PHQV1IYVZnAXqEA96colxbBM= +=imHV +-----END PGP SIGNATURE----- diff --git a/corresponding-source/glibc_2.39.orig.tar.xz b/corresponding-source/glibc_2.39.orig.tar.xz new file mode 100644 index 0000000..145a888 Binary files /dev/null and b/corresponding-source/glibc_2.39.orig.tar.xz differ diff --git a/corresponding-source/glibc_2.39.orig.tar.xz.asc b/corresponding-source/glibc_2.39.orig.tar.xz.asc new file mode 100644 index 0000000..3f77782 --- /dev/null +++ b/corresponding-source/glibc_2.39.orig.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEcnNUKzmWLfeymZMUFnkrTqJTQPgFAmW6xDIACgkQFnkrTqJT +QPgAAg/9FnW6kXC3eth/BhuzaEEjOmy8BiTSrEHVLWz9veRTuBBYavxH/XUKfLrg +dAFMnASU4DtDUFI2kFWGJIDLk8E/rUau8a0f5Skh4W3VSrc7MDvQhMMt1HggRIqE +O2G7fS6uKZokZElcvEKYrTnq7iwH25K1lvXUKAeaUnVQg6upGYnCe8vuZutuZR9x +OFkewctLM8Canc6wvW0V92Oy+6eZsDq08HCJenFklSh1wz9+MUzyivkv/b6fXK6E +lKGLxpPH7vzfKao6YJBrAHT5raFxdz3yUGzevfeBE9S8UrOYHhsoBRZsaQUWkyKU +A3Gn1ioTkxj8szfgHCrweN6A4Y4MlGgMeQbplQnrjQEfUTVo9N2zkQwWRsM4VGeF +RkbWwpIQg6zMi3BFfizHqAZZWjjWb0wu6mDWmnQBaQ97dN0DAuKJ7cDNmLe6+vOE +OkXRTses8Ta3npAxKjrWNm6WjcrYAzEYLGUT6hLBmZj+WulHmRDPEyuo8H3eihuL +JzJXc7X6O3HntgCqqGrC4yNGtjRF0r3FjZ9Zrv6snEzWHVBnDw/9C5Ss9aZ+VSxO +Uqo0nESWKHtz9UBS73yA/H1k1rMnQS9yeugMoqBil+cJD5xMZETNKFbUGwR8feQ4 +O6w8uH1q70ZwtTVf3l6sbijMeORfcrS0WEErxm8IREmUbqPIMRw= +=uGia +-----END PGP SIGNATURE----- diff --git a/corresponding-source/libusb-1.0_1.0.27-1.debian.tar.xz b/corresponding-source/libusb-1.0_1.0.27-1.debian.tar.xz new file mode 100644 index 0000000..ddf688d Binary files /dev/null and b/corresponding-source/libusb-1.0_1.0.27-1.debian.tar.xz differ diff --git a/corresponding-source/libusb-1.0_1.0.27-1.dsc b/corresponding-source/libusb-1.0_1.0.27-1.dsc new file mode 100644 index 0000000..e5888a5 --- /dev/null +++ b/corresponding-source/libusb-1.0_1.0.27-1.dsc @@ -0,0 +1,49 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +Format: 3.0 (quilt) +Source: libusb-1.0 +Binary: libusb-1.0-0, libusb-1.0-0-dev, libusb-1.0-doc, libusb-1.0-0-udeb +Architecture: linux-any hurd-any all +Version: 2:1.0.27-1 +Maintainer: Aurelien Jarno +Homepage: http://www.libusb.info +Standards-Version: 4.6.2 +Testsuite: autopkgtest +Testsuite-Triggers: autoconf, automake, build-essential, libudev-dev, libumockdev-dev, pkg-config, umockdev +Build-Depends: debhelper-compat (= 13), libudev-dev [linux-any], pkg-config, umockdev , libumockdev-dev +Build-Depends-Indep: doxygen +Package-List: + libusb-1.0-0 deb libs optional arch=linux-any,hurd-any + libusb-1.0-0-dev deb libdevel optional arch=linux-any,hurd-any + libusb-1.0-0-udeb udeb debian-installer optional arch=linux-any,hurd-any profile=!noudeb + libusb-1.0-doc deb doc optional arch=all +Checksums-Sha1: + 7c169047f5970e767d937b598f60979de5f036e3 643680 libusb-1.0_1.0.27.orig.tar.bz2 + ffb7e3e03c672d154bfc9e8aa840cd9c1db656a7 833 libusb-1.0_1.0.27.orig.tar.bz2.asc + 9eee768d76ee4a490beeeade2e17a5cb0b334cb3 17044 libusb-1.0_1.0.27-1.debian.tar.xz +Checksums-Sha256: + ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575 643680 libusb-1.0_1.0.27.orig.tar.bz2 + 1cd22bbfe4ce382ca9b091e2a6275c48f1c776253815cbb615da295ae0bfe687 833 libusb-1.0_1.0.27.orig.tar.bz2.asc + 560bc02e704b8f28b04be3e6a551ccbf2b5c5cb0850864d6a5416ad05723f0b4 17044 libusb-1.0_1.0.27-1.debian.tar.xz +Files: + 1fb61afe370e94f902a67e03eb39c51f 643680 libusb-1.0_1.0.27.orig.tar.bz2 + be3b4265eb0ba77ea2a4bbe78539e1d7 833 libusb-1.0_1.0.27.orig.tar.bz2.asc + 9ab0e4b8a46ea6c3456343bba278398a 17044 libusb-1.0_1.0.27-1.debian.tar.xz + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEUryGlb40+QrX1Ay4E4jA+JnoM2sFAmW75icACgkQE4jA+Jno +M2vR2g/8CAvEt2Ea1iT/cc/HmAm6InO5RdG82FbqpQABSK9JNgN+EuuIA861GfSf +jCjrLE2B55oioAKSx6zKxadAC59dP9VdFMmfTBvfpDJrYlXyYLlQpKDUrok23nT2 +C9w2EI7lnQLpqbuuefxXzpmTqoGja1pSsX8BSmQxb2oqo8W516YPswItBhY7v6WQ +yBILaPGlrdUibqxIw0Qw+jbbi4VT1XIE7l+30XtZ7NK5nvBTVK83vi1OU7+yjPop +tVoFp99LWQS3M3q6LK94UtRYpi2XQqL+0ZgoomhkZZ/NUOzDehGguiyLUBlvv3yD +yq4DQsi8EmaWQzuofqtEO4BpdMZT2A3pZ17LU3LBx4AmHSdrSiJIK/nXMWtn3r+1 +qUsFzMQi0DufpyYCU+JUOvLoSg+v1ab42/yupvDzpbp0gQcO72/av+c8Y5u6XAIC +fZSmCast8kqLsFY9GACEe5def3Ua2UT9M5Hm6fMoR33+AQJhcURPVC/qzeykEoSP +K2Uq5PAO0qJa2frGD0Op3vyfpETIjWXylj725tGFLw87+Ckug5tYyGPh/rZ5Oh/k +1xRVApEeiu6ppsis5cJwo6yXSewCS0aqo71KGdurcR4/kqoRa+DjZJqzdkOh+cSF +ClCWGsD3BAL8fHB9vrkeWsuv0PvQ0EItXX0fmBQdlc/AprzMqCk= +=Uhhm +-----END PGP SIGNATURE----- diff --git a/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2 b/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2 new file mode 100644 index 0000000..9c5f6ba Binary files /dev/null and b/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2 differ diff --git a/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2.asc b/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2.asc new file mode 100644 index 0000000..5ae1286 --- /dev/null +++ b/corresponding-source/libusb-1.0_1.0.27.orig.tar.bz2.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEnH6pSTnGnE+8Pb+oqgY5B577YbkFAmW6xoMACgkQqgY5B577 +YbmEWg/9Fp2VLICEVOIUYT5dMQt9d3nRf6hSNHepUj8sSq2F7AYDWLTMQjhU1JZg +0uuLqcBMDjRQL22L5AVoC/Gfyg8Xe2fWGFKWOFEqOeK6wz+zGVvzOI5Wq58fqgLG +z3aIuvMLU3YflGQGfKhNo4O1RRWtt18mI81vJWL6iKN2FJeDSQ/fpqrgVYjk87IX +XpkE4ukzb5xJ97iORfZxHIvE20Y00wTWloMm/dGnhh9c+Wxa3tKwXL3H4g+pQjli +SIEzYNFKXXic2U6jgAdlSw8pll4VPoALQAmrYD4Bb7uR0KYD3PL0Ln2rXWB252+T +ZGGS6kQFN/Ju6wz36RqbYfzVf3GxdlPagBYyZx/UQgj7r7HuvxhS5FaBaWr0U5e6 +D7bQZDwqc7WoDEpGAkAkdCBp/qs9JWbWW8CgTKkLPcnT+oZDOJxlj3ADoDivS91c +QBDg28Wia7BWMesDgWYlvMsDNd6zSTPcQLpiJjb7wBdlYuU9ELNgVziwlz8vEyt2 +4U0Ec7180nJpzn6BpabHx1V2NEMXIwOqBjC8zisjLluRi7S8Vj/Ts6DDyw9SGqN7 +ncTeSEU+LK2a9nvsIaXIyZ/N+0sic7rdjWbqHcCtneQ1QdqjFtsAXW48AbG6vJvY +sYh610vVqHXGlwbJlzO4HnhoAPAKbFxGsXgZa1YTATGN+4TO2QE= +=e8dx +-----END PGP SIGNATURE----- diff --git a/corresponding-source/lzo2_2.10-2build4.debian.tar.xz b/corresponding-source/lzo2_2.10-2build4.debian.tar.xz new file mode 100644 index 0000000..ac252df Binary files /dev/null and b/corresponding-source/lzo2_2.10-2build4.debian.tar.xz differ diff --git a/corresponding-source/lzo2_2.10-2build4.dsc b/corresponding-source/lzo2_2.10-2build4.dsc new file mode 100644 index 0000000..51f7ccb --- /dev/null +++ b/corresponding-source/lzo2_2.10-2build4.dsc @@ -0,0 +1,46 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +Format: 3.0 (quilt) +Source: lzo2 +Binary: liblzo2-dev, liblzo2-2, liblzo2-2-udeb +Architecture: any +Version: 2.10-2build4 +Maintainer: Ubuntu Developers +Homepage: https://www.oberhumer.com/opensource/lzo/ +Standards-Version: 4.5.0 +Vcs-Browser: https://salsa.debian.org/debian/lzo2 +Vcs-Git: https://salsa.debian.org/debian/lzo2.git +Build-Depends: debhelper-compat (= 12), pkg-config +Package-List: + liblzo2-2 deb libs optional arch=any + liblzo2-2-udeb udeb debian-installer optional arch=any + liblzo2-dev deb libdevel optional arch=any +Checksums-Sha1: + 4924676a9bae5db58ef129dc1cebce3baa3c4b5d 600622 lzo2_2.10.orig.tar.gz + 9d219fb0dfdf031f2cb0fda60d26a23cb2163530 7120 lzo2_2.10-2build4.debian.tar.xz +Checksums-Sha256: + c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072 600622 lzo2_2.10.orig.tar.gz + 3115ad74a2495f0ca5f7f0e8761b6c3c81e14d19abd9d7b47c7fc020091fcb47 7120 lzo2_2.10-2build4.debian.tar.xz +Files: + 39d3f3f9c55c87b1e5d6888e1420f4b5 600622 lzo2_2.10.orig.tar.gz + 26aeb09697919dcf8ba93508f6c9c4c2 7120 lzo2_2.10-2build4.debian.tar.xz +Original-Maintainer: Stephen Kitt + +-----BEGIN PGP SIGNATURE----- + +iQJHBAEBCgAxFiEET7WIqEwt3nmnTHeHb6RY3R2wP3EFAmYUF6MTHGp1bGlhbmtA +dWJ1bnR1LmNvbQAKCRBvpFjdHbA/cYOhD/0bsopVacb07CzRBODJRwgSUJnrtov2 +031RHKNrCYK+d1SoVkTtIr0XEwzgH1t7caVcfD2GaYl0sNwikVEyvNF1EEh1aG4n +Wb+h3YNJBqS2hBgKdQnTcnY9booE9B0Xw+o6He9AxM9go9xcng4YUC59w8P0JFn7 +9i5rnV55eJjrWpCdV7UXwBh/1nqnsyVyXc0HuyjHg0Nroad5AyvSXCtNpWwZW99Z +VL8GkWqs2Hkb4fu8nOPBIBR+nwuCp+tBEqH3Wl3Ph6sHX7WVDqVdcuPvF5JHyd0y +PaStokpxAlwYp6kILuxJ4UUZAmAv8c+KE4uhvVxV5ymv9CzCffBc7/4sFyiH5+7S +mKCVDfG2aCTRENJTZBp5KOZMHDpuPVfVKw6Wpn7sZVQuooo6CBhiOneWIjeB/X0x +ZO/Vq5qtNSBRkQjCPSZGLR2wdqON/OjK65oA/UXlBnP+NH/20ivknbpsrwowgYgW +dZmXKjou4M9CqCc7zTf4N/l7NKv8TixQcSOVOGpTnc3/ORAoIZZz9M7V2Wg0ezlF +PQ5O1+CC8k8EL3QRY8ZF+S/p0oJWvZ55Qrm2tevqj/u79rFAgQi2xyWJyjMU8noU +JVyXdifyRrxWv5SR7ltOdKloK7iqIjYHcJiIauIeIASeWQrw2V9MChUxxEoAhBhn +amHCUqiG5/H2wg== +=dcLM +-----END PGP SIGNATURE----- diff --git a/corresponding-source/lzo2_2.10.orig.tar.gz b/corresponding-source/lzo2_2.10.orig.tar.gz new file mode 100644 index 0000000..f379058 Binary files /dev/null and b/corresponding-source/lzo2_2.10.orig.tar.gz differ diff --git a/corresponding-source/squashfs-tools_4.6.1-1build1.debian.tar.xz b/corresponding-source/squashfs-tools_4.6.1-1build1.debian.tar.xz new file mode 100644 index 0000000..099b6dc Binary files /dev/null and b/corresponding-source/squashfs-tools_4.6.1-1build1.debian.tar.xz differ diff --git a/corresponding-source/squashfs-tools_4.6.1-1build1.dsc b/corresponding-source/squashfs-tools_4.6.1-1build1.dsc new file mode 100644 index 0000000..889351f --- /dev/null +++ b/corresponding-source/squashfs-tools_4.6.1-1build1.dsc @@ -0,0 +1,42 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +Format: 3.0 (quilt) +Source: squashfs-tools +Binary: squashfs-tools +Architecture: linux-any kfreebsd-any +Version: 1:4.6.1-1build1 +Maintainer: Ubuntu Developers +Homepage: https://github.com/plougher/squashfs-tools +Standards-Version: 4.6.2 +Build-Depends: debhelper-compat (= 13), libattr1-dev, liblzma-dev, liblzo2-dev, liblz4-dev, zlib1g-dev, libzstd-dev, help2man +Package-List: + squashfs-tools deb kernel optional arch=linux-any,kfreebsd-any +Checksums-Sha1: + daad956aa77ccddf5df20db6a2eebcc4a226cab5 286848 squashfs-tools_4.6.1.orig.tar.gz + fd8e9ac4845148e6b48c6f38dc27d54ac3a02e25 10376 squashfs-tools_4.6.1-1build1.debian.tar.xz +Checksums-Sha256: + 94201754b36121a9f022a190c75f718441df15402df32c2b520ca331a107511c 286848 squashfs-tools_4.6.1.orig.tar.gz + e0774865d7d9988633534276139f199ab4db59109f9ee1a5355eb9c1e5b300e1 10376 squashfs-tools_4.6.1-1build1.debian.tar.xz +Files: + db23a40fa0dc54b4d6d225fb20ee6555 286848 squashfs-tools_4.6.1.orig.tar.gz + 8b71b3fa5c85a74aa967f0ffc4f795dc 10376 squashfs-tools_4.6.1-1build1.debian.tar.xz +Original-Maintainer: Laszlo Boszormenyi (GCS) + +-----BEGIN PGP SIGNATURE----- + +iQJHBAEBCgAxFiEET7WIqEwt3nmnTHeHb6RY3R2wP3EFAmYUGWYTHGp1bGlhbmtA +dWJ1bnR1LmNvbQAKCRBvpFjdHbA/cZkHEACNuqQFoqN+q3SjLFRyHvoQpgnuCgMF +zdtmvovEK/4pnEOdY1sGmjL7OlPi77/0TeQmLp4NKUOOHleM7L1N1Ho6Kjy81RU2 +mkgaZ4D0IK0ttEuRPFQZTSs6lIGgmGvZmnxvwM/QYODrj09F1PFeNeW7vO9rmTWI +FitEgsb8ZQotLGiyM1YZWo1P4bIcSXzjy4gZEKGJ22DPXS3K4U0xSdijKMHffaK+ +W3a1bIkqdy58VInDKwFaeN0/qSmISJomTLFsZMrlMDXpIZpQudr/dzJEh9yMSjYw +SoAgFsCf4o8jI4MXz1JvxzUFo8QZpigdahg9JW70M0evnHHU5cJl68Iz/4tUpQZH +ER2Lmd9lRttanZp/ltj+jEDQzPXYp0fS+YHdaQNJdhNh6b1fiJWwxiJb2IDKeGGV +7F08rI48N26jdxb9RllJM8qEa7jGN7yEWYvia0TKWMRG6WAm432tC1smjGcpDUmL +rVqErVoyiacraWU382jrS0HYLX27p1GefQ+Fs8tjrGQ6Cban3bsgkEmYeLd/jD8f +8BCXVvTqHipfjtL8ielSNz2ULpwTdd+/1LqsMbb7XOWJgGIv0+NWNvfrxYLCQEdM +oUsuem+fl01KceXXCa/9xHjpnVoU7QBfZvUBfk39cjkHG/MvCcuT1Y6Z0NJB/dUv +0/vsx4OAD+5qVg== +=NebR +-----END PGP SIGNATURE----- diff --git a/corresponding-source/squashfs-tools_4.6.1.orig.tar.gz b/corresponding-source/squashfs-tools_4.6.1.orig.tar.gz new file mode 100644 index 0000000..fdffbc5 Binary files /dev/null and b/corresponding-source/squashfs-tools_4.6.1.orig.tar.gz differ diff --git a/diskos-installer b/diskos-installer new file mode 100755 index 0000000..34eb131 --- /dev/null +++ b/diskos-installer @@ -0,0 +1,21 @@ +#!/bin/sh +# diskOS installer launcher. Runs the installer from source using the local .venv (created by +# ./install.sh); falls back to the system python3 if no venv is present. Pass any subcommand +# straight through, e.g. ./diskos-installer doctor or ./diskos-installer gui +set -eu +HERE=$(CDPATH= cd "$(dirname "$0")" && pwd) + +if [ -x "$HERE/.venv/bin/python" ]; then PY="$HERE/.venv/bin/python" +elif [ -x "$HERE/.venv/Scripts/python.exe" ]; then PY="$HERE/.venv/Scripts/python.exe" +elif command -v python3 >/dev/null 2>&1; then PY=$(command -v python3) +elif command -v python >/dev/null 2>&1; then PY=$(command -v python) +else + echo "diskos-installer: no Python 3 found. Run ./install.sh first." >&2 + exit 1 +fi + +# Make the diskos_installer package importable regardless of the caller's working directory, +# without changing it (so relative --firmware paths still resolve against where the user is). +PYTHONPATH="$HERE${PYTHONPATH:+:$PYTHONPATH}" +export PYTHONPATH +exec "$PY" -m diskos_installer "$@" diff --git a/diskos_installer/__init__.py b/diskos_installer/__init__.py new file mode 100644 index 0000000..dd423c7 --- /dev/null +++ b/diskos_installer/__init__.py @@ -0,0 +1,12 @@ +"""diskOS installer - cross-platform (Linux/macOS) installer for the FiiO Snowsky Disc. + +The whole tool is Python; the two bricking-sensitive operations (the Ingenic +mask-ROM USB flash and squashfs packing) delegate to PROVEN native binaries that +ship alongside this package (see ``bundle.py``) rather than being reimplemented. + +Run from source with your own Python: ``./install.sh`` creates a local virtualenv and +installs the two pip dependencies (pyusb, pycryptodome), then ``./diskos-installer`` +runs it. The GUI additionally needs the system's Tk; the flash step needs libusb-1.0. +""" + +__version__ = "1.0.0" diff --git a/diskos_installer/__main__.py b/diskos_installer/__main__.py new file mode 100644 index 0000000..5c2b2d5 --- /dev/null +++ b/diskos_installer/__main__.py @@ -0,0 +1,220 @@ +"""diskOS installer CLI. + +Commands: + doctor Report host, bundled tools, device presence, and saved state. + install Build a diskOS image from YOUR stock firmware and flash it. + restore-stock Deactivate diskOS: reflash your saved stock-rootfs image (leaves /usr/data files). + remove Delete everything this tool created (its full uninstall footprint). + +Design: the whole tool is Python; the bricking-sensitive steps delegate to bundled +proven native binaries. Nothing is installed system-wide on Linux/macOS. +""" + +import argparse +import fcntl +import os +import sys +import tempfile + +from diskos_installer import (__version__, bundle, errors, flasher, imagebuild, + platform_probe, service, state, ui) +from diskos_installer.reporter import CLIReporter + + +# --- single-run lock so two installers can't touch the ONE physical device at once. It must be +# SHARED across all users on the host (a GUI as your user and a CLI under sudo must not both flash), +# so it lives at a FIXED /tmp path - NOT per-user. Advisory flock (auto-released on death -> no +# stale-lock hazard). Opened READ-ONLY so any user can take the flock on a 0644 file, and O_NOFOLLOW +# so a planted symlink can't redirect the open (we bail instead of following it). --- +class _Lock: + def __init__(self): + # A FIXED absolute path, not tempfile.gettempdir(): $TMPDIR differs between a normal run and a + # sudo run (and per-user on macOS), which would hand them SEPARATE locks and defeat the whole + # point. /tmp is world-accessible on every supported host (Linux/macOS); Windows is unsupported. + self.path = "/tmp/diskos-installer.lock" + self.fd = None + + def __enter__(self): + import stat as _stat + flags = os.O_CREAT | os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_CLOEXEC", 0) + try: + self.fd = os.open(self.path, flags, 0o644) + except OSError as e: + raise SystemExit(ui.red( + f"could not open the run-lock at {self.path} ({e}). If it exists as a symlink, " + "remove it and retry.")) + # A planted symlink is refused by O_NOFOLLOW above; also refuse a non-regular file. + if not _stat.S_ISREG(os.fstat(self.fd).st_mode): + os.close(self.fd); self.fd = None + raise SystemExit(ui.red(f"run-lock {self.path} is not a regular file - refusing.")) + try: + fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError: + os.close(self.fd) + self.fd = None + raise SystemExit(ui.red( + "another diskOS installer is already running. Close it and retry " + "(only one may touch the device at a time).")) + return self + + def __exit__(self, *exc): + try: + if self.fd is not None: + fcntl.flock(self.fd, fcntl.LOCK_UN) + os.close(self.fd) + except OSError: + pass + return False + + +def _set_phase(st, phase): + st["phase"] = phase + state.save(st) + + +def cmd_doctor(args): + ui.step("diskOS installer - doctor") + o, a = platform_probe.host() + ui.info(f"version : {__version__}") + ui.info(f"host : {o}-{a} ({'supported' if platform_probe.is_supported() else 'UNSUPPORTED'})") + ui.info(f"state dir : {state.state_dir()}") + + ui.info("bundled tools:") + all_ok = True + for name in ("usbboot", "mksquashfs", "unsquashfs", "my_write5_dram.bin", "disc_spl_lpddr3.bin"): + p = bundle.native(name, required=False) + (ui.ok if p else ui.err)(f" {name}: {p or 'MISSING'}") + all_ok = all_ok and bool(p) + for name in ("mq_ui", "S97diskos_install", "S99usbserial", "diskos-debug.sh", "dropbearmulti"): + p = bundle.data(name, required=False) + (ui.ok if p else ui.err)(f" {name}: {p or 'MISSING'}") + all_ok = all_ok and bool(p) # payload files are required to build a flashable image + + n = platform_probe.maskrom_count() + if n < 0: + ui.warn("device: cannot enumerate USB (pyusb/lsusb unavailable)") + elif n == 0: + ui.info("device: none in mask-ROM mode (normal unless you're about to flash)") + else: + (ui.ok if n == 1 else ui.warn)(f"device: {n} in mask-ROM mode") + + st = state.load() + if st.get("installed"): + ui.info(f"diskOS installed via this tool: yes (variant={st.get('variant')}, at {st.get('installed_at')})") + else: + ui.info("diskOS installed via this tool: no record") + ui.info(f"saved bone-stock image (for restore): {'yes' if state.have_stock_image() else 'no'}") + return 0 if all_ok else 1 + + +def _cli_confirm(yes): + """Build a service `confirm` callback for the CLI (prints the summary, then + y/N - or auto-yes with --yes).""" + def confirm(summary): + ui.step("Ready - please confirm") + for k in ("action", "variant", "image", "duration", "consequence"): + if summary.get(k): + ui.info(f" {k}: {summary[k]}") + if yes: + return True + return ui.confirm("Proceed?", default=False) + return confirm + + +def cmd_install(args): + if not platform_probe.is_supported(): + ui.err(f"[E101] unsupported host {platform_probe.host_tag()} - Linux/macOS only for now") + return 2 + if not args.firmware and not args.stock: + ui.err("[E140] need --firmware (or --stock ).") + return 2 + params = {"firmware": args.firmware, "stock": args.stock, + "ui_binary": args.ui, "variant": args.variant} + r = service.do_install(params, CLIReporter(), _cli_confirm(args.yes)) + if r.get("ok"): + ui.info("The UI is embedded in the flashed image - just reboot the device and it installs") + ui.info("diskOS automatically on first boot (no microSD step needed).") + ui.info("To deactivate diskOS later (reflash your saved stock rootfs): diskos-installer restore-stock") + return 0 + return 3 if r.get("aborted") else 1 + + +def cmd_restore_stock(args): + r = service.do_restore({"firmware": args.firmware}, CLIReporter(), _cli_confirm(args.yes)) + return 0 if r.get("ok") else (3 if r.get("aborted") else 1) + + +def cmd_remove(args): + def confirm(summary): + ui.warn(summary.get("consequence", "")) + return args.yes or ui.confirm("Delete the installer's files anyway?", default=False) + r = service.do_remove({"force": args.force}, CLIReporter(), confirm) + if r.get("ok"): + if getattr(sys, "frozen", False): + ui.info(f"To finish: delete this executable ({sys.executable}).") + else: + ui.info(f"To finish: delete the installer folder ({bundle.resource_root()}).") + ui.info("Nothing was installed system-wide, so there is nothing else to clean up.") + return 0 + if r.get("errors"): + return 1 # partial-removal errors already reported by the service + ui.info("aborted; nothing removed.") + return 3 + + +def build_parser(): + p = argparse.ArgumentParser(prog="diskos-installer", + description="Standalone diskOS installer for the FiiO Snowsky Disc.") + p.add_argument("--version", action="version", version=f"diskos-installer {__version__}") + # no subcommand -> GUI (running ./diskos-installer with no arguments); subcommands = CLI + sub = p.add_subparsers(dest="cmd", required=False) + + sub.add_parser("gui", help="launch the graphical installer (also the default with no arguments)") + + d = sub.add_parser("doctor", help="report host, bundled tools, device, and state") + d.set_defaults(func=cmd_doctor) + + i = sub.add_parser("install", help="build from YOUR stock firmware and flash diskOS") + i.add_argument("--firmware", help="FiiO official update .zip (your stock firmware)") + i.add_argument("--stock", help="pre-extracted stock rootfs.squashfs (instead of --firmware)") + i.add_argument("--ui", help="diskOS UI binary (default: bundled mq_ui)") + i.add_argument("--variant", choices=["public", "dev"], default="public", + help="public (no always-on shell; Debug Mode enables SSH on demand) or dev (adds an ALWAYS-ON PASSWORDLESS ROOT SHELL over " + "USB - anyone with physical access gets root every boot; dev devices only)") + i.add_argument("-y", "--yes", action="store_true", help="don't prompt before flashing") + i.set_defaults(func=cmd_install) + + r = sub.add_parser("restore-stock", help="deactivate diskOS (reflash your saved stock rootfs)") + r.add_argument("--firmware", help="FiiO update .zip (only needed if no stock image is saved)") + r.add_argument("-y", "--yes", action="store_true", help="don't prompt before flashing") + r.set_defaults(func=cmd_restore_stock) + + rm = sub.add_parser("remove", help="delete the installer and everything it created") + rm.add_argument("-y", "--yes", action="store_true", help="don't prompt") + rm.add_argument("--force", action="store_true", help="remove even if diskOS is still on the device") + rm.set_defaults(func=cmd_remove) + return p + + +def main(argv=None): + args = build_parser().parse_args(argv) + # no subcommand, or 'gui' -> launch the graphical installer (under the lock) + if getattr(args, "cmd", None) in (None, "gui"): + from diskos_installer import gui + with _Lock(): + return gui.main() + try: + if args.cmd == "doctor": # doctor is read-only; no lock needed + return args.func(args) + with _Lock(): + return args.func(args) + except errors.DiskOSError as e: # any coded installer error (Build/Flash/Preflight) + ui.err(str(e)) + return 1 + except KeyboardInterrupt: + ui.err("interrupted.") + return 130 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/diskos_installer/bundle.py b/diskos_installer/bundle.py new file mode 100644 index 0000000..b7b5062 --- /dev/null +++ b/diskos_installer/bundle.py @@ -0,0 +1,85 @@ +"""Locate the bundled native binaries and data files. + +Layout (in the source tree AND inside the PyInstaller bundle): + /vendor/-/ usbboot, mksquashfs, unsquashfs, + my_write5_dram.bin, disc_spl_lpddr3.bin, + lib/ (bundled .so/.dylib for usbboot) + /payload/ mq_ui, S97diskos_install, S99usbserial, + diskos_manifest templates, etc. + +When frozen by PyInstaller, data is unpacked under sys._MEIPASS; in the source +tree it sits next to this package. `resource_root()` resolves both.""" + +import os +import stat +import sys + +from . import platform_probe + + +def resource_root(): + """Directory that contains vendor/ and payload/. + + - Frozen (PyInstaller): sys._MEIPASS. + - Source tree: the installer/ dir (parent of this package).""" + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + return sys._MEIPASS + # this file is installer/diskos_installer/bundle.py -> installer/ + return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def vendor_dir(): + return os.path.join(resource_root(), "vendor", platform_probe.host_tag()) + + +def payload_dir(): + return os.path.join(resource_root(), "payload") + + +def _ensure_exec(path): + try: + st = os.stat(path) + os.chmod(path, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + except OSError: + pass + return path + + +def native(name, required=True): + """Absolute path to a bundled native binary for this host, made executable. + Raises FileNotFoundError if required and missing.""" + p = os.path.join(vendor_dir(), name) + if os.path.exists(p): + return _ensure_exec(p) + if required: + from .errors import PreflightError + raise PreflightError( + f"bundled tool '{name}' not found for {platform_probe.host_tag()}", + code="E102", + action="this build may not include binaries for your platform - " + "re-download the correct build") + return None + + +def data(name, required=True): + """Absolute path to a bundled data/payload file.""" + p = os.path.join(payload_dir(), name) + if os.path.exists(p): + return p + if required: + from .errors import PreflightError + raise PreflightError(f"bundled payload '{name}' not found", code="E102", + action="the install is incomplete - re-download the installer") + return None + + +def native_env(): + """Environment for running a bundled native binary, with its private lib dir + on the loader path (so a bundled libusb is found without touching the system).""" + env = dict(os.environ) + libdir = os.path.join(vendor_dir(), "lib") + if os.path.isdir(libdir): + o, _ = platform_probe.host() + var = "DYLD_LIBRARY_PATH" if o == "macos" else "LD_LIBRARY_PATH" + env[var] = libdir + (os.pathsep + env[var] if env.get(var) else "") + return env diff --git a/diskos_installer/errors.py b/diskos_installer/errors.py new file mode 100644 index 0000000..ee8b661 --- /dev/null +++ b/diskos_installer/errors.py @@ -0,0 +1,63 @@ +"""Stable, user-quotable error codes for the diskOS installer. + +Every user-facing failure carries a short code a beta tester can quote from a +screenshot so we can triage it instantly. Namespaces: + + E1xx environment / preflight (nothing written to the device yet) + E2xx firmware extract & image build + E3xx flash orchestration (host side) + F1xx device writer result (mirrors the on-device dbg[16] codes) + +An exception's str() renders as: "[E301] - " +so existing `rep.error(str(e))` call sites show the code with no other change. +""" + + +class DiskOSError(Exception): + """Base installer error carrying a stable `code` and an optional `action` + (a short, plain-language "what to do next").""" + code = "E000" + + def __init__(self, message, code=None, action=None): + self.message = message + if code: + self.code = code + self.action = action + super().__init__(self.render()) + + def render(self): + s = f"[{self.code}] {self.message}" + if self.action: + s += f" - {self.action}" + return s + + +class PreflightError(DiskOSError): + """E1xx - environment/preflight; the device has NOT been touched.""" + code = "E100" + + +class BuildError(DiskOSError): + """E2xx - firmware extraction / diskOS image build.""" + code = "E200" + + +class FlashError(DiskOSError): + """E3xx - flash orchestration (host side). F0xx = device-writer verdicts.""" + code = "E300" + + +# Device-writer result codes (from my_write5.c dbg[16]) → stable F-codes for the user. +DEVICE_RESULT_CODES = { + 0x600DF10C: ("F001", "SUCCESS"), + 0xDEAD0001: ("F101", "ABORT init/ECC"), + 0xDEAD0002: ("F102", "ABORT out-of-space"), + 0xDEAD0003: ("F103", "ABORT persistent block-write fail"), + 0xDEAD0004: ("F104", "ABORT too many bad blocks"), + 0xDEAD0005: ("F105", "ABORT ECC re-enable failed"), + 0xDEAD0006: ("F106", "ABORT bad-block marker unreadable"), +} + +RECOVERABLE = ("The device is normally recoverable via mask-ROM (not guaranteed for every unit " + "or failure): power the device OFF, hold Volume-Down, plug in USB to return to " + "mask-ROM, and re-flash your saved stock image or diskOS.") diff --git a/diskos_installer/flasher.py b/diskos_installer/flasher.py new file mode 100644 index 0000000..acbb127 --- /dev/null +++ b/diskos_installer/flasher.py @@ -0,0 +1,273 @@ +"""Mask-ROM USB flashing - Python wrapper around the PROVEN native binaries +(usbboot + the my_write5 DRAM NAND writer + the X2000 SPL). We do NOT reimplement +the flashing protocol; we orchestrate it, watch it, and interpret the result. + +Faithful port of flash_diskos.sh, with the python helper snippets (poison blob, +debug-struct parse) folded in natively, plus honest progress and fail-closed +result interpretation. +""" + +import os +import shutil +import signal +import struct +import subprocess +import tempfile + +from . import bundle, platform_probe +from .reporter import CLIReporter +from .errors import FlashError, DEVICE_RESULT_CODES, RECOVERABLE + +IMG_SIZE = 76021760 +SQUASH_MAGIC = b"hsqs" + +RESULT_NAMES = {code: name for code, (_fcode, name) in DEVICE_RESULT_CODES.items()} + +FLASH_EXPECT_SECS = 75 * 60 # ~60-90 min; expected writer duration +# Hard ceiling for the whole usbboot invocation: the writer's own --wait is 5400s (90 min); +# give it that plus margin for the image download + result readback, then treat a still-running +# usbboot as a hung/reset device and terminate it (E303) rather than blocking forever. +FLASH_HARD_TIMEOUT_SECS = 5400 + 20 * 60 # 90 min writer + 20 min margin + + +def _probe_helpers(): + """Confirm every bundled native tool can actually execute (and load its libs) + BEFORE the destructive gate. Catches a noexec temp mount / missing dylibs while + the device is still untouched. Non-zero exit is fine; an OSError is not.""" + probes = [(bundle.native("usbboot"), ["--help"]), + (bundle.native("mksquashfs"), ["-version"]), + (bundle.native("unsquashfs"), ["-version"])] + for path, args in probes: + try: + subprocess.run([path] + args, env=bundle.native_env(), + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + timeout=15) + except OSError as e: + raise FlashError( + f"bundled helper '{os.path.basename(path)}' cannot execute ({e})", + code="E103", + action="the tool's directory may be on a noexec mount, or a required library is " + "missing; move the installer to an exec-capable filesystem and retry") + except subprocess.TimeoutExpired: + pass # it started (that's all we needed to prove) + + +def preflight(image_path, rep=None): + """Validate the image and that exactly one device is in mask-ROM. Fail closed.""" + rep = rep or CLIReporter() + if not os.path.exists(image_path): + raise FlashError(f"image not found: {image_path}", code="E120") + sz = os.path.getsize(image_path) + if sz != IMG_SIZE: + raise FlashError(f"wrong image size {sz} (must be {IMG_SIZE})", code="E121", + action="use a diskOS image built by this installer") + with open(image_path, "rb") as f: + if f.read(4) != SQUASH_MAGIC: + raise FlashError("image is not a squashfs (bad magic) - not a diskOS/stock image", code="E122") + + _probe_helpers() # every bundled tool must actually execute (catches noexec /tmp, missing libs) + + n = platform_probe.maskrom_count() + if n == 0: + raise FlashError( + "no device in mask-ROM mode", code="E110", + action="power the device OFF, hold Volume-Down, plug in USB (screen stays " + "black), then retry") + if n > 1: + raise FlashError(f"{n} devices in mask-ROM mode - need exactly 1", code="E111", + action="unplug the other Ingenic devices") + if n < 0: + # FAIL CLOSED: we could not prove exactly one device (no libusb backend or a + # permission error). Never cross the destructive gate on an unproven count. + raise FlashError( + "cannot confirm exactly one device (USB enumeration failed - missing libusb " + "backend or insufficient USB permissions)", code="E112", + action="refusing to flash on an unproven device count; fix USB access and retry") + + +def _parse_debug(dbg_path): + """Parse the 1KB little-endian debug readback (256 x uint32). Returns a dict. + Mirrors the field offsets in flash_diskos.sh.""" + with open(dbg_path, "rb") as f: + raw = f.read(1024) + if len(raw) < 1024: + raise FlashError(f"short debug readback ({len(raw)} bytes) - flash result UNKNOWN", code="E302", + action=RECOVERABLE) + w = struct.unpack("<256I", raw) + nbad = w[20] + return { + "magic": w[0], + "done": w[9], + "skipped": w[10], + "result": w[16], + "retried": w[17], + "worst_retries": w[18], + "bad_found": nbad, + "bad_list": [w[40 + i] for i in range(min(nbad, 64))], + } + + +def flash(image_path, log_path=None, rep=None): + """Flash `image_path` to the device via mask-ROM. Returns the parsed debug dict + on SUCCESS; raises FlashError otherwise (fail-closed).""" + rep = rep or CLIReporter() + preflight(image_path, rep) + image_path = os.path.abspath(image_path) + + usbboot = bundle.native("usbboot") + writer = bundle.native("my_write5_dram.bin") + spl = bundle.native("disc_spl_lpddr3.bin") + + tmp = tempfile.mkdtemp(prefix="diskos-flash-") + poison = os.path.join(tmp, "poison.bin") + dbg = os.path.join(tmp, "dbg.bin") + with open(poison, "wb") as f: + f.write(b"\xee" * 128) + + cmd = [ + usbboot, "-v", "--cpu", "x2000", "--stage1", spl, "--wait", "2", + "--addr", "0xa0c00000", "--download", writer, + "--addr", "0xa1000000", "--download", image_path, + "--addr", "0xa0a00000", "--download", poison, + "--start1", "0xa0c00030", "--wait", "5400", + "--addr", "0xa0a00000", "--length", "0x400", "--upload", dbg, + ] + + rep.phase("Flashing diskOS (mask-ROM) - ~60-90 minutes", destructive=True) + rep.warning("Do NOT disconnect the device or let the host sleep during the flash.") + + logf = open(log_path, "w") if log_path else open(os.path.join(tmp, "flash.log"), "w") + rep.indeterminate(True, note="flashing (scan → erase → program → verify → retry)", + expect_secs=FLASH_EXPECT_SECS) + try: + try: + with _sleep_inhibited(rep): + # own session so a GUI/parent crash can't SIGPIPE the flasher; output goes + # to a file (never a pipe) so a closed reader can't deadlock or kill it. + proc = subprocess.Popen(cmd, stdout=logf, stderr=subprocess.STDOUT, + env=bundle.native_env(), start_new_session=True) + + def _kill_flasher(): + # The flasher runs in its OWN session (start_new_session) so a parent SIGPIPE + # can't kill it mid-write - but that also means it SURVIVES us. Kill the whole + # process group so NAND writes actually stop, then reap without blocking. + try: + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + except OSError: + proc.kill() + try: + proc.wait(timeout=30) # reap; don't block if stuck in D-state + except subprocess.TimeoutExpired: + pass + + try: + # A3: bound the wait. If the device resets/re-enumerates mid-flash, + # usbboot can block on a dead handle forever - never hang the app. + rc = proc.wait(timeout=FLASH_HARD_TIMEOUT_SECS) + except subprocess.TimeoutExpired: + _kill_flasher() + raise FlashError( + f"flash timed out - the device stopped responding after " + f"{FLASH_HARD_TIMEOUT_SECS // 60} min (it most likely reset " + "mid-flash)", code="E303", action=RECOVERABLE) + except BaseException: + # Ctrl-C, SIGTERM, a GUI/parent crash, or any error during the wait: WITHOUT + # this the flasher keeps writing NAND after we return, and a tester who believes + # flashing stopped may unplug mid-write and brick the device. Kill it, then + # propagate the original interruption/error. + _kill_flasher() + raise + finally: + rep.indeterminate(False) + logf.close() + + rep.log(f"usbboot exit={rc}") + if not os.path.exists(dbg): + raise FlashError( + "no debug readback produced - flash result UNKNOWN; assume FAILED", + code="E301", action=RECOVERABLE) + + d = _parse_debug(dbg) + ok = (d["magic"] == 0x4004E005 and d["done"] == 0x55555555 + and d["result"] == 0x600DF10C) + rep.log(f"scan: bad-blocks-found={d['bad_found']} list={d['bad_list']}") + # B4: dbg[17]/[18] mean retried/worst only on SUCCESS; on the out-of-space / + # block-write-fail aborts they hold the last phys/logical block; on the other + # aborts they are 0 and must NOT be shown as "last block" (would be misleading). + if ok: + rep.log(f"write: skipped={d['skipped']} retried={d['retried']} " + f"worst={d['worst_retries']}") + elif d["result"] in (0xDEAD0002, 0xDEAD0003): + rep.log(f"write: skipped={d['skipped']} last-phys-block={d['retried']} " + f"last-logical-block={d['worst_retries']}") + else: + rep.log(f"write: skipped={d['skipped']}") + fcode, name = DEVICE_RESULT_CODES.get(d["result"], ("F000", "UNKNOWN")) + rep.log(f"result: 0x{d['result']:08X} [{fcode}] {name}") + + if not ok: + raise FlashError( + f"flash FAILED (device result [{fcode}] {name}, " + f"magic=0x{d['magic']:08X} done=0x{d['done']:08X})", + code="E310", action=RECOVERABLE) + rep.ok("flash verified OK") + return d + finally: + # B3: never leak the per-flash tempdir (poison/dbg/flash.log), but keep the log + # debuggable: if no external log_path was given, preserve the internal log to a + # single stable file (overwritten each flash - bounded, not a growing leak). + internal_log = os.path.join(tmp, "flash.log") + if not log_path and os.path.exists(internal_log): + try: + # Unique, 0600, no symlink-follow: write THROUGH the mkstemp fd (never reopen the path, + # which could follow a swapped-in symlink and truncate an arbitrary target). Path reported. + fd, kept = tempfile.mkstemp(prefix="diskos-flash-", suffix=".log") + with os.fdopen(fd, "wb") as out, open(internal_log, "rb") as src: + shutil.copyfileobj(src, out) + rep.log(f"flash log saved to {kept}") + except OSError: + pass + shutil.rmtree(tmp, ignore_errors=True) + + +class _sleep_inhibited: + """Best-effort host sleep inhibitor for the duration of the flash. No-op if the + platform tool isn't available; never blocks or fails the flash. If it CAN'T + inhibit sleep, it warns (B5) - a suspend mid-flash would abort it.""" + + def __init__(self, rep=None): + self.rep = rep + + def __enter__(self): + self.proc = None + o, _ = platform_probe.host() + try: + if o == "macos": + self.proc = subprocess.Popen(["caffeinate", "-dimsu"]) + elif o == "linux": + import shutil + if shutil.which("systemd-inhibit"): + # keep a long-lived inhibitor process alive; we kill it on exit + self.proc = subprocess.Popen( + ["systemd-inhibit", "--what=sleep:idle", + "--why=diskOS flash in progress", "sleep", "infinity"]) + except Exception: + self.proc = None + if self.proc is None and self.rep is not None: + self.rep.warning( + "could not auto-inhibit system sleep on this host - make sure your " + "computer will NOT sleep/suspend for the next ~90 minutes (a suspend " + "mid-flash aborts it; the device stays recoverable).") + return self + + def __exit__(self, *exc): + if self.proc: + try: + self.proc.terminate() + self.proc.wait(timeout=5) # reap so no zombie / stray 'sleep infinity' lingers + except Exception: + try: + self.proc.kill() + except Exception: + pass + return False diff --git a/diskos_installer/gui.py b/diskos_installer/gui.py new file mode 100644 index 0000000..a72ac4b --- /dev/null +++ b/diskos_installer/gui.py @@ -0,0 +1,570 @@ +"""Tkinter/ttk graphical installer. + +Architecture: + * All Tk work on the main thread; a single worker thread runs the engine. + * The worker drives a QueueReporter; the GUI drains the queue via root.after. + * A blocking confirm() gate: the worker asks, the main thread shows a modal + acknowledgement dialog, the worker resumes on the user's decision. + * Explicit operation states; no Cancel once the destructive flash starts; + window-close is intercepted while flashing. +""" + +import os +import queue +import sys +import threading +import time + +from diskos_installer import __version__, bundle, platform_probe, service, state +from diskos_installer.reporter import QueueReporter + +ACCENT = "#FF375F" # diskOS accent +BG = "#1c1c1e" +FG = "#f2f2f7" +SUBFG = "#9a9aa0" +PANEL = "#2c2c2e" +WARN = "#ffcc00" +OKC = "#34c759" +ERRC = "#ff453a" + +# operation states +IDLE, RUNNING, CONFIRM, SUCCESS, FAILED = "idle", "running", "confirm", "success", "failed" + + +class App: + def __init__(self, root): + self.root = root + self.q = queue.Queue() + self.reporter = QueueReporter(self.q) + self.worker = None + self.state = IDLE + self.flashing = False # True only during the destructive phase + self._confirm_event = threading.Event() + self._confirm_result = False + self.start_time = None + + root.title("diskOS Installer") + root.configure(bg=BG) + root.minsize(560, 640) + root.protocol("WM_DELETE_WINDOW", self._on_close) + + self._build_style() + self._build_widgets() + self._refresh_state_labels() + self.root.after(80, self._drain) + self.root.after(1000, self._tick_elapsed) + self.root.after(4000, self._tick_device) + + # ---- styling ----------------------------------------------------------- + def _build_style(self): + import tkinter.font as tkfont + from tkinter import ttk + self.ttk = ttk + st = ttk.Style() + try: + st.theme_use("clam") # consistent across Linux/macOS + except Exception: + pass + # Derive from the platform's NAMED fonts (always present) rather than + # hard-coding families that may be absent (Helvetica/Menlo on Linux). + def _font(base, size, weight="normal"): + f = tkfont.nametofont(base).copy() + f.configure(size=size, weight=weight) + return f + self.f_title = _font("TkDefaultFont", 20, "bold") + self.f_h = _font("TkDefaultFont", 12, "bold") + self.f_b = _font("TkDefaultFont", 11) + self.f_mono = _font("TkFixedFont", 9) + st.configure("TFrame", background=BG) + st.configure("Panel.TFrame", background=PANEL) + st.configure("TLabel", background=BG, foreground=FG, font=self.f_b) + st.configure("Sub.TLabel", background=BG, foreground=SUBFG) + st.configure("Panel.TLabel", background=PANEL, foreground=FG) + st.configure("H.TLabel", background=BG, foreground=FG, font=self.f_h) + st.configure("TRadiobutton", background=BG, foreground=FG, font=self.f_b) + st.map("TRadiobutton", background=[("active", BG)]) + st.configure("TCheckbutton", background=BG, foreground=FG, font=self.f_b) + st.map("TCheckbutton", background=[("active", BG)]) + st.configure("Accent.TButton", font=self.f_h, padding=10) + st.configure("TButton", padding=6) + st.configure("diskos.Horizontal.TProgressbar", background=ACCENT, troughcolor=PANEL) + + # ---- layout ------------------------------------------------------------ + def _build_widgets(self): + ttk = self.ttk + pad = {"padx": 16} + head = ttk.Frame(self.root) + head.pack(fill="x", pady=(16, 4), **pad) + ttk.Label(head, text="diskOS Installer", font=self.f_title, + background=BG, foreground=FG).pack(anchor="w") + self.host_lbl = ttk.Label(head, text="", style="Sub.TLabel") + self.host_lbl.pack(anchor="w") + + # mode + self.mode_frame = ttk.Frame(self.root) + self.mode_frame.pack(fill="x", pady=(10, 4), **pad) + self.mode = __import__("tkinter").StringVar(value="install") + for val, txt in (("install", "Install diskOS"), + ("restore", "Remove diskOS (restore stock)"), + ("remove", "Uninstall this tool")): + ttk.Radiobutton(self.mode_frame, text=txt, value=val, variable=self.mode, + command=self._on_mode).pack(anchor="w", pady=2) + + # config panel (per-mode) + self.cfg = ttk.Frame(self.root, style="Panel.TFrame") + self.cfg.pack(fill="x", pady=10, **pad) + self._build_config() + + # primary action + self.action_btn = ttk.Button(self.root, text="Install diskOS…", + style="Accent.TButton", command=self._on_action) + self.action_btn.pack(fill="x", pady=(4, 8), **pad) + + # progress + prog = ttk.Frame(self.root) + prog.pack(fill="both", expand=True, **pad) + self.phase_lbl = ttk.Label(prog, text="Ready.", style="H.TLabel") + self.phase_lbl.pack(anchor="w") + self.status_lbl = ttk.Label(prog, text="", style="Sub.TLabel") + self.status_lbl.pack(anchor="w", pady=(0, 6)) + self.bar = ttk.Progressbar(prog, style="diskos.Horizontal.TProgressbar", + mode="determinate", maximum=100) + self.bar.pack(fill="x") + self.elapsed_lbl = ttk.Label(prog, text="", style="Sub.TLabel") + self.elapsed_lbl.pack(anchor="w", pady=(2, 6)) + + self.warn_banner = __import__("tkinter").Label( + prog, text="", bg=WARN, fg="#111", font=self.f_h, anchor="center") + # packed only while flashing + + import tkinter as tk + logwrap = ttk.Frame(prog) + logwrap.pack(fill="both", expand=True, pady=(4, 12)) + self.log = tk.Text(logwrap, height=10, bg="#111", fg="#cfcfd4", + font=self.f_mono, wrap="word", relief="flat", + insertbackground=FG) + sb = ttk.Scrollbar(logwrap, command=self.log.yview) + self.log.configure(yscrollcommand=sb.set, state="disabled") + self.log.pack(side="left", fill="both", expand=True) + sb.pack(side="right", fill="y") + + def _build_config(self): + for w in self.cfg.winfo_children(): + w.destroy() + ttk = self.ttk + import tkinter as tk + m = self.mode.get() + inner = ttk.Frame(self.cfg, style="Panel.TFrame") + inner.pack(fill="x", padx=12, pady=12) + if m == "install": + ttk.Label(inner, text="Your FiiO firmware (.zip):", style="Panel.TLabel").grid( + row=0, column=0, sticky="w") + self.fw_var = tk.StringVar() + ttk.Entry(inner, textvariable=self.fw_var, width=44).grid(row=1, column=0, sticky="we", pady=(2, 8)) + ttk.Button(inner, text="Browse…", command=self._pick_firmware).grid(row=1, column=1, padx=(8, 0)) + ttk.Label(inner, text="Variant:", style="Panel.TLabel").grid(row=2, column=0, sticky="w") + self.variant = tk.StringVar(value="public") + vr = ttk.Frame(inner, style="Panel.TFrame") + vr.grid(row=3, column=0, sticky="w") + ttk.Radiobutton(vr, text="Public (recommended)", value="public", + variable=self.variant, command=self._variant_warn).pack(side="left") + ttk.Radiobutton(vr, text="Dev (root shell)", value="dev", + variable=self.variant, command=self._variant_warn).pack(side="left", padx=(12, 0)) + self.variant_warn_lbl = tk.Label( + inner, text="", bg=PANEL, fg=WARN, font=self.f_b, justify="left", wraplength=460) + self.variant_warn_lbl.grid(row=4, column=0, sticky="w", pady=(4, 0)) + inner.columnconfigure(0, weight=1) + elif m == "restore": + have = state.stock_image_exists() # fast; full verify happens at restore time + msg = ("A saved bone-stock image is available - restore is one click." + if have else + "No saved stock image. Pick your FiiO firmware .zip so I can rebuild it.") + ttk.Label(inner, text=msg, style="Panel.TLabel", wraplength=460).grid( + row=0, column=0, columnspan=2, sticky="w") + if not have: + self.fw_var = tk.StringVar() + ttk.Entry(inner, textvariable=self.fw_var, width=44).grid(row=1, column=0, sticky="we", pady=(8, 0)) + ttk.Button(inner, text="Browse…", command=self._pick_firmware).grid(row=1, column=1, padx=(8, 0), pady=(8, 0)) + inner.columnconfigure(0, weight=1) + else: # remove + ttk.Label(inner, wraplength=460, style="Panel.TLabel", + text=("Deletes this installer's saved files from THIS computer. " + "Nothing was installed system-wide. If diskOS is still on the " + "device, use ‘Remove diskOS’ first.")).pack(anchor="w") + + # ---- helpers ----------------------------------------------------------- + def _pick_firmware(self): + from tkinter import filedialog + p = filedialog.askopenfilename(title="Select FiiO firmware .zip", + filetypes=[("Firmware zip", "*.zip"), ("All", "*")]) + if p: + self.fw_var.set(p) + + def _variant_warn(self): + lbl = getattr(self, "variant_warn_lbl", None) + if lbl is None: + return + if self.variant.get() == "dev": + lbl.config(text="⚠ Dev installs an ALWAYS-ON PASSWORDLESS ROOT SHELL over USB - " + "anyone with physical access gets root on every boot. It bypasses " + "device security. Development devices only, never an everyday one.") + else: + lbl.config(text="") + + def _refresh_state_labels(self): + o, a = platform_probe.host() + n = platform_probe.maskrom_count() + dev = {0: "no device in flash mode", -1: "USB not enumerable"}.get(n, f"{n} device(s) in flash mode") + self.host_lbl.config(text=f"{o}-{a} · v{__version__} · {dev}") + + def _on_mode(self): + self._build_config() + self.action_btn.config(text={"install": "Install diskOS…", + "restore": "Remove diskOS (restore stock)…", + "remove": "Uninstall this tool"}[self.mode.get()]) + + def _log(self, line, color=None): + self.log.config(state="normal") + self.log.insert("end", line + "\n") + # cap the log so it can't grow unbounded + if int(self.log.index("end-1c").split(".")[0]) > 500: + self.log.delete("1.0", "100.0") + self.log.see("end") + self.log.config(state="disabled") + + # ---- action / worker --------------------------------------------------- + def _on_action(self): + if self.state == RUNNING: + return + m = self.mode.get() + params = {} + if m == "install": + fw = getattr(self, "fw_var", None) and self.fw_var.get().strip() + if not fw: + self._log("Choose your FiiO firmware .zip first.") + return + params = {"firmware": fw, "variant": self.variant.get()} + fn = service.do_install + elif m == "restore": + fw = getattr(self, "fw_var", None) + params = {"firmware": (fw.get().strip() if fw else None)} + fn = service.do_restore + else: + params = {"force": False} + fn = service.do_remove + + self.state = RUNNING + self.start_time = time.monotonic() + self._set_controls(False) + self.bar.config(mode="determinate", value=0) + self.status_lbl.config(text="") + self.elapsed_lbl.config(text="") + self.phase_lbl.config(text="Starting…") + self._clear_finish_extra() + + def run(): + try: + r = fn(params, self.reporter, self._worker_confirm) + self.q.put(("done", r)) + except Exception as e: # BuildError/FlashError/etc. + self.q.put(("failed", {"error": str(e)})) + + self.worker = threading.Thread(target=run, daemon=True) + self.worker.start() + + def _set_controls(self, enabled): + """Enable/disable all pre-flight controls (mode + config + action) so nothing + can be changed or re-triggered while a worker is running.""" + st = "normal" if enabled else "disabled" + + def walk(w): + for c in w.winfo_children(): + cls = c.winfo_class() + if cls in ("TRadiobutton", "TButton", "TEntry", "TCheckbutton", + "Radiobutton", "Button", "Entry", "Checkbutton"): + try: + c.config(state=st) + except Exception: + pass + walk(c) + walk(self.mode_frame) + walk(self.cfg) + self.action_btn.config(state=st) + + def _clear_finish_extra(self): + w = getattr(self, "_finish_extra", None) + if w is not None: + w.destroy() + self._finish_extra = None + + def _worker_confirm(self, summary): + """Called ON THE WORKER THREAD. Ask the UI (main thread) and block.""" + self._confirm_result = False + self._confirm_event.clear() + self.q.put(("confirm", summary)) + self._confirm_event.wait() + return self._confirm_result + + def _show_confirm_dialog(self, summary): + import tkinter as tk + from tkinter import ttk + W = 480 + dlg = tk.Toplevel(self.root) + dlg.title("Confirm") + dlg.configure(bg=BG) + dlg.transient(self.root) + dlg.resizable(False, False) + destructive = summary.get("action") in ("install", "restore-stock") + + body = ttk.Frame(dlg) + body.pack(fill="both", expand=True, padx=18, pady=16) + ttk.Label(body, text=summary.get("action", "confirm").upper(), + style="H.TLabel").pack(anchor="w", pady=(0, 6)) + for k in ("variant", "duration", "consequence"): + if summary.get(k): + ttk.Label(body, text=f"{k}: {summary[k]}", style="Sub.TLabel", + wraplength=W - 36, justify="left").pack(anchor="w", pady=1) + + def _resolve(val): + # Mark the destructive boundary SYNCHRONOUSLY (main thread) the instant the + # user accepts - before the worker unblocks and spawns usbboot - so the + # window can't be closed in the gap before the 'phase' event is drained. + if val and destructive: + self.flashing = True + self._confirm_result = val + dlg.destroy() + self._confirm_event.set() + + # button row FIRST (both buttons are direct children of it) + row = ttk.Frame(body) + row.pack(side="bottom", fill="x", pady=(14, 0)) + ttk.Button(row, text="Cancel", command=lambda: _resolve(False)).pack(side="right", padx=(8, 0)) + begin = ttk.Button(row, text=("Begin 60-90 minute flash" if destructive else "Proceed"), + style="Accent.TButton", command=lambda: _resolve(True)) + begin.pack(side="right") + + ack = tk.BooleanVar(value=not destructive) + if destructive: + # classic tk.Checkbutton (ttk.Checkbutton has no wraplength) + tk.Checkbutton(body, variable=ack, wraplength=W - 40, justify="left", + text="I understand this rewrites my device and must not be interrupted.", + bg=BG, fg=FG, selectcolor=PANEL, activebackground=BG, + activeforeground=FG, highlightthickness=0, font=self.f_b, + command=lambda: begin.config(state=("normal" if ack.get() else "disabled")) + ).pack(anchor="w", pady=(12, 8)) + begin.config(state="disabled") + + # size to content, then centre over the main window + dlg.update_idletasks() + h = dlg.winfo_reqheight() + self.root.update_idletasks() + px, py = self.root.winfo_rootx(), self.root.winfo_rooty() + pw = self.root.winfo_width() + dlg.geometry(f"{W}x{h}+{px + max(0, (pw - W) // 2)}+{py + 110}") + dlg.grab_set() + dlg.protocol("WM_DELETE_WINDOW", lambda: _resolve(False)) + dlg.bind("", lambda e: _resolve(False)) + dlg.bind("", lambda e: _resolve(True) if str(begin["state"]) == "normal" else None) + (begin if not destructive else dlg).focus_set() + + # ---- queue drain (main thread) ---------------------------------------- + def _drain(self): + budget = 200 # bounded per tick so a flood of events can't starve Tk + try: + while budget > 0: + kind, payload = self.q.get_nowait() + budget -= 1 + try: + self._handle(kind, payload) + except Exception as e: # one malformed event must not stop the drain + self._log(f"[ui error handling {kind}: {e}]") + except queue.Empty: + pass + self.root.after(80, self._drain) + + def _handle(self, kind, p): + if kind == "phase": + self.phase_lbl.config(text=p["name"]) + if p.get("destructive"): + self.flashing = True + self.bar.config(mode="indeterminate") + self.bar.start(12) + self.warn_banner.config(text="DO NOT DISCONNECT THE DEVICE OR LET THE HOST SLEEP") + self.warn_banner.pack(fill="x", pady=6, before=self.log.master) + self._log("▶ " + p["name"]) + elif kind == "status": + self.status_lbl.config(text=p["message"]) + self._log(p["message"]) + elif kind == "log": + self._log(" " + p["line"]) + elif kind == "progress": + if self.bar["mode"] == "indeterminate": + self.bar.stop(); self.bar.config(mode="determinate") + total = p["total"] or 1 + self.bar.config(maximum=total, value=p["completed"]) + elif kind == "indeterminate": + if p["active"]: + self.bar.config(mode="indeterminate"); self.bar.start(12) + self.status_lbl.config(text=p.get("note", "")) + else: + self.bar.stop(); self.bar.config(mode="determinate") + elif kind == "warning": + self._log("! " + p["message"]) + elif kind == "ok": + self._log("✓ " + p["message"]) + elif kind == "error": + self._log("✗ " + p["message"]) + elif kind == "confirm": + self._show_confirm_dialog(p) + elif kind == "done": + self._finish(p) + elif kind == "failed": + self._fail(p.get("error", "unknown error")) + + def _finish(self, r): + self.flashing = False + if self.bar["mode"] == "indeterminate": + self.bar.stop() + self.warn_banner.pack_forget() + self._set_controls(True) + if r.get("aborted"): + self.state = IDLE + self.phase_lbl.config(text="Cancelled - device unchanged.") + elif r.get("errors"): # e.g. uninstall couldn't remove everything + self.state = FAILED + self.phase_lbl.config(text="Finished with problems - see log.") + else: + self.state = SUCCESS + self.bar.config(mode="determinate", maximum=100, value=100) + if self.mode.get() == "install": + self.phase_lbl.config(text="Flashed ✓ One more step ↓") + self._install_followup() + elif self.mode.get() == "remove": + self.phase_lbl.config(text="Removed ✓ Delete the app to finish.") + else: + self.phase_lbl.config(text="Done ✓ Power-cycle the device.") + self._refresh_state_labels() + + def _install_followup(self): + """The UI is embedded in the flashed image, so first boot installs diskOS on its own - + no microSD step. Offer an OPTIONAL SD copy only as a recovery fallback.""" + import tkinter as tk + from tkinter import ttk + self._clear_finish_extra() + box = tk.Frame(self.root, bg=OKC) + box.pack(fill="x", padx=16, pady=(0, 8)) + self._finish_extra = box + tk.Label(box, bg=OKC, fg="#111", font=self.f_h, justify="left", wraplength=500, + text="Done - just power-cycle the device. diskOS is embedded in the flash and " + "installs itself on first boot; no microSD step needed.").pack( + anchor="w", padx=10, pady=(8, 4)) + tk.Label(box, bg=OKC, fg="#111", font=self.f_b, justify="left", wraplength=500, + text="Optional: you can also stage a fallback copy on a microSD (used only if the " + "embedded copy is ever unreadable).").pack(anchor="w", padx=10, pady=(0, 2)) + ttk.Button(box, text="Copy fallback UI to microSD…", command=self._export_ui).pack( + anchor="w", padx=10, pady=(0, 8)) + + def _export_ui(self): + from tkinter import filedialog, messagebox + src = bundle.data("mq_ui", required=False) + if not src: + messagebox.showerror("diskOS Installer", "bundled mq_ui not found.") + return + d = filedialog.askdirectory(title="Select your microSD card (root)") + if not d: + return + try: + dest_dir = os.path.join(d, "diskos") + os.makedirs(dest_dir, exist_ok=True) + dest = os.path.join(dest_dir, "mq_ui") + with open(src, "rb") as s, open(dest, "wb") as o: + o.write(s.read()) + os.chmod(dest, 0o755) + self._log(f"✓ copied UI to {dest}") + messagebox.showinfo("diskOS Installer", + f"Copied a fallback diskOS UI to:\n{dest}\n\nThis is optional - the " + "device installs the embedded copy on its own. The card is only used " + "if that embedded copy is ever unreadable.") + except OSError as e: + messagebox.showerror("diskOS Installer", f"Could not copy to the card:\n{e}") + + def _fail(self, msg): + self.flashing = False + try: + self.bar.stop() + except Exception: + pass + self.warn_banner.pack_forget() + self.state = FAILED + self._set_controls(True) + self.phase_lbl.config(text="Failed") + self._log("✗ " + msg) + from tkinter import messagebox + messagebox.showerror("diskOS Installer", msg) + + def _tick_elapsed(self): + if self.state == RUNNING and self.start_time: + secs = int(time.monotonic() - self.start_time) + self.elapsed_lbl.config(text=f"elapsed {secs // 60}m{secs % 60:02d}s") + self.root.after(1000, self._tick_elapsed) + + def _tick_device(self): + # keep the device-status line fresh, but never scan USB mid-operation + if self.state != RUNNING: + self._refresh_state_labels() + self.root.after(4000, self._tick_device) + + def _on_close(self): + from tkinter import messagebox + if self.flashing: + messagebox.showwarning( + "Flash in progress", + "A flash is in progress. Closing now can leave the device needing a " + "mask-ROM recovery. Please wait until it finishes.") + return + if self.state == RUNNING: + if not messagebox.askokcancel( + "Operation in progress", + "An operation is still running. Close anyway?"): + return + self.root.destroy() + + +def main(): + try: + import tkinter as tk + except Exception as e: + sys.stderr.write(f"diskOS Installer: Tkinter unavailable ({e}). Use the CLI: " + "diskos-installer --help\n") + return 2 + try: + root = tk.Tk() + App(root) + root.mainloop() + return 0 + except Exception as e: + # A packaged GUI hides early tracebacks - persist one and show a dialog. + import tempfile + import traceback + # Unique, 0600 crash log (a fixed name in a world-writable temp dir could be pre-placed as a + # symlink to truncate an arbitrary target). The exact path is shown in the message below. + try: + fd, logp = tempfile.mkstemp(prefix="diskos-installer-startup-", suffix=".log") + with os.fdopen(fd, "w") as f: + f.write(traceback.format_exc()) + except OSError: + logp = "(could not write a log file)" + sys.stderr.write(f"diskOS Installer failed to start: {e}\n(log: {logp})\n" + "Try the CLI: diskos-installer doctor\n") + try: + import tkinter.messagebox as mb + mb.showerror("diskOS Installer", + f"Failed to start:\n{e}\n\nDetails: {logp}\n" + "You can still use the command line: diskos-installer doctor") + except Exception: + pass + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/diskos_installer/imagebuild.py b/diskos_installer/imagebuild.py new file mode 100644 index 0000000..72b4a56 --- /dev/null +++ b/diskos_installer/imagebuild.py @@ -0,0 +1,511 @@ +"""Build a flashable diskOS image from the user's OWN stock firmware. + +Faithful Python port of the proven mkdiskos.sh / extract_stock_rootfs.sh logic: + extract_stock_rootfs(zip) -> stock rootfs.squashfs (byte-exact from FiiO's zip) + build_image(stock, mq_ui, variant) -> diskos_.bin (76021760 bytes) + +We NEVER ship FiiO's rootfs; the user supplies their official firmware zip and we +build locally. squashfs pack/unpack delegates to the bundled mksquashfs/unsquashfs +(reference tools) - we do not reimplement squashfs. +""" + +import os +import re +import subprocess +import zipfile + +from . import bundle +from .reporter import CLIReporter + +IMG_SIZE = 76021760 # diskOS image size: 580 NAND blocks (~72.5 MiB); written to the start of the mtd2 rootfs partition (RO squashfs need not fill the 128 MB partition) + +# Known-good stock rootfs.squashfs, verified out-of-band (NOT trusting the in-zip OTA manifest, +# which an attacker could modify consistently). A tested firmware whose extracted rootfs does not +# match its pin is refused: this rejects a modified/tampered/corrupt rootfs before it is patched +# and flashed. Map: MAIN_OS_VER -> (sha256, size_bytes). Add a version's pin only after hashing an +# authentic copy of that firmware. +PINNED_ROOTFS = { + "228": ("0ffd877bca2c69ddff9ca70f4494da0d9e580c18d0f587e2c6d9921f2db82bd2", 72957952), + "209": ("f1e3c69fb0e88b923c135558e01f4387a661f68839c8118e8ad490bdc9fc74e6", 75919360), +} +# Firmware versions diskOS has been flash-tested against. Others have DIFFERENT command-tag +# meanings, so diskOS built on them can send wrong commands and misbehave/reboot. +TESTED_FW = {"209", "228"} +SQUASH_MAGIC = b"hsqs" + + +def validate_stock_rootfs(stock_squashfs, rep=None): + """Validate that `stock_squashfs` is a genuine, supported, known-good Snowsky Disc rootfs - + a SHARED gate called BEFORE the image is saved as the recovery copy, BEFORE build, and BEFORE + every restore-flash (so a wrong-device or crafted rootfs can never be saved or flashed on the + strength of a size/magic preflight alone). Raises BuildError (E220 not-a-Disc-rootfs / E221 + untested version / E224 hash mismatch). Returns the MAIN_OS_VER string. Only extracts the tiny + version.in - cheap enough to run on every path. DISKOS_ALLOW_UNTESTED_FW=1 relaxes E221/E224.""" + rep = rep or CLIReporter() + import hashlib, tempfile, shutil + unsq = bundle.native("unsquashfs") + with open(stock_squashfs, "rb") as f: + if f.read(4) != SQUASH_MAGIC: + raise BuildError("not a squashfs image (bad magic) - not a Snowsky Disc rootfs", code="E220") + tmp = tempfile.mkdtemp(prefix="diskos-vchk-") + try: + _run([unsq, "-d", os.path.join(tmp, "x"), "-f", stock_squashfs, + "etc/product_version/version.in"], capture_output=True, text=True) + ver_in = os.path.join(tmp, "x", "etc/product_version/version.in") + prod = _grep1(ver_in, r"PRODUCT=([A-Za-z0-9_]+)") + mver = _grep1(ver_in, r"MAIN_OS_VER=([0-9]+)") + finally: + shutil.rmtree(tmp, ignore_errors=True) + override = os.environ.get("DISKOS_ALLOW_UNTESTED_FW") == "1" + if prod != "SNOWSKY_DISC": + raise BuildError(f"not a Snowsky Disc rootfs (PRODUCT={prod!r})", code="E220") + if mver not in TESTED_FW and not override: + raise BuildError( + f"firmware MAIN_OS_VER={mver or '?'} is not tested (supported: {', '.join(sorted(TESTED_FW))}). " + "Other versions can have incompatible command meanings. Re-run with " + "DISKOS_ALLOW_UNTESTED_FW=1 at your own risk.", code="E221") + pin = PINNED_ROOTFS.get(mver) + if pin: + exp_sha, exp_sz = pin + got_sz = os.path.getsize(stock_squashfs) + # The pin is over the EXACT extracted rootfs (exp_sz bytes). But the SAVED recovery copy is + # those same bytes zero-PADDED to the partition size (_save_stock pads to IMG_SIZE), so this + # gate is called with both the unpadded (install/build) and padded (restore) forms. Validate + # the first exp_sz bytes against the pin and require every byte AFTER to be zero padding - so a + # padded copy verifies identically to the original, while arbitrary appended data still fails. + got_sha, tail_zero = None, False + if exp_sz <= got_sz <= IMG_SIZE: + with open(stock_squashfs, "rb") as f: + h = hashlib.sha256(); remaining = exp_sz + while remaining > 0: + chunk = f.read(min(1 << 20, remaining)) + if not chunk: + break + h.update(chunk); remaining -= len(chunk) + got_sha = h.hexdigest() if remaining == 0 else None + tail_zero = True # anything past exp_sz must be pure zero padding + while True: + chunk = f.read(1 << 20) + if not chunk: + break + if chunk.strip(b"\x00"): + tail_zero = False; break + if got_sha != exp_sha or not tail_zero: + if override: + rep.warning(f"stock rootfs hash {(got_sha or '?')[:12]}... != pinned V{mver} - proceeding (override set)") + else: + raise BuildError( + f"stock rootfs does not match the known-good V{mver} image (got " + f"{(got_sha or 'short/oversize')[:12]}..., expected {exp_sha[:12]}...). The firmware " + "may be modified, corrupt, or repackaged - re-download the official FiiO firmware. " + "(Set DISKOS_ALLOW_UNTESTED_FW=1 at your own risk.)", code="E224") + else: + rep.log(f"stock rootfs matches the pinned known-good V{mver} image (sha256 verified)") + elif mver in TESTED_FW: + rep.warning(f"no pinned hash for V{mver} yet - rootfs authenticity is NOT verified against a pin") + rep.log(f"stock rootfs OK: PRODUCT={prod} MAIN_OS_VER={mver or '?'}") + return mver + + +from .errors import BuildError # coded (E2xx); re-exported so imagebuild.BuildError still resolves + + +# --- safe zip extraction (reject traversal / symlink escape / bombs) --------- +def _safe_extract_member(zf, member, dest_root): + name = member.filename + if name.startswith("/") or os.path.isabs(name) or ".." in name.replace("\\", "/").split("/"): + raise BuildError(f"unsafe path in zip: {name!r}", code="E202") + target = os.path.realpath(os.path.join(dest_root, name)) + if not (target == os.path.realpath(dest_root) or + target.startswith(os.path.realpath(dest_root) + os.sep)): + raise BuildError(f"zip entry escapes extraction dir: {name!r}", code="E202") + return target + + +# FiiO chunks the rootfs and wraps each chunk (and the manifest) in AES-256-CBC +# (openssl -pbkdf2) under the fixed key "fo123" (their reused OTA string - +# obfuscation, not protection). We decrypt + concatenate in index order. We only +# READ the image; no signature is involved. +_OTA_KEY = "fo123" + + +def _openssl_aes_decrypt(data, password): + """Replicate `openssl enc -d -aes-256-cbc -pbkdf2 -k `: + 'Salted__' + 8-byte salt header, PBKDF2-HMAC-SHA256 (10000 iters) -> 32B key + + 16B IV, AES-256-CBC, PKCS7 padding.""" + import hashlib + from Crypto.Cipher import AES # pycryptodome (bundled) + if data[:8] != b"Salted__": + raise BuildError("encrypted blob missing openssl 'Salted__' header (not a FiiO OTA chunk?)", code="E211") + salt = data[8:16] + ct = data[16:] + if len(ct) == 0 or len(ct) % 16 != 0: + raise BuildError("encrypted blob has bad length (truncated chunk?)", code="E211") + keyiv = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 10000, 48) + pt = AES.new(keyiv[:32], AES.MODE_CBC, keyiv[32:48]).decrypt(ct) + pad = pt[-1] if pt else 0 + if pad < 1 or pad > 16 or pt[-pad:] != bytes([pad]) * pad: + raise BuildError("bad PKCS7 padding after AES decrypt (wrong key or firmware?)", code="E211") + return pt[:-pad] + + +def extract_stock_rootfs(fw_zip, out_squashfs, workdir, rep=None): + """Pull the exact stock rootfs.squashfs out of FiiO's official update zip by + decrypting + reassembling the main_os OTA chunks. Byte-exact; written only + after it validates (manifest size + squashfs magic), so a failure never + clobbers the output.""" + rep = rep or CLIReporter() + rep.phase("Extracting stock firmware") + if not zipfile.is_zipfile(fw_zip): + raise BuildError(f"not a zip archive: {fw_zip}", code="E201") + + ex = os.path.join(workdir, "fw_unzip") + import shutil + if os.path.isdir(ex): # fresh every time - never mix two firmwares' chunks + shutil.rmtree(ex, ignore_errors=True) + os.makedirs(ex) + total_uncompressed = 0 + with zipfile.ZipFile(fw_zip) as zf: + infos = zf.infolist() + if len(infos) > 20000: # per-entry count bound (not just aggregate size) + raise BuildError("zip has an implausible number of entries - refusing.", code="E202") + for m in infos: + total_uncompressed += m.file_size + if total_uncompressed > 4 * (1 << 30): # 4 GiB bomb guard + raise BuildError("zip expands beyond 4 GiB - refusing (possible zip bomb)", code="E202") + rep.status("Unpacking firmware zip") + for i, m in enumerate(infos): + tgt = _safe_extract_member(zf, m, ex) + if m.is_dir(): + os.makedirs(tgt, exist_ok=True) + else: + os.makedirs(os.path.dirname(tgt), exist_ok=True) + with zf.open(m) as src, open(tgt, "wb") as dst: + while True: + chunk = src.read(1 << 20) + if not chunk: + break + dst.write(chunk) + rep.progress(i + 1, len(infos)) + + # locate the ONE main_os OTA manifest (refuse ambiguity) + import glob + mans = sorted(glob.glob(os.path.join(ex, "**", "main_os", "ota_v*", "ota_update.in.enc"), + recursive=True)) + if len(mans) == 0: + raise BuildError("no main_os/ota_v*/ota_update.in.enc in this zip - not a Disc " + "main-OS firmware?", code="E210") + if len(mans) > 1: + raise BuildError(f"{len(mans)} main_os manifests in this zip - ambiguous, aborting.", code="E210") + man_enc = mans[0] + ota_dir = os.path.dirname(man_enc) + rep.log(f"OTA dir: {os.path.relpath(ota_dir, ex)}") + + manifest = _openssl_aes_decrypt(open(man_enc, "rb").read(), _OTA_KEY).decode("utf-8", "ignore") + img_name, img_size = _parse_rootfs_manifest(manifest) + if not img_name: + raise BuildError("no rootfs image in the OTA manifest.", code="E210") + # img_name must be a bare basename (no path separators / traversal) + if img_name != os.path.basename(img_name) or img_name in ("", ".", "..") or "/" in img_name or "\\" in img_name: + raise BuildError(f"OTA manifest rootfs image name is not a safe basename: {img_name!r}", code="E210") + if img_size is not None: + if not img_size.isdigit() or not (0 < int(img_size) <= 256 * (1 << 20)): + raise BuildError(f"OTA manifest img_size is implausible: {img_size!r}", code="E210") + rep.log(f"rootfs image={img_name} expected_size={img_size or '?'}") + + # order the $img.NNNN.enc chunks by numeric index (skip ota_sha256_* etc.); + # reject DUPLICATE indices and require a contiguous 0..N-1 sequence. + by_idx = {} + for f in glob.glob(os.path.join(ota_dir, glob.escape(img_name) + ".*.enc")): + rest = os.path.basename(f)[len(img_name) + 1:] # "NNNN..enc" + idx = rest.split(".", 1)[0] + if not idx.isdigit(): + continue + i = int(idx) + if i in by_idx: + raise BuildError(f"duplicate rootfs chunk index {i} in the OTA dir - refusing.", code="E212") + by_idx[i] = f + if not by_idx: + raise BuildError(f"no {img_name}.NNNN.*.enc chunks found in the OTA dir.", code="E212") + idxs = sorted(by_idx) + if idxs != list(range(len(idxs))): + raise BuildError(f"rootfs chunk indices are not a contiguous 0..{len(idxs)-1} sequence " + f"(got {idxs[:3]}…{idxs[-1]}) - missing chunk, refusing.", code="E212") + chunks = [(i, by_idx[i]) for i in idxs] + + tmpout = os.path.join(workdir, "rootfs.assembled") + rep.status("Decrypting + assembling rootfs") + with open(tmpout, "wb") as out: + for i, (_idx, f) in enumerate(chunks): + out.write(_openssl_aes_decrypt(open(f, "rb").read(), _OTA_KEY)) + rep.progress(i + 1, len(chunks)) + + got = os.path.getsize(tmpout) + rep.log(f"assembled {len(chunks)} chunks ({got} bytes)") + if img_size and got != int(img_size): + raise BuildError(f"assembled size {got} != manifest {img_size} (missing/dup chunk?)", code="E213") + with open(tmpout, "rb") as f: + if f.read(4) != SQUASH_MAGIC: + raise BuildError("assembled output is not a squashfs (bad magic) - wrong key/firmware.", code="E213") + + os.replace(tmpout, out_squashfs) + rep.ok(f"stock rootfs extracted -> {out_squashfs} ({got} bytes)") + return out_squashfs + + +def _parse_rootfs_manifest(text): + """From the decrypted ota_update.in, return (img_name, img_size) for the block + whose img_type=rootfs (mirrors the awk in extract_stock_rootfs.sh).""" + in_rootfs = False + name = size = None + for line in text.splitlines(): + line = line.strip() + if line == "img_type=rootfs" or line.endswith("=rootfs") and line.startswith("img_type"): + in_rootfs = True + continue + if in_rootfs: + if line.startswith("img_name="): + name = line[len("img_name="):] + elif line.startswith("img_size="): + size = line[len("img_size="):] + if name and size: + break + return name, size + + +def _copyfile(src, dst): + with open(src, "rb") as s, open(dst, "wb") as d: + while True: + b = s.read(1 << 20) + if not b: + break + d.write(b) + + +# --- fiio_init.sh boot-hook patch (python-native, single-match-or-refuse) ---- +_OLD_IF = 'if [ "$COREDUMP_FLAG" == "1" ]; then' +_LAUNCH = _OLD_IF + "\n /usr/data/mq_ui &" +_BLOCK = ( + "if [ -f /usr/data/mq_ui ] && [ -f /usr/data/mq_player ]; then\n" + " # diskOS override: run our UI + the player from /usr/data (persists across\n" + " # rootfs flashes). Falls back to the stock rootfs binaries if either is absent.\n" + " /usr/data/mq_ui &\n sleep 2\n /usr/data/mq_player &\n" + 'elif [ "$COREDUMP_FLAG" == "1" ]; then' +) + + +def _patch_fiio_init(path, rep): + with open(path, encoding="utf-8", errors="surrogateescape") as f: + s = f.read() + # We always start from freshly-extracted OFFICIAL stock, so the file must be + # UNpatched: refuse anything already containing our marker (corrupt/re-used tree) + # rather than trusting it. + if "diskOS override" in s: + raise BuildError("fiio_init.sh already contains a diskOS marker - refusing to " + "patch a non-pristine rootfs. Re-extract from official firmware.", code="E223") + n = s.count(_LAUNCH) + if n != 1: + raise BuildError( + "boot-hook anchor (COREDUMP launch block) " + f"{'not found' if n == 0 else 'ambiguous'} in fiio_init.sh - " + "incompatible firmware boot structure; do not ship this base untested.", code="E223") + i = s.index(_LAUNCH) + s = s[:i] + _BLOCK + s[i + len(_OLD_IF):] + with open(path, "w", encoding="utf-8", errors="surrogateescape") as f: + f.write(s) + + +# --- ELF sanity for the UI binary ------------------------------------------- +def _validate_ui_elf(ui_path): + if not os.path.exists(ui_path): + raise BuildError(f"UI binary not found: {ui_path}", code="E222") + with open(ui_path, "rb") as f: + hdr = f.read(20) + if hdr[:4] != b"\x7fELF": + raise BuildError(f"'{ui_path}' is not an ELF", code="E222") + if hdr[4:6] != b"\x01\x01": + raise BuildError(f"'{ui_path}' is not ELF32 little-endian (EI_CLASS/DATA)", code="E222") + if hdr[18:20] != b"\x08\x00": + raise BuildError(f"'{ui_path}' e_machine is not MIPS-LE", code="E222") + + +def _run(cmd, **kw): + return subprocess.run(cmd, env=bundle.native_env(), **kw) + + +def _validate_squashfs_output(sq_path, unsq, expect_ui_sha, expect_ui_sz, rep=None): + """Validate a freshly-repacked squashfs by its CONTENT, not the repacker's exit status. Does a + COMPLETE independent extraction (so silent corruption ANYWHERE fails, not just in two files), + then verifies every boot-critical artefact: the boot-hook patch in fiio_init.sh, the executable + first-boot installer hook, the manifest (agreeing with the embedded UI), and the embedded UI + itself (exact sha256 + size + exec bit). Raises BuildError (E232) on any problem.""" + import hashlib, tempfile, shutil + with open(sq_path, "rb") as f: + if f.read(4) != b"hsqs": + raise BuildError("repacked image is not a valid squashfs (bad superblock magic) - " + "the repacker produced a corrupt file", code="E232") + tmp = tempfile.mkdtemp(prefix="diskos-sqcheck-") + try: + dst = os.path.join(tmp, "x") + # FULL extraction (no file subset): a corrupt inode / metadata block / file anywhere in the + # bootable filesystem makes this fail, which two-file extraction would miss. + r = _run([unsq, "-d", dst, "-f", sq_path], capture_output=True, text=True) + if r.returncode != 0: + raise BuildError(f"repacked image failed full extraction (unsquashfs rc={r.returncode}) - " + f"corrupt/truncated repack: {r.stderr.strip()[:200]}", code="E232") + + def _need(rel, what): + p = os.path.join(dst, rel) + if not os.path.exists(p): + raise BuildError(f"repacked image is missing {what} ({rel}) - do NOT flash", code="E232") + return p + + # boot hook present + patched + with open(_need("usr/project/fiio_init.sh", "boot script"), encoding="utf-8", errors="ignore") as f: + if "diskOS override" not in f.read(): + raise BuildError("repacked image: fiio_init.sh lacks the diskOS boot-hook patch", code="E232") + # first-boot installer hook present + executable + s97 = _need("etc/init.d/S97diskos_install", "first-boot installer hook") + if not (os.stat(s97).st_mode & 0o111): + raise BuildError("repacked image: S97diskos_install is not executable", code="E232") + # embedded UI: exact identity + exec bit + ui = _need("opt/diskos/mq_ui", "embedded UI") + if os.path.getsize(ui) != expect_ui_sz: + raise BuildError(f"repacked image: embedded UI size {os.path.getsize(ui)} != {expect_ui_sz}", code="E232") + if hashlib.sha256(open(ui, "rb").read()).hexdigest() != expect_ui_sha: + raise BuildError("repacked image: embedded UI hash mismatch (repack corrupted it) - do NOT flash", code="E232") + if not (os.stat(ui).st_mode & 0o111): + raise BuildError("repacked image: embedded UI is not executable", code="E232") + # manifest present + agrees with the embedded UI (the on-device hook trusts it) + man = _need("etc/diskos_manifest", "diskOS manifest") + if (_grep1(man, r"SHA256=([0-9a-fA-F]+)") != expect_ui_sha + or _grep1(man, r"SIZE=([0-9]+)") != str(expect_ui_sz)): + raise BuildError("repacked image: manifest/UI mismatch - do NOT flash", code="E232") + finally: + shutil.rmtree(tmp, ignore_errors=True) + if rep is not None: + rep.log("output squashfs validated (full extraction + boot hook + S97 + manifest + UI hash/mode)") + + +def build_image(stock_squashfs, ui_binary, variant, out_bin, workdir, rep=None): + """Build diskos_.bin from a stock rootfs + the diskOS UI.""" + rep = rep or CLIReporter() + if variant not in ("public", "dev"): + raise BuildError(f"variant must be 'public' or 'dev', got {variant!r}", code="E250") + rep.phase(f"Building diskOS image ({variant})") + + unsq = bundle.native("unsquashfs") + mksq = bundle.native("mksquashfs") + rf = os.path.join(workdir, "rf") + if os.path.isdir(rf): + import shutil + shutil.rmtree(rf) + + rep.status("[1/6] unpacking stock rootfs") + r = _run([unsq, "-d", rf, stock_squashfs], capture_output=True, text=True) + if r.returncode != 0: + raise BuildError(f"unsquashfs failed: {r.stderr.strip()[:400]}", code="E230") + + rep.status("[2/6] validating base is a Snowsky Disc rootfs") + validate_stock_rootfs(stock_squashfs, rep) # product / tested-version / known-good-hash gate + + rep.status("[3/6] validating the diskOS UI binary") + _validate_ui_elf(ui_binary) + + rep.status("[4/6] patching fiio_init.sh + installing first-boot hook") + fiio_path = os.path.join(rf, "usr/project/fiio_init.sh") + _assert_within_rf(rf, fiio_path) # a crafted rootfs must not redirect the in-place patch + _patch_fiio_init(fiio_path, rep) + _install(bundle.data("S97diskos_install"), os.path.join(rf, "etc/init.d/S97diskos_install"), 0o755, rf) + + import hashlib + ui_sha = hashlib.sha256(open(ui_binary, "rb").read()).hexdigest() + ui_sz = os.path.getsize(ui_binary) + import time + manifest_path = os.path.join(rf, "etc/diskos_manifest") + _assert_within_rf(rf, manifest_path) + if os.path.islink(manifest_path): + os.unlink(manifest_path) # never follow a planted symlink at the manifest path + with open(manifest_path, "w") as f: + f.write(f"SHA256={ui_sha}\nSIZE={ui_sz}\nARCH=mips-le\nVARIANT={variant}\n" + f"BUILT={time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}\n") + + # Embed the UI INSIDE the rootfs at a fixed path the S97 hook installs from FIRST (SD is only a + # fallback source). This makes first boot need no SD card: flash -> reboot -> diskOS. The hook + # still verify_ui's this copy against the manifest above, so a corrupt flash can't run it. + _install(ui_binary, os.path.join(rf, "opt/diskos/mq_ui"), 0o755, rf) + + # Debug-access tooling (BOTH variants): the diskos-debug helper + a static dropbear. mq_ui's + # "Debug Mode" toggle drives these to start SSH (random per-enable password) and/or the USB + # serial shell on demand. Shipping them in the public image too means a normal user can enable + # debug access from the UI without needing the dev build. + _install(bundle.data("dropbearmulti"), os.path.join(rf, "usr/project/dropbearmulti"), 0o755, rf) + _install(bundle.data("diskos-debug.sh"), os.path.join(rf, "usr/project/diskos-debug.sh"), 0o755, rf) + + if variant == "dev": + # Dev only: an ALWAYS-ON USB serial recovery shell (builds the gadget + attaches the one + # diskos-debug shell at boot), so a dev build is reachable over USB even before the UI runs. + _install(bundle.data("S99usbserial"), os.path.join(rf, "etc/init.d/S99usbserial"), 0o755, rf) + + rep.status("[5/6] repacking squashfs (stock params: lzo, -b 131072)") + out_sq = os.path.join(workdir, "out.squashfs") + if os.path.exists(out_sq): + os.remove(out_sq) + r = _run([mksq, rf, out_sq, "-comp", "lzo", "-b", "131072", + "-no-xattrs", "-all-root", "-noappend"], capture_output=True, text=True) + if r.returncode != 0: + raise BuildError(f"mksquashfs failed: {r.stderr.strip()[:400]}", code="E230") + sqsz = os.path.getsize(out_sq) + if sqsz > IMG_SIZE: + raise BuildError( + f"squashfs is {sqsz} > {IMG_SIZE} partition - refusing (truncating would " + "make it unbootable). Trim content or use a smaller UI.", code="E231") + + # Trust the OUTPUT, not the repacker's exit code: confirm the superblock magic AND that the + # embedded UI extracts byte-identical (an independent unsquashfs round-trip). Catches a + # silently-corrupt/truncated repack - the exact failure mode a nonzero-exit-but-valid (or, worse, + # zero-exit-but-corrupt) mksquashfs could hide - before it ever reaches the device. + _validate_squashfs_output(out_sq, unsq, ui_sha, ui_sz, rep) + + rep.status("[6/6] finalizing image (pad to partition size)") + _copyfile(out_sq, out_bin) + with open(out_bin, "r+b") as f: # pad to exact partition size + f.truncate(IMG_SIZE) + + import hashlib as _h + md5 = _h.md5(open(out_bin, "rb").read()).hexdigest() + rep.ok(f"image built: {out_bin} ({os.path.getsize(out_bin)} bytes) md5={md5}") + return out_bin + + +def _grep1(path, pattern): + try: + with open(path, encoding="utf-8", errors="ignore") as f: + m = re.search(pattern, f.read()) + return m.group(1) if m else None + except OSError: + return None + + +def _assert_within_rf(rf, dst, code="E233"): + """Refuse a write target that, via a symlink AT the target or in any PARENT component, resolves + OUTSIDE the extracted rootfs `rf`. Image building runs with the caller's privileges (often sudo, + since mask-ROM USB needs it), so a crafted stock squashfs that ships e.g. + `etc/init.d/S97diskos_install -> /etc/cron.d/x` could make a plain open() clobber a host file. + realpath() resolves every symlink on the path, so an escape is caught here before any write.""" + rroot = os.path.realpath(rf) + real = os.path.realpath(dst) + if not (real == rroot or real.startswith(rroot + os.sep)): + raise BuildError( + f"refusing to write outside the rootfs: {dst!r} resolves to {real!r} via a symlink - " + "the stock firmware may be crafted or corrupt. Re-download the official FiiO firmware.", + code=code) + + +def _install(src, dst, mode, rf): + _assert_within_rf(rf, dst) + os.makedirs(os.path.dirname(dst), exist_ok=True) + if os.path.islink(dst): + os.unlink(dst) # replace a planted symlink with a real file - never follow it + _copyfile(src, dst) + os.chmod(dst, mode) diff --git a/diskos_installer/platform_probe.py b/diskos_installer/platform_probe.py new file mode 100644 index 0000000..eba2b70 --- /dev/null +++ b/diskos_installer/platform_probe.py @@ -0,0 +1,104 @@ +"""Host OS/arch detection and Snowsky-Disc device presence checks. + +Device USB identities we care about: + - Ingenic X2000 mask-ROM (flashing mode): VID:PID 0a108:eaef (a108:eaef) + - normal running device (not used for flashing) is a different id. + +We enumerate USB via pyusb (bundled) so we don't depend on `lsusb` being present. +""" + +import platform +import sys + +MASKROM_VID = 0x0a108 & 0xFFFF # printed as a108 by lsusb; VID field is 0xa108 +MASKROM_PID = 0xeaef +# lsusb shows "a108:eaef"; libusb reports idVendor=0xa108 idProduct=0xeaef +MASKROM_VID = 0xa108 + + +def host(): + """Return (os_key, arch_key) e.g. ('linux','x86_64') / ('macos','arm64').""" + sysname = platform.system().lower() + if sysname == "darwin": + os_key = "macos" + elif sysname == "linux": + os_key = "linux" + else: + os_key = sysname # 'windows' etc. - unsupported for now + machine = platform.machine().lower() + arch = { + "x86_64": "x86_64", "amd64": "x86_64", + "arm64": "arm64", "aarch64": "arm64", + }.get(machine, machine) + return os_key, arch + + +def host_tag(): + o, a = host() + return f"{o}-{a}" + + +def is_supported(): + o, _ = host() + return o in ("linux", "macos") + + +def _bundled_libusb_backend(): + """A pyusb libusb1 backend pointed at OUR bundled libusb, so USB enumeration + works in the frozen app even when the system has no libusb. Returns a backend + or None (caller falls back to pyusb's default search).""" + try: + import glob + import os + import usb.backend.libusb1 as libusb1 + from . import bundle + libdir = os.path.join(bundle.vendor_dir(), "lib") + # macOS dylib or Linux .so, whatever we bundled + for pat in ("libusb-1.0*.dylib", "libusb-1.0.so*", "libusb-1.0*"): + hits = sorted(glob.glob(os.path.join(libdir, pat))) + if hits: + return libusb1.get_backend(find_library=lambda _n, _p=hits[0]: _p) + except Exception: + pass + return None + + +def _maskrom_count_pyusb(): + """Count devices in Ingenic mask-ROM mode via pyusb. Returns int or None if + pyusb/backend is unavailable.""" + try: + import usb.core # pyusb (bundled) + except Exception: + return None + try: + backend = _bundled_libusb_backend() # prefer our bundled libusb + devs = list(usb.core.find(find_all=True, idVendor=MASKROM_VID, + idProduct=MASKROM_PID, backend=backend)) + return len(devs) + except Exception: + return None + + +def _maskrom_count_lsusb(): + """Fallback: parse `lsusb` if present (Linux).""" + import shutil + import subprocess + if not shutil.which("lsusb"): + return None + try: + out = subprocess.run(["lsusb"], capture_output=True, text=True, timeout=10).stdout + except Exception: + return None + return sum(1 for ln in out.splitlines() if "a108:eaef" in ln.lower()) + + +def maskrom_count(): + """How many devices are currently in mask-ROM mode. Prefers pyusb, falls back + to lsusb, returns -1 if neither is available (caller should warn).""" + n = _maskrom_count_pyusb() + if n is not None: + return n + n = _maskrom_count_lsusb() + if n is not None: + return n + return -1 diff --git a/diskos_installer/reporter.py b/diskos_installer/reporter.py new file mode 100644 index 0000000..7a2fb45 --- /dev/null +++ b/diskos_installer/reporter.py @@ -0,0 +1,137 @@ +"""Reporter interface - decouples the engine (imagebuild/flasher/state) from the +front-end. The engine reports FACTS; it never touches widgets or prints directly. + + CLIReporter -> renders to the terminal via ui.py + QueueReporter -> enqueues immutable events for the Tk GUI to drain on its main + thread (the engine runs on a worker thread) + +Contract: + phase(name, destructive=False) a new stage began + status(message) transient 'what's happening now' line + log(line) detail/log line + progress(completed, total) determinate progress (total > 0) + indeterminate(active, note="") long op with no count (the flash) on/off + warning(message) / ok(message) / error(message) +""" + +import threading + +from . import ui + + +class Reporter: + def phase(self, name, destructive=False): ... + def status(self, message): ... + def log(self, line): ... + def progress(self, completed, total): ... + def indeterminate(self, active, note="", expect_secs=None): ... + def warning(self, message): ... + def ok(self, message): ... + def error(self, message): ... + + +class CLIReporter(Reporter): + """Terminal renderer - preserves the existing CLI look via ui.py.""" + + def __init__(self): + self._phase = "" + self._bar = None + self._hb = None + self._hb_stop = None + self._hb_thread = None + + def phase(self, name, destructive=False): + self._end_bar() + self._phase = name + ui.step(name + (" (this rewrites the device)" if destructive else "")) + + def status(self, message): + self._end_bar() + ui.info(message) + + def log(self, line): + ui.info(ui.dim(line)) + + def progress(self, completed, total): + if not total or total <= 0: + return + if self._bar is None: + self._bar = ui.Bar(self._phase or "working", total) + self._bar.update(completed) + if completed >= total: + self._bar.done() + self._bar = None + + def _end_bar(self): + if self._bar is not None: + self._bar.done() + self._bar = None + + def indeterminate(self, active, note="", expect_secs=None): + if active: + if self._hb is not None: + return + self._hb = ui.Heartbeat(note or self._phase or "working", expect_secs=expect_secs) + self._hb_stop = threading.Event() + + def _run(hb, stop): + while not stop.is_set(): + hb.tick() + stop.wait(0.5) + hb.stop() + + self._hb_thread = threading.Thread(target=_run, args=(self._hb, self._hb_stop), daemon=True) + self._hb_thread.start() + else: + if self._hb_stop: + self._hb_stop.set() + if self._hb_thread: + self._hb_thread.join(timeout=2) + self._hb = self._hb_stop = self._hb_thread = None + + def warning(self, message): + self._end_bar() + ui.warn(message) + + def ok(self, message): + self._end_bar() + ui.ok(message) + + def error(self, message): + self._end_bar() + ui.err(message) + + +class QueueReporter(Reporter): + """Enqueues immutable (kind, payload) events for the GUI to drain via + root.after on the main thread. NEVER touches Tk widgets itself.""" + + def __init__(self, q): + self.q = q + + def _emit(self, kind, **kw): + self.q.put((kind, kw)) + + def phase(self, name, destructive=False): + self._emit("phase", name=name, destructive=destructive) + + def status(self, message): + self._emit("status", message=message) + + def log(self, line): + self._emit("log", line=line) + + def progress(self, completed, total): + self._emit("progress", completed=completed, total=total) + + def indeterminate(self, active, note="", expect_secs=None): + self._emit("indeterminate", active=active, note=note, expect_secs=expect_secs) + + def warning(self, message): + self._emit("warning", message=message) + + def ok(self, message): + self._emit("ok", message=message) + + def error(self, message): + self._emit("error", message=message) diff --git a/diskos_installer/service.py b/diskos_installer/service.py new file mode 100644 index 0000000..a62a2b5 --- /dev/null +++ b/diskos_installer/service.py @@ -0,0 +1,168 @@ +"""Application service - the install / restore-stock / remove flows, independent +of any front-end. CLI and GUI both call these with a Reporter and a `confirm` +callback, so the two front-ends can never diverge in behaviour or safety checks. + +`confirm(summary: dict) -> bool` is the destructive-action gate: + - CLI: a y/N prompt. + - GUI: the CONFIRMATION screen (acknowledge checkbox + explicit button), + driven from the worker thread via a blocking Event. + +Each flow returns a result dict; raises BuildError/FlashError on failure. +""" + +import os +import time + +from . import bundle, flasher, imagebuild, state + + +def _save_stock(stock_sq, rep): + """Copy the user's bone-stock image into state (padded to the partition size) + so restore-stock can always reflash exactly it. Returns its sha256.""" + rep.status("Saving your stock rootfs image (so diskOS can always be deactivated/reverted)") + sz = os.path.getsize(stock_sq) + if sz > imagebuild.IMG_SIZE: + # NEVER truncate a larger image - a truncated file can still look flashable + # (hsqs magic + right size) yet be a corrupt, unbootable stock. + raise imagebuild.BuildError( + f"stock rootfs is {sz} bytes > {imagebuild.IMG_SIZE} partition - refusing " + "(truncating it would produce a corrupt 'stock' image).", code="E240") + if sz < imagebuild.IMG_SIZE: + padded = stock_sq + ".padded" + imagebuild._copyfile(stock_sq, padded) + with open(padded, "r+b") as f: + f.truncate(imagebuild.IMG_SIZE) + src = padded + else: + src = stock_sq + digest = state.save_stock_image(src, progress=lambda r, t: rep.progress(r, t)) + rep.ok(f"bone-stock image saved (sha256={digest[:16]}…)") + return digest + + +def _save_state_soft(st, rep): + """Persist state, but NEVER let a bookkeeping failure masquerade as a flash + failure. Call only AFTER a verified flash.""" + try: + state.save(st) + except Exception as e: # disk full, permissions, etc. + rep.warning(f"flash succeeded, but saving local history failed: {e}") + + +def do_install(params, rep, confirm): + """params: dict(firmware=?, stock=?, ui_binary=?, variant='public'|'dev'). + Extract -> save bone-stock -> build -> confirm -> flash. Returns result dict.""" + ui_bin = params.get("ui_binary") or bundle.data("mq_ui", required=False) + if not ui_bin or not os.path.exists(ui_bin): + raise imagebuild.BuildError("no diskOS UI binary (bundled 'mq_ui' missing).", code="E102", + action="the install is incomplete; re-download the installer") + variant = params.get("variant", "public") + + work = state.build_dir() + st = state.load() + st.update({"phase": "prepared", "variant": variant}) + state.save(st) + + # 1) obtain the stock rootfs + save it as the restore image + stock_sq = os.path.join(work, "stock_rootfs.squashfs") + if params.get("stock"): + imagebuild._copyfile(params["stock"], stock_sq) + elif params.get("firmware"): + imagebuild.extract_stock_rootfs(params["firmware"], stock_sq, work, rep=rep) + else: + raise imagebuild.BuildError("need a firmware .zip or a stock rootfs.squashfs.", code="E140", + action="pass your official FiiO firmware .zip") + # Validate the stock image is a genuine, supported, known-good Disc rootfs BEFORE it overwrites + # the saved recovery copy - so a wrong/corrupt image can't destroy a good recovery then abort. + imagebuild.validate_stock_rootfs(stock_sq, rep) + _save_stock(stock_sq, rep) + + # 2) build the diskOS image + out_bin = os.path.join(work, f"diskos_{variant}.bin") + imagebuild.build_image(stock_sq, ui_bin, variant, out_bin, work, rep=rep) + + # 3) preflight + confirm (destructive gate), then flash + flasher.preflight(out_bin, rep) + summary = { + "action": "install", + "variant": variant, + "image": out_bin, + "duration": "~60-90 minutes", + "consequence": "This rewrites the device root filesystem. Do not disconnect.", + } + if not confirm(summary): + rep.warning("aborted before flashing (nothing written to the device).") + return {"ok": False, "aborted": True} + + st.update({"phase": "flash-started"}) + _save_state_soft(st, rep) + # From here, flash() either raises (real failure) or returns verified. A later + # state-save error must NOT turn a verified flash into a reported failure. + d = flasher.flash(out_bin, log_path=os.path.join(state.state_dir(), "last-flash.log"), rep=rep) + st.update({"phase": "flash-verified", "installed": True, + "installed_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())}) + _save_state_soft(st, rep) + rep.ok("diskOS flashed and verified. Power-cycle the device to boot it.") + return {"ok": True, "debug": d} + + +def do_restore(params, rep, confirm): + """Reflash the saved stock-rootfs image (deactivates diskOS; leaves /usr/data files). If no image + is saved, a firmware .zip must be provided to rebuild it.""" + stock_bin, _ = state.stock_paths() + if not state.have_stock_image(): + if not params.get("firmware"): + raise flasher.FlashError( + "no saved bone-stock image", code="E141", + action="provide FiiO's firmware .zip so I can rebuild your stock rootfs image") + work = state.build_dir() + stock_sq = os.path.join(work, "stock_rootfs.squashfs") + imagebuild.extract_stock_rootfs(params["firmware"], stock_sq, work, rep=rep) + imagebuild.validate_stock_rootfs(stock_sq, rep) + _save_stock(stock_sq, rep) + + # Never restore-flash an unvalidated image: validate whatever is about to be written (the saved + # copy or the freshly-extracted one) - product/version/known-good-hash, not just size+magic. + imagebuild.validate_stock_rootfs(stock_bin, rep) + flasher.preflight(stock_bin, rep) + summary = { + "action": "restore-stock", + "image": stock_bin, + "duration": "~60-90 minutes", + "consequence": "This reflashes bone-stock and removes diskOS. Do not disconnect.", + } + if not confirm(summary): + rep.warning("aborted (device unchanged).") + return {"ok": False, "aborted": True} + + st = state.load() + st.update({"phase": "restore-started"}) + _save_state_soft(st, rep) + d = flasher.flash(stock_bin, log_path=os.path.join(state.state_dir(), "last-restore.log"), rep=rep) + st.update({"phase": "restore-verified", "installed": False}) + _save_state_soft(st, rep) + rep.ok("Stock system reflashed. Power-cycle to boot stock.") + rep.warning("This deactivates diskOS but is not a byte-for-byte factory wipe: the diskOS " + "files under /usr/data (a separate partition) remain, inert - they do nothing " + "without the boot hook this reflash removed. (The UI embedded in the diskOS " + "rootfs is gone, since this reflash overwrote that partition with stock.)") + return {"ok": True, "debug": d} + + +def do_remove(params, rep, confirm): + """Delete everything the tool created on this computer (its full uninstall + footprint). Nothing was installed system-wide, so this is the whole cleanup.""" + if state.load().get("installed") and not params.get("force"): + if not confirm({"action": "remove-tool", + "consequence": "diskOS still appears to be on the device. This only " + "removes the installer + its saved files from THIS " + "computer (run restore-stock first to clear the device)."}): + return {"ok": False, "aborted": True} + d, errors = state.wipe_all() + if errors: + rep.error(f"could not fully remove {len(errors)} item(s) under {d}:") + for p, m in errors[:8]: + rep.log(f" {p}: {m}") + return {"ok": False, "errors": errors, "removed": d} + rep.ok(f"removed all tool state: {d}") + return {"ok": True, "removed": d} diff --git a/diskos_installer/state.py b/diskos_installer/state.py new file mode 100644 index 0000000..7b7628f --- /dev/null +++ b/diskos_installer/state.py @@ -0,0 +1,258 @@ +"""Per-user state so the tool can always put the device back to bone-stock and +so `remove` can clean up after itself. + +Everything the tool creates lives under ONE directory (state_dir()); nothing is +written system-wide. `remove` deletes that directory (and, if the user asks, the +tool itself) - that's the whole uninstall footprint on Linux/macOS. + +Contents: + state.json what we've done (installed?, versions, timestamps) + stock/stock.bin the saved stock-rootfs image, rebuilt from the user's firmware (for restore-stock) + stock/stock.sha256 its checksum + build/ scratch for extract/build (cleaned opportunistically) +""" + +import hashlib +import json +import os +import shutil +import sys +import time + +from . import errors + +APP = "diskos-installer" +# A marker file dropped in our state dir. wipe_all() refuses to recursively delete any directory +# that does not contain it, so a mis-set DISKOS_INSTALLER_HOME can never delete an arbitrary tree. +SENTINEL = ".diskos-installer-state" +# The diskOS install/restore lifecycle values written to state.json["phase"]. Used to recognise a +# legacy (pre-sentinel) state dir by CONTENT, specifically enough that a foreign project's state.json +# cannot masquerade as ours. Keep in sync with service.py. +_KNOWN_PHASES = {"prepared", "flash-started", "flash-verified", "restore-started", "restore-verified"} + + +def _fsync_dir(path): + """Fsync a directory so a rename/create inside it is durable across a crash.""" + try: + fd = os.open(path, os.O_RDONLY) + try: + os.fsync(fd) + finally: + os.close(fd) + except OSError: + pass + + +def _state_base(): + """Compute the state-dir PATH only - no directory creation, no side effects. + Used by wipe_all() so validation never re-creates the sentinel it is about to check.""" + env = os.environ.get("DISKOS_INSTALLER_HOME") + if env: + return env + if sys.platform == "darwin": + return os.path.expanduser(f"~/Library/Application Support/{APP}") + xdg = os.environ.get("XDG_DATA_HOME") + return os.path.join(xdg, APP) if xdg else os.path.expanduser(f"~/.local/share/{APP}") + + +def _is_our_statedir(base): + """STRONG test that `base` is (or was) a diskOS state dir - governs whether we may adopt it and, + in wipe_all(), recursively delete it. Requires GENUINE evidence, never a merely-common basename, + so an unrelated directory that happens to contain a 'build/' or a 'state.json' is NEVER matched: + - our sentinel file, OR + - our exact stock-recovery pair stock/stock.bin + stock/stock.sha256, OR + - a state.json that parses AND matches our specific schema (a boolean `installed` plus a + `phase` drawn from our known lifecycle values) - not merely the generic key NAMES, so an + unrelated project's state.json cannot be mistaken for ours and later deleted.""" + if os.path.isfile(os.path.join(base, SENTINEL)): + return True + if (os.path.isfile(os.path.join(base, "stock", "stock.bin")) + and os.path.isfile(os.path.join(base, "stock", "stock.sha256"))): + return True + sj = os.path.join(base, "state.json") + if os.path.isfile(sj): + try: + with open(sj) as f: + d = json.load(f) + if (isinstance(d, dict) and isinstance(d.get("installed"), bool) + and isinstance(d.get("phase"), str) and d.get("phase") in _KNOWN_PHASES): + return True + except (OSError, ValueError, TypeError): + pass + return False + + +def state_dir(): + """A single, per-user, non-system directory for all tool state (created on first use).""" + base = _state_base() + marker = os.path.join(base, SENTINEL) + # SAFETY, only for an EXPLICITLY-set home: refuse to adopt a pre-existing, non-empty directory + # that shows NO sign of being ours, so a mis-set DISKOS_INSTALLER_HOME (e.g. $HOME) can't be + # tagged here and later wiped by 'remove'. The DEFAULT XDG/APP path is always ours; and an + # existing state dir (even one predating the sentinel) is recognised by its contents. + if (os.environ.get("DISKOS_INSTALLER_HOME") and os.path.isdir(base) + and not _is_our_statedir(base)): + try: + nonempty = bool(os.listdir(base)) + except OSError: + nonempty = True + if nonempty: + raise errors.PreflightError( + f"DISKOS_INSTALLER_HOME={base!r} is a non-empty directory that is not a diskOS " + "state dir - refusing to use it. Point it at a new or empty path.", code="E142") + os.makedirs(base, exist_ok=True) + if not os.path.exists(marker): + try: + with open(marker, "w") as f: + f.write("diskOS installer state directory - safe to delete via 'diskos-installer remove'\n") + except OSError: + pass + return base + + +def _state_path(): + return os.path.join(state_dir(), "state.json") + + +def load(): + """Return the saved state. A corrupt file is reported (not silently erased) so + 'never installed' and 'state damaged' stay distinguishable.""" + p = _state_path() + if not os.path.exists(p): + return {} + try: + with open(p) as f: + return json.load(f) + except ValueError: + return {"_corrupt": True} + except OSError: + return {} + + +def save(d): + d = {k: v for k, v in d.items() if k != "_corrupt"} + d["updated"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + tmp = _state_path() + ".tmp" + with open(tmp, "w") as f: + json.dump(d, f, indent=2) + f.flush() + os.fsync(f.fileno()) + os.replace(tmp, _state_path()) + _fsync_dir(state_dir()) + + +def sha256_file(path, progress=None): + h = hashlib.sha256() + total = os.path.getsize(path) + read = 0 + with open(path, "rb") as f: + while True: + chunk = f.read(1 << 20) + if not chunk: + break + h.update(chunk) + read += len(chunk) + if progress: + progress(read, total) + return h.hexdigest() + + +def stock_paths(): + d = os.path.join(state_dir(), "stock") + os.makedirs(d, exist_ok=True) + return os.path.join(d, "stock.bin"), os.path.join(d, "stock.sha256") + + +def save_stock_image(src_bin, progress=None): + """Copy the saved stock-rootfs flashable image into state so restore-stock can + always reflash exactly it. Atomic + validated: the previous good image is only + replaced once the new one is fully written, hashed, magic-checked, and fsynced - + so a crash/disk-full can't destroy your recovery image. Returns its sha256.""" + dst, shafile = stock_paths() + d = os.path.dirname(dst) + tmp_bin, tmp_sha = dst + ".tmp", shafile + ".tmp" + + h = hashlib.sha256() + total = os.path.getsize(src_bin) + read = 0 + with open(src_bin, "rb") as s, open(tmp_bin, "wb") as o: + while True: + chunk = s.read(1 << 20) + if not chunk: + break + o.write(chunk) + h.update(chunk) + read += len(chunk) + if progress: + progress(read, total) + o.flush() + os.fsync(o.fileno()) + digest = h.hexdigest() + + # validate BEFORE committing (never publish a truncated/non-squashfs image) + with open(tmp_bin, "rb") as f: + if f.read(4) != b"hsqs": + os.unlink(tmp_bin) + raise ValueError("refusing to save a non-squashfs stock image (bad magic).") + with open(tmp_sha, "w") as f: + f.write(digest + "\n") + f.flush() + os.fsync(f.fileno()) + + # commit: rename sha first, then the image, then fsync the dir. If interrupted + # between renames, have_stock_image() sees a mismatch and reports 'no valid image' + # rather than trusting a half-committed pair. + os.replace(tmp_sha, shafile) + os.replace(tmp_bin, dst) + _fsync_dir(d) + return digest + + +def stock_image_exists(): + """Fast existence check (no hashing) - safe to call on the UI thread.""" + dst, shafile = stock_paths() + return os.path.exists(dst) and os.path.exists(shafile) + + +def have_stock_image(): + dst, shafile = stock_paths() + try: + if not (os.path.exists(dst) and os.path.exists(shafile)): + return False + want = open(shafile).read().strip() + return sha256_file(dst) == want + except OSError: + return False + + +def build_dir(): + d = os.path.join(state_dir(), "build") + os.makedirs(d, exist_ok=True) + return d + + +def wipe_all(): + """Delete the entire tool state directory (the full uninstall footprint). + Returns (path, errors): errors is a list of (path, message) for anything that + could NOT be removed, so the caller reports the truth instead of a false success.""" + d = _state_base() # PATH only - never re-create the sentinel we are about to verify + errs = [] + # SAFETY: only ever recursively delete a directory we can PROVE is our own state dir. This + # guards against DISKOS_INSTALLER_HOME being set to '/', '$HOME', the cwd, or any other tree. + real = os.path.realpath(d) + forbidden = {os.path.realpath(os.sep), os.path.realpath(os.path.expanduser("~"))} + try: + forbidden.add(os.path.realpath(os.getcwd())) + except OSError: + pass + if real in forbidden: + return d, [(d, "refusing to delete a root/home/current directory")] + if not (os.path.isdir(d) and _is_our_statedir(d)): + return d, [(d, "not a diskOS state dir (no sentinel or known state) - refusing to delete")] + try: + if os.stat(d).st_uid != os.getuid(): + return d, [(d, "state dir is not owned by you - refusing to delete")] + except OSError as e: + return d, [(d, str(e))] + shutil.rmtree(d, onerror=lambda fn, path, exc: errs.append((path, str(exc[1])))) + return d, errs diff --git a/diskos_installer/ui.py b/diskos_installer/ui.py new file mode 100644 index 0000000..26055a8 --- /dev/null +++ b/diskos_installer/ui.py @@ -0,0 +1,153 @@ +"""Terminal progress + messaging. TTY-aware: renders live bars/spinners on a +terminal, and degrades to plain timestamped log lines when piped to a file so a +saved log stays readable.""" + +import sys +import time + +# --- colour (only on a TTY that isn't dumb) --------------------------------- +_TTY = sys.stdout.isatty() + + +def _c(code, s): + return f"\033[{code}m{s}\033[0m" if _TTY else s + + +def bold(s): return _c("1", s) +def dim(s): return _c("2", s) +def red(s): return _c("31", s) +def green(s): return _c("32", s) +def yellow(s): return _c("33", s) +def cyan(s): return _c("36", s) + + +def _t(): + return time.strftime("%H:%M:%S") + + +def info(msg): + print(f"{dim(_t())} {msg}", flush=True) + + +def step(msg): + print(f"\n{cyan('▶')} {bold(msg)}", flush=True) + + +def ok(msg): + print(f"{green('✓')} {msg}", flush=True) + + +def warn(msg): + print(f"{yellow('!')} {msg}", flush=True) + + +def err(msg): + print(f"{red('✗')} {msg}", file=sys.stderr, flush=True) + + +def fmt_dur(secs): + secs = int(secs) + h, rem = divmod(secs, 3600) + m, s = divmod(rem, 60) + if h: + return f"{h}h{m:02d}m{s:02d}s" + if m: + return f"{m}m{s:02d}s" + return f"{s}s" + + +class Bar: + """A determinate progress bar for steps with a known total (0..total).""" + + def __init__(self, label, total, width=28): + self.label = label + self.total = max(1, total) + self.width = width + self.start = time.monotonic() + self.last_len = 0 + self.update(0) + + def update(self, done, note=""): + frac = min(1.0, done / self.total) + elapsed = time.monotonic() - self.start + eta = (elapsed / frac - elapsed) if frac > 0.02 else 0 + if _TTY: + fill = int(self.width * frac) + bar = "█" * fill + "·" * (self.width - fill) + line = (f"\r {self.label} {cyan(bar)} {int(frac*100):3d}%" + f" {dim(fmt_dur(elapsed))}" + + (f" · ETA {fmt_dur(eta)}" if eta > 0 else "") + + (f" {note}" if note else "")) + pad = max(0, self.last_len - len(line)) + sys.stdout.write(line + " " * pad) + sys.stdout.flush() + self.last_len = len(line) + else: + # non-TTY: emit occasional milestone lines, not a live bar + pct = int(frac * 100) + if pct in (0, 25, 50, 75, 100) and pct != getattr(self, "_last_pct", -1): + self._last_pct = pct + print(f" {self.label}: {pct}% ({fmt_dur(elapsed)}){(' ' + note) if note else ''}", + flush=True) + + def done(self, note=""): + self.update(self.total, note) + if _TTY: + sys.stdout.write("\n") + sys.stdout.flush() + + +class Heartbeat: + """Indeterminate progress for a long step with no reliable progress channel + (the ~60-90 min mask-ROM flash). Shows elapsed time + a spinner and a fixed + 'do not disconnect' reminder. Call tick() periodically; stop() to finish.""" + + FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" + + def __init__(self, label, expect_secs=None, reminder="do NOT disconnect the device"): + self.label = label + self.expect = expect_secs + self.reminder = reminder + self.start = time.monotonic() + self.i = 0 + self.last_len = 0 + + def tick(self, note=""): + elapsed = time.monotonic() - self.start + self.i = (self.i + 1) % len(self.FRAMES) + if _TTY: + spin = self.FRAMES[self.i] + approx = "" + if self.expect: + approx = f" / ~{fmt_dur(self.expect)}" + line = (f"\r {cyan(spin)} {self.label} {dim(fmt_dur(elapsed) + approx)}" + f" {yellow('· ' + self.reminder)}" + + (f" {note}" if note else "")) + pad = max(0, self.last_len - len(line)) + sys.stdout.write(line + " " * pad) + sys.stdout.flush() + self.last_len = len(line) + else: + # non-TTY: a line every ~30s so a log shows liveness without spamming + if int(elapsed) % 30 == 0 and int(elapsed) != getattr(self, "_last_log", -1): + self._last_log = int(elapsed) + print(f" {self.label}: {fmt_dur(elapsed)} elapsed ({self.reminder})", flush=True) + + def stop(self): + if _TTY: + sys.stdout.write("\n") + sys.stdout.flush() + + +def confirm(prompt, default=False): + """Yes/no prompt. Non-interactive stdin -> returns default (never blocks a pipe).""" + if not sys.stdin.isatty(): + return default + d = "Y/n" if default else "y/N" + try: + ans = input(f"{yellow('?')} {prompt} [{d}] ").strip().lower() + except EOFError: + return default + if not ans: + return default + return ans in ("y", "yes") diff --git a/docs/COMMAND_MAP.md b/docs/COMMAND_MAP.md new file mode 100644 index 0000000..01f8351 --- /dev/null +++ b/docs/COMMAND_MAP.md @@ -0,0 +1,257 @@ +# Snowsky Disc V2.09 - mq_player full command map + +Dispatch entry = `{tag_str_ptr, handler_thunk_ptr}` (8B); thunks tail-jump via GOT to the real handler. +Confidence: **V**=string-verified · **I**=inferred from family · **U**=unknown (runtime-registered, GOT slot 0). +⚠ = static reading conflicts with our 1.95-era LIVE tests (trust live; V2.09 meanings need live re-verify). + +## SOURCE / WORK-MODE SWITCH - 0657 (corrected; supersedes "close player" reading) +`0657` = switch audio SOURCE, payload = integer mode index (jump table @0x6736b0). Frame = `0657000C000`: +- `0657000C0001` = **LOCALPLAYER** (safe recover) · `0657000C0009` = **USB-DAC (UAC)** · `0657000C000C` = **BTSINK** +- **Network receivers (AirPlay/DLNA/Roon):** `0657000C0008` PLUS companion `0642000C000X` (NETWORK_MODE selector, X in {0,1,2,5}; exact AirPlay value UNMAPPED). Prereq: WLAN up. +- ✅ **PRE-STOP PINNED + CONFIRMED (2026-08-03, live strace of stock mq_ui + fixed on device):** the pre-stop is **`0666000C0006`** (out_dev=6, local). Stock always sends it BEFORE `0666000C0002` (route to BT). Skipping it = the **g_fiio_local trap** → mq_player SIGSEGV → SD freed → MCU reboot. Mechanism: direct→2 can leave shadow-out=2 with DAC-flag=1 (split state); 6→2 normalizes DAC-flag=0. **This was THE cause of every BT-route reboot.** See "BT AUDIO OUTPUT" section below. +- ⚠ AirPlay discovery CANNOT be tested on an iPhone Personal Hotspot (client/mDNS isolation) - needs a normal router. PARKED pending router + 0666 pre-stop. +NB: this 0657 is the V2.09 SOURCE switch; the 1.95 "0657=play-mode" was a different binary/table. Our earlier play-mode toggle using 0657 on V2.09 was therefore WRONG (it sent source-switch values) - FIXED 2026-06-25 (now uses 0102, below). + +## PLAY-MODE - 0102 (GROUND TRUTH, captured from stock UI 2026-06-25) +`0102` = LOCAL play-mode setter. Frame = `0102000C000`. Captured by strace'ing the stock +`/usr/bin/mq_ui`'s `mq_timedsend` while tapping its play-mode control (the loop icon, NP +transport page) - the stock UI cycles these 5 values 0→1→2→3→4→0: +| value | frame | stock icon | mode | +|---|---|---|---| +| 0 | `0102000C0000` | →→ (two arrows) | Sequential (play in order) | +| 1 | `0102000C0001` | ⇄ (crossed) | Shuffle (random) | +| 2 | `0102000C0002` | ↻ with "1" | Repeat One (single loop) | +| 3 | `0102000C0003` | ↻ (loop) | Repeat All (list loop) | +| 4 | `0102000C0004` | "1" + arrow | Single (play one track, stop) | +NB: this SUPERSEDES the old "0102 = Roon-only no-op" reading - stock uses 0102 for LOCAL +play-mode live. diskOS sends this via ui_set_workmode (main.c). The cycle order above is +the stock order; diskOS's prior 4-mode icons (seq/shuffle/repeat-one/repeat-all) already +match values 0-3. + +## LIVE-tested (ground truth) +| tag | meaning | +|---|---| +| 0100 | Play by list (list_type + start index) ✅ | +| 0102 | **Play-mode** 0102000C000<0..4> (seq/shuffle/rep-one/rep-all/single) ✅ stock-captured 2026-06-25 | +| 0103 | Seek (ms) ✅ verified live 2026-06-25 (pos jumped to target) | +| 0104 | Favorite toggle (current song) | +| 0201 | Transport play/pause toggle (generic, VALUE1-driven) - verified NOT Roon-specific | +| 0622 | Rescan SD / rebuild DB | +| 0657 | **SOURCE switch** (NOT play-mode) - see section above | +| 0666 | Output route (→set_out_device 0x461e74): 2=BTSRC 4=SPDIF 6=local-DAC. V2.09-confirmed; the "close_player" sighting was a shared teardown preamble, not this cmd's meaning. | +| 0715 | Volume absolute 0-120 ✅ verified 2026-06-25 (set 20 via 0715000C0014, persisted+displayed) | + +## BT AUDIO OUTPUT (transmit to a BT speaker, a2dp-source) - ✅ WORKING (2026-08-03) +Captured from a live strace of stock mq_ui doing a working transmit, then replicated + fixed in diskOS `ui_route_bt()`. **Plays stereo, no stutter, no reboot.** The device IS designed to transmit (not only the "Bluetooth Receiving Mode"/a2dp-sink); stock transmit works but STUTTERS because its default-quality stereo SBC exceeds the X2000 CPU. + +Working route-to-BT sequence (diskOS, MAC = the connected speaker, uppercased): +``` +0666000C0006 PRE-STOP: switch output to LOCAL first (MANDATORY - skip = g_fiio_local crash → MCU reboot) +0642000C0000 reset network/output mode +0657000C0008 work-mode 8 +06c1000C0000 start player BT-init thread (06b3 no-ops until this completes; async on cold start) +0666000C0002 route: out_dev = BT source +0657000C0008 work-mode 8 (again, as stock does) +06b3001D0000 codec select: 0=SBC (our bluealsa is sbc-only) + MAC payload (stock frame-shape; worker ignores it) +0715000C volume +``` +Then play normally (`0100…`). Reverse (BT→local) = `ui_route_analog()`: `0666000C0006` then `0657000C0008`. +**No-stutter requires bluealsa `--sbc-quality=medium`** (bit-pool ~33): stock default-quality stutters; medium plays clean stereo (~86% CPU idle on device). Set in `bt.c` bt_enable/bt_ensure_services. `--a2dp-force-mono` also works but is NOT needed (stereo is fine at medium quality). + +## Table A @0x7c9d30 - 131 entries (terminator 0x7ca148) +| tag | thunk | meaning | conf | +|---|---|---|---| +| 0802 | 0x411790 | get total song count (SELECT count(*) FROM SONG) | V | +| 0620 | 0x4117b0 | get WIFI MAC (/usr/data/fiio/nb.txt) | V | +| 0710 | 0x4117d0 | network/wifi getter | I | +| 0711 | 0x4117f0 | network/wifi getter | I | +| 0712 | 0x411810 | network/wifi op | I | +| 0713 | 0x411830 | network/wifi op | I | +| 0714 | 0x411850 | network/wifi op | I | +| 0715 | 0x411870 | network/wifi op (NB: vs live 0715=volume - table-A 0715 differs) | I | +| 0716 | 0x411890 | network/wifi op | U | +| 0601 | 0x4118b0 | media getter | U | +| 0651 | 0x4118d0 | media getter/setter | U | +| 0602 | 0x4118f0 | media getter | U | +| 0652 | 0x411910 | media getter/setter | U | +| 0606 | 0x411930 | media DB getter | I | +| 0656 | 0x411950 | media DB getter/setter | I | +| 0603 | 0x411970 | media DB getter | I | +| 0653 | 0x411990 | media DB getter/setter | I | +| 0604 | 0x4119b0 | media DB getter | I | +| 0654 | 0x4119d0 | media DB getter/setter | I | +| 0605 | 0x4119f0 | media query -> reply ([PLAY] send) | V | +| 0655 | 0x411a10 | media DB getter/setter | I | +| 0608 | 0x411a30 | media DB getter | I | +| 0658 | 0x411a50 | media DB getter/setter | I | +| 0611 | 0x411a70 | media DB getter | I | +| 0661 | 0x411a90 | media DB getter/setter | I | +| 0612 | 0x411ab0 | media DB getter | I | +| 0662 | 0x411ad0 | media DB getter/setter | I | +| 0613 | 0x411af0 | media DB getter | I | +| 0663 | 0x411b10 | media DB getter/setter | I | +| 0609 | 0x411b30 | media DB getter | I | +| 0659 | 0x411b50 | media DB getter/setter | I | +| 0615 | 0x411b70 | media DB getter | I | +| 0665 | 0x411b90 | media DB getter/setter | I | +| 0614 | 0x411bb0 | media DB getter | I | +| 0664 | 0x411bd0 | media DB getter/setter | I | +| 0607 | 0x411bf0 | media query -> reply ([PLAY] send) | V | +| 0657 | 0x411c10 | reads "close_player"/killall avahi-publish (⚠ 1.95=play-mode) | V⚠ | +| 0801 | 0x411c30 | playback/system getter | I | +| 0702 | 0x411c50 | wifi/network getter | I | +| 0703 | 0x411c70 | wifi/network getter | I | +| 0704 | 0x411c90 | get wifi scan list | V | +| 0705 | 0x411cb0 | connect wifi (psid/passwd) / SET_POWER_DOWN_TO_MCU | V | +| 0706 | 0x411cd0 | wifi/network op | I | +| 0707 | 0x411cf0 | wifi/network op | U | +| 0708 | 0x411cf0 | wifi/network op | U | +| 0639 | 0x411d30 | media-info query -> reply a639 | V | +| 0689 | 0x411d50 | **EQ PRESET SELECT** - invokes 21-way preset engine 0x449544 (via 0x4961bc), updates rate caps, replies a639. NOT a media-info query. | V | +| 0690 | 0x411d70 | media-info query -> play-send | V | +| 0675 | 0x411d90 | get EQ/PEQ (gain/filterType/frequency/loading) | V | +| 0626 | 0x411db0 | EQ getter (family) | I | +| 0627 | 0x411dd0 | EQ getter | I | +| 0677 | 0x411df0 | EQ getter/setter | I | +| 0628 | 0x411e10 | EQ getter/setter (PEQ) | I | +| 0678 | 0x411e30 | SET PEQ band (filterType/frequency/gain/qValue) | V | +| 0629 | 0x411e50 | EQ getter/setter | I | +| 0630 | 0x411e70 | get EQ (gain/filterType/frequency/loading) | V | +| 0803 | 0x411e90 | playback/system getter | I | +| 0724 | 0x411eb0 | system/OTA op | I | +| 0720 | 0x411ed0 | OTA/network (killall wget, ip route, wlan0) | V | +| 0624 | 0x411ef0 | mount/storage path (mnt/) | V | +| 0622 | 0x411f10 | system getter (NB: live 0622=rescan; table-A differs) | I | +| 0800 | 0x411f30 | write wpa_supplicant.conf (country=%s) | V | +| 0623 | 0x411f50 | system getter | I | +| 0616 | 0x411f70 | media/system getter | I | +| 0a51 | 0x411f90 | extended command (only 0aXX tag) | I | +| 0634 | 0x411fb0 | media-info query -> play-send | V | +| 0684 | 0x411fd0 | media getter | I | +| 0725 | 0x411ff0 | system/OTA op | I | +| 0617 | 0x412010 | media/system getter | I | +| 0667 | 0x412030 | media getter/setter | I | +| 0621 | 0x412050 | DROP DB tables (SONG/MY_LOVE/PLAY_LIST) | V | +| 06a0 | 0x412070 | BT/media getter | I | +| 06a1 | 0x412090 | BT/media getter | I | +| 06a3 | 0x4120b0 | BT/media getter | I | +| 0640 | 0x4120d0 | media getter/setter | I | +| 0644 | 0x4120f0 | media getter/setter | I | +| 0643 | 0x412110 | media op | U | +| 06a2 | 0x412130 | BT/media getter | I | +| 06b1 | 0x412150 | BT sample-rate param (→0x496ac4→change_rate_set_params 0x40d4d8; VALUE1 selects 44100/48000/82000?/96000) | V | +| 06b2 | 0x412170 | BT bit-depth param (→0x496b58→change_format_set_param 0x40d66c; 16/24/32-bit) | V | +| 06b3 | 0x412190 | **BT CODEC SELECT** (→0x496bb8→worker 0x40eaf4): VALUE1 0=SBC 1=AAC 2=LDAC-mob 3=LDAC-std 4=LDAC-high. Stock's connect callback sends `06b3000X` (X=persisted BT_CODEC, MAC as ignored-by-worker payload for frame-shape). **diskOS sends `06b3001D0000` (X=0 SBC, our bluealsa is `--codec=sbc` only) - value 3 only "works" on an SBC sink via a fragile `/usr/data/bt_codec` fallback, so pick the codec our bluealsa actually enables.** Worker requires `06c1` BT-init done first (else no-ops). | V (live) | +| 06b4 | 0x4121b0 | LDAC **quality** only (→0x496c94→0x40dbe8 via /usr/data/bt_pipe_recv): VALUE1 0=mobile 1=standard 2=high. Does NOT select SBC or set rate. | V | +| 06b6 | 0x4121d0 | BT op | I | +| 06b7 | 0x4121f0 | BT get paired addr+name | V | +| 06b8 | 0x412210 | BT send connected device list | V | +| 06c3 | 0x412230 | BT op | I | +| 06c2 | 0x412250 | BT op | I | +| 06c0 | 0x412270 | BT trust/power (trust/power off/hci down) | V | +| 06c4 | 0x412290 | BT disconnect/remove (bluetooth.db) | V | +| 06c5 | 0x4122b0 | BT op | I | +| 06c1 | 0x4122d0 | BT set device alias | V | +| 0679 | 0x4122f0 | media getter/setter | I | +| 0666 | 0x412310 | reads close_player (⚠ 1.95=output route) | V⚠ | +| 06b5 | 0x412330 | BT op | I | +| 0804 | 0x412350 | playback/system getter | I | +| 0805 | 0x412370 | playback/system op | U | +| 0701 | 0x412390 | wifi init/restart (init_wifi; killall udhcpc/wpa) | V | +| 0700 | 0x4123b0 | close network card (network.c) | V | +| 0808 | 0x4123d0 | playback/system getter | I | +| 0813 | 0x4123f0 | playback/system getter | I | +| 0816 | 0x412410 | playback/system getter | I | +| 0818 | 0x412430 | playback/system getter | I | +| 0817 | 0x412450 | playback/system getter | I | +| 0811 | 0x412470 | playback/system getter | I | +| 0809 | 0x412490 | playback/system getter | I | +| 0815 | 0x4124b0 | set MEMORY_PLAY position (UPDATE ... POSITION) | V | +| 0810 | 0x4124d0 | playback/system getter/setter | I | +| 0812 | 0x4124f0 | playback/system getter/setter | I | +| 0806 | 0x412510 | playback/system getter | I | +| 0645 | 0x412530 | media getter/setter | I | +| 0646 | 0x412550 | media getter/setter | I | +| 0807 | 0x412570 | playback/system getter | I | +| 0660 | 0x412590 | media op | U | +| 0610 | 0x4125b0 | media op | U | +| 0687 | 0x4125d0 | media getter | I | +| 0637 | 0x4125f0 | media op | U | +| 0814 | 0x412610 | playback/system getter/setter | I | +| 0642 | 0x412630 | media getter/setter | I | +| 0641 | 0x412650 | media-info query -> play-send | V | +| 0618 | 0x412670 | media op | U | +| 0668 | 0x412690 | media op | U | +| 0619 | 0x4126b0 | media op | U | +| 0669 | 0x4126d0 | media op | U | +| 0722 | 0x412710 | OTA update (wget ota_patch_user.json) | V | +| 0820 | 0x412730 | set key SINGLE-click action | V | +| 0821 | 0x412750 | set key DOUBLE-click action | V | +| 0822 | 0x412770 | set key LONG-press action | V | +| 06b9 | 0x4126f0 | BT op | U | +| 0647 | 0x412790 | set gapless | V | +| 0648 | 0x4127b0 | set artist_class_type | V | +| 0649 | 0x4127d0 | system-config op | U | + +## Table B @0x7ca150 - 75 entries (terminator 0x7ca3a8); many NULL=unimplemented +| tag | handler | meaning | conf | +|---|---|---|---| +| 0425 0435 0445 | NULL | unimplemented | V | +| 0501 | 0x4130f0 | media/thumb type (png/gif/mp4) -> a501/a60a | I | +| 0105 | 0x413110 | emits a102 (status/ack) | I | +| 0202 | 0x413130 | NAS getter/setter | I | +| 0599 | 0x413150 | system/version (-> a599) | I | +| 0401 | 0x413170 | boolean setter -> a401 | I | +| 0411 0450 0451 0452 0453 | NULL | unimplemented (04xx settings) | V | +| 0402 | 0x413190 | boolean setter -> a402 | I | +| 0416 0460 0461 0462 | NULL | unimplemented | V | +| 0403 | 0x4131b0 | boolean setter -> a403 | I | +| 0410 0470 0471 0473 0474 | NULL | unimplemented | V | +| 0404 | 0x4131d0 | setter -> a404 | I | +| 0417 0480 0481 0482 | NULL | unimplemented | V | +| 0405 0418 0419 0420 0421 0422 | NULL | unimplemented (replies declared) | V | +| 0406 | 0x4131f0 | setter -> a406 | I | +| 0426 0407 | NULL | unimplemented | V | +| 0502 | 0x413210 | 05xx misc -> a502 | I | +| 0201 | 0x413230 | Transport play/pause toggle (generic, VALUE1-driven → 0x419ff4) - verified NOT Roon-specific | V | +| 0102 | 0x413250 | playback control (transport) | I | +| 0104 | 0x413270 | playback control (72-byte frame; live: favorite) | I | +| 0111 | 0x413290 (GOT0) | unimplemented stub | V | +| 0112 | 0x4132b0 | playlist: add song (playlist idx, song_path) | V | +| 0115 | 0x4132d0 | add to custom list | V | +| 0113 | 0x4132f0 | love-list delete | V | +| 0114 | 0x413310 | custom-list delete | V | +| 0116 | 0x413330 | playlist op (list mutation) | I | +| 0203 | 0x413350 | NAS op (shares worker w/0412) | I | +| 0412 | 0x413370 | -> reply a412 | I | +| 0413 | 0x413390 | album query (unknown_album) -> a413 | V | +| 0414 | 0x4133b0 | style/genre query (unknown_style) -> a414 | V | +| 0415 | 0x4133d0 | query w/ strcmp -> a415 | I | +| 0465 | 0x4133f0 | -> a465 | I | +| 0495 0496 0497 0498 | NULL | unimplemented | V | +| 0408 | 0x413410 | file/folder browse (mnt/, path build) -> a408 | V | +| 0490 0491 0409 | NULL | unimplemented | V | +| 0117 | 0x413490 | playlist reorder/move (src_list/dst_list) | V | +| 0463 0464 0483 0484 | GOT0 | unimplemented stubs | V | +| 0210 | 0x4134b0 (GOT0) | NAS stub | V | +| 0211 | 0x4134d0 (GOT0) | NAS (json_data, /tmp/nas/) stub | V | +| 0212 0213 0214 | GOT0 | NAS folder op stubs | V | +| 0103 | 0x413430 | song-info/get op | I | +| 0100 | 0x413450 | MAIN PLAY (jumptable @0x650e2c by list_type; unknown_artist) | V | +| 0101 | 0x413470 (GOT0) | unimplemented stub (a101 declared) | V | + +## MCU / SPI name-commands (hw_ctrl.c @0x48d0b8, over internal SPI to MCU) +GET_FIRMWARE_VERSION · GET/SET_DEVICE_MAX_VOL · GET/SET_BALANCED_VOL · REPORT/READ_DEVICE_VOL · SET_DEVICE_VOL · +REPORT_LCD_ACTION · GET/SET_INPUT_MODE · SET_OUTPUT_MODE · SET_GAIN · GET/SET_DAC_FILTER · SET_USB_MODE · +GET/SET_EQ_PRE · GET/SET_EQ_PARAMETER · SET_EQ_RESET · SAVE_EQ/RESAVE_EQ · SET/REPORT_AUDIO_FORMAT · +MCU/ARM_REPORT_STATUS · SET_FACTORY · ENTER_MCU_UPDATE_MODE/REPORT_UPDATE_STATUS · +GET/SET_ZERO_DATA_DETECT_TIME + ZERO_DATA_STATUS · SET_MCU_POWER · SET_MUTE · +SET_STATUS_TO_MCU/SET_POWER_DOWN_TO_MCU (shutdown, 63 call sites) · BT_REPORT_RATE/STATE/CODEC_TO_MCU + +## sysconfig-key setters (system_c... @0x488000) +RGB_COLOUR · TRIGGER_IN · SYS_THEME · LANGUAGE · USB_MODE · NETWORK_MODE · EQ_TYPE · MAX_VOL · BALANCE_VOL · +POWER_SAVE · FILTER_TYPE · PLAY_MODE · FOLDER_JUMP · PLAY_GAP · INPUT_MODE · VOL_KNOB_MODE · OTA_CFG · +PO_PRE_VOL · PO_VOL · PRE_VOL · TREBLE · BASS · LO_DISABLE · OS_MODE · DSD_DECODE + +## Reply frames +~102 `axxx` player->UI frames. Known: a639=media-info, a706=net status, a714=volume, aa1b=UAC srate, a644=now-playing JSON, a704/a705=wifi status, a6c*=BT. Most undocumented. diff --git a/docs/HARDWARE.md b/docs/HARDWARE.md new file mode 100644 index 0000000..5069a47 --- /dev/null +++ b/docs/HARDWARE.md @@ -0,0 +1,298 @@ +# Snowsky Disc - Hardware Capability Map + +Live-probed from the device root shell (serial `/dev/ttyACM0`) on 2026-06-25, while +running **stock firmware** (V2.09 family, kernel built 2026-06-03). This documents what +the *hardware* can do, independent of what stock software chooses to use - to scope +diskOS enhancements and the "write our own mq_player/mq_ui" question. + +Probing was read-only (`/proc`, `/sys`, `aplay --dump-hw-params`, `i2cdetect`-equivalent +via sysfs names, `dmesg`). Nothing was written to the device during this survey. + +--- + +## 1. SoC & Compute + +| Item | Value | Source | +|---|---|---| +| SoC | Ingenic **X2000** (xburst2), board `ingenic,x2000_halley5_module_base` | `/proc/cpuinfo` | +| Cores | **2× XBurst II V2**, SMP | `/proc/cpuinfo` (processor 0,1) | +| Clock | ~1.2 GHz (BogoMIPS ≈ 2390) | `/proc/cpuinfo` | +| FPU | Yes (per-core) | `/proc/cpuinfo` | +| SIMD | **MSA** (MIPS SIMD Architecture, 128-bit) - `ASEs implemented: msa` | `/proc/cpuinfo` | +| ISA | mips1 / mips2 / mips32r2 + MSA | `/proc/cpuinfo` | +| TLB | 288 entries; 1 HW watchpoint; 6 kscratch regs | `/proc/cpuinfo` | +| DVFS | **None exposed** - no `cpufreq/scaling_available_frequencies` | `/sys/.../cpufreq` | +| Thermal | **No thermal zones** - `/sys/class/thermal` empty | sysfs | +| GPU | **None** (X2000 has no GPU) | `/dev` has no gpu node | +| VPU / video decode | **None** - no `vpu`/`video`/`mem2mem` dev nodes | `/dev` | +| Hardware JPEG | **None** as a dev node (stock `jpg_to_png.c` is software) | `/dev`, RE | + +**Implication:** all audio DSP and all UI rendering are CPU-bound. The single biggest +untapped compute lever is **MSA SIMD** - diskOS's LVGL is almost certainly built without +`-mmsa`, so blends/scales/rotations run scalar. Rebuilding LVGL (and any DSP/resampler) +with MSA is the highest-leverage perf win available. + +--- + +## 2. Memory + +| Item | Value | +|---|---| +| RAM total | **120 MB** (`MemTotal: 120308 kB`) | +| Free / available (stock running) | ~19 MB free / ~64 MB available | + +Tight but workable. diskOS already runs in this budget. Big in-RAM buffers (e.g. a +full-res rotating album-art layer) must be sized carefully. + +--- + +## 3. Storage + +NAND (MTD) with **A/B dual-boot** for OTA, plus the user microSD: + +| mtd | size | name | notes | +|---|---|---|---| +| mtd0 | 2 MB | uboot | bootloader | +| mtd1 | 8 MB | kernel | slot A | +| mtd2 | 128 MB | rootfs | slot A - **squashfs, read-only** (why `fiio_init.sh` can't be edited in place) | +| mtd3 | 8 MB | kernel2 | slot B | +| mtd4 | 25 MB | rootfs2 | slot B (smaller - recovery/fallback) | +| mtd5 | 1 MB | ota | OTA state | +| mtd6 | 1 MB | mac | MAC/calibration | +| mtd7 | 83 MB | **userdata** | writable - mounted as `/usr/data`, where **diskOS lives** | +| mmcblk0 | ~238 GB | microSD | single partition `mmcblk0p1` - the music card | + +**Implication:** the **boot hook** is a small edit patched into the read-only rootfs's +`usr/project/fiio_init.sh` (which is why enabling it requires rewriting the rootfs partition - the +flash). The **payload it launches** - the `mq_ui` binary and diskOS's runtime state - lives in the +writable `/usr/data` (mtd7), the safe persistent target. So: the hook is baked into the RO rootfs; +only the UI/state are on `/usr/data`. + +--- + +## 4. Audio - the headline subsystem + +### 4.1 DACs - quad CS43131, fully balanced +Four Cirrus **CS43131** chips on I²C bus 3: + +| I²C addr | sysfs name | /dev node (major) | +|---|---|---| +| 3-0030 | cs43131 | `/dev/cs43131` (248) | +| 3-0031 | cs43131b | `/dev/cs43131b` (247) | +| 3-0032 | cs43131c | `/dev/cs43131c` (246) | +| 3-0033 | cs43131d | `/dev/cs43131d` (245) | + +dmesg tags include `cs43131_left_negetive` and `cs43131b_open` → the four DACs are wired +as **L+/L−/R+/R− (fully differential / balanced)**, two CS43131 per channel. This is an +unusually serious analog design for the form factor. *(Whether the physical jack exposes +balanced (4.4 mm) or sums to single-ended (3.5 mm) is a board question - confirm against +the unit's connectors.)* + +Each CS43131 is a stereo DAC + integrated headphone amp; the family supports PCM to +384 kHz and **DSD64/128/256**. Control is via a **custom FiiO `cs43131` kernel driver** +exposing the four char devices above; stock `mq_player` drives them with **ioctl** (not +ALSA controls). Raw `/dev/i2c-3` is also present as a fallback. + +### 4.2 SoC I²S/DMA path - the streaming ceiling +The Ingenic audio controller (ALSA card 0 `x2000`) exposes **5 playback + 5 capture DMA +channels** (`hw:0,0`-`hw:0,4` playback). `aplay --dump-hw-params` on an idle channel: + +``` +FORMAT: ALL +SAMPLE_BITS: [3 64] +CHANNELS: [1 8] +RATE: [8000 768000] +``` + +So the **kernel/I²S link can stream up to 768 kHz / 64-bit / 8-channel** - far beyond the +current track (S32_LE / 48 kHz / 2ch on DMA3). The real output ceiling is set by the +CS43131 (~384 kHz PCM, DSD256), not the SoC. + +### 4.3 ALSA mixer surface +`amixer controls` shows only the **SoC internal codec** (`ICODEC HPOUTL/MIC GAIN`, +`MICBIAS`), digital mic (`DMIC ...`), line-out muxes (`LO0_MUX`…`LO11_MUX`), and the five +audio-interface formatters (`baic0_fmt`…`baic4_fmt`). There is **no CS43131 control, no +DSD switch, and no digital-filter control in ALSA** - all of that is the kernel driver + +mq_player ioctl path. `BAIC: baic start/stop` in dmesg marks I²S on/off per track. + +### 4.4 Decode & format support (from mq_player RE) +- Decoder backend: **libavcodec.so.58 (ffmpeg)** - string `decoder_ffmpeg`. +- DSD: `DSD_MODE_NONE` / `DSD_MODE_NATIVE` / `DSD_MODE_DOP` - native DSD **and** DoP. +- MQA: `is_mqa` flag (detection/passthrough). +- Containers: FLAC, APE, DSF/DFF, **SACD ISO**, CUE sheets; ffmpeg covers ALAC/OPUS/ + WavPack/etc. +- Param validation: `check_sample_param`, `reset hw params`, `AudioCodecOpen dsd`, + `no support sample! dsd_mode/out_dev/...` - the player gates rate/format per DAC mode. + +**Tools present:** `aplay`, `amixer`, `tinyplay`, `tinymix` - raw PCM playback is possible +today (the DAC just has to already be configured for the rate). + +--- + +## 5. Display + +| Item | Value | +|---|---| +| Driver | `ingenicfb` | +| Visible | 360×360, 32 bpp (XRGB8888/BGRA, panel mounted 180°-rotated) | +| Framebuffer virtual | 360×**1080** = triple-buffered 360×360 | +| **Overlay layers** | `fb0`-`fb3` = **4 hardware LCDC planes** (`/dev/fb0..fb3`) | +| Backlight | standard `backlight` class, **41 levels (0-40)** | + +**Layer control interface (confirmed via sysfs):** `ingenicfb` exposes `layer0`-`layer3` +under `/sys/class/graphics/fb0/device/`, each with `enable`, `src_fmt`, `src_size`, +`target_pos`, and **`target_size`**. `target_size` ≠ `src_size` ⇒ the LCDC has a +**per-layer hardware scaler** (notable, since there's otherwise no GPU/VPU). layer0 is the +active UI plane (`enable: 1`, src 360×360). Layers position + scale in hardware but **do +not rotate**. + +**Implication:** static scaled art/backdrops *could* be HW-composited on fb1-3 (e.g. a +scaled cover or a dim backdrop under the LVGL UI) with no CPU blend. BUT: +- A **spinning** cover still needs software rotation (layers don't rotate) - LVGL already + does this fine, so the overlay is an optimization, not a requirement. +- **Alpha/blend mode is NOT in sysfs** (no alpha/zorder/colorkey node) - likely an FBIO + ioctl in the ingenicfb driver; blending behavior is **unverified**. +- **Visual confirmation needs a camera:** `fbshot` reads fb0's memory, but overlays + composite at *scanout*, so an fb1 test pattern won't appear in an fb0 capture. Defer the + live overlay test until we're ready to pursue HW-layer art and can eyeball the panel. + +--- + +## 6. Input + +| Device | node | notes | +|---|---|---| +| Touch | `/dev/input/event1` - **cst816t** | single-finger panel, but speaks **MT type-B** protocol (slot/tracking-id/ABS_MT_POSITION_X/Y/TOUCH_MAJOR/PRESSURE; no plain ABS_X/Y). Caps `EV=0xb`, `ABS=0x6618000` (high word). | +| Keys | `/dev/input/event0` - **x2000_key** | **physical buttons** (GPIO keys) | + +We can synthesize input by writing the 32-bit-ABI `input_event` (16-byte) MT-B sequence to +`/dev/input/event1` - verified working (used to drive stock's UI pages over serial during RE). diskOS can also read the hardware keys via event0. + +--- + +## 7. Power + +| IC | I²C | role | +|---|---|---| +| **SGM41513** | 2-001a | battery charger (Li-ion, ~3A class) | +| **CW221X** (Cellwise) | 2-0064 | battery **fuel gauge** → `/sys/class/power_supply/cw221X-bat` | +| **AW35615** | 2-0022 | **USB-C PD / CC** controller (Type-C orientation + power delivery) | + +Live read: capacity 100%, 4.32 V, source "Mains". The fuel gauge gives real %/voltage; +`/dev/usbcc_ioctl` (from RE) is the PD/CC control path. **PD negotiation hardware exists**, +so faster charging / power-role awareness is at least theoretically addressable. + +--- + +## 8. Connectivity (wireless) + +| Item | Value | Source | +|---|---|---| +| Module | **AP6212** = Broadcom **BCM43438 / 4343A1** | dmesg: `chip:0xa9a6`, `fw_bcm43438a1.bin`, `nvram_ap6212a.txt` | +| WiFi | **2.4 GHz only**, 802.11 b/g/n (single-band) | BCM43438 spec | +| WiFi attach | SDIO (`[dhd]` driver v101.10.591.91.40, 512 KB dongle RAM) | dmesg | +| Bluetooth | Broadcom BT over **UART `/dev/ttyS0`** @3Mbaud (`hci0`, BD address (device-specific, redacted)) | `hciconfig` | +| BT firmware | `BCM4343A1_001.002.009.1026.1055.hcd` (the `BCM4345C5/C0` `.hcd` files are leftovers for other FiiO models) | `/lib/firmware/bt_bcm` | +| BT bring-up | **hci0 is NOT attached at boot** - `bcmdhd.ko` only loads the driver/GPIOs. The HCI iface is created on-demand by `brcm_patchram_plus --enable_lpm --enable_hci --baudrate 3000000 --patchram <.hcd> /dev/ttyS0`, then `/usr/project/bluetoothd` (a2dp,avrcp,source) + `bluealsa --device=hci0` (SBC/LDAC). **Stock = bluez; `bsa_server`/`bt_enable_bsa*.sh` = dead code for the wrong chip.** Decoded from `mq_player` strings. | mq_player RE | + +> **Corrects prior note:** earlier memory said "BCM4345C5". The actual silicon is +> **BCM43438 (AP6212)** - single-band 2.4 GHz + BT 4.x. + +**Implication:** no 5 GHz → WiFi music transfer / streaming is capped at 2.4 GHz real +throughput (tens of Mbps, congestion-sensitive). BT codec quality (LDAC/aptX) is a +userspace-stack question, not a chip blocker for A2DP. + +--- + +## 9. USB + +| Item | Value | +|---|---| +| Controller | **DWC2 OTG** (`13500000.otg_new`) - **dual-role** (host *or* device) | +| Current mode | device; gadget `serial_demo` exposing **ACM** only (VID 0x0525 / PID 0xa4a7) → this *is* our serial shell | +| USB-DAC mode | stock "UAC" work-mode reconfigures the gadget to **USB Audio Class** (device-as-DAC for a host PC) | + +**Untapped:** DWC2 is OTG, so **USB host mode is physically possible** - mounting USB +storage, or driving an *external* USB DAC (device as a pure transport). Stock only ships +device-mode (serial + UAC). Host-mode would need role switch + the right gadget/host +config and likely a USB-C OTG adapter. + +--- + +## 10. Misc + +- **RTC:** `rtc0` present and correct - real hardware clock. +- **ADC:** SoC SAR ADC, 6 aux channels (`/dev/jz_adc_aux_0..5`) - analog reads (e.g. + jack/line detect, if wired). +- **Watchdog:** hardware `/dev/jz_watchdog`. +- **LED:** **none.** `/sys/class/leds` is empty and there is **no physical LED** on the + unit (confirmed visually). The `RGB_LEVEL`/`RGB_STATUS` fields in mq_player's config blob + are vestigial, inherited from the halley5 reference design / other FiiO products. + → diskOS should plan **no LED features**. +- **Motion sensors:** none on I²C (no accel/gyro) - no tilt/gesture input despite the + round watch-like shape. + +--- + +## 11. Stock-uses vs hardware-can-do (gap list) + +| Capability | HW supports | Stock uses | diskOS opportunity | +|---|---|---|---| +| MSA SIMD | Yes (128-bit) | UI not built for it (unknown) | Rebuild LVGL/DSP `-mmsa` → faster render/effects | +| LCDC overlay planes | 4 (fb0-3) | fb0 only | HW-composited art/video layer (spinning cover, backdrops) | +| I²S rate | 768k/64b/8ch | ≤384k/DSD256 (DAC-limited) | none beyond DAC; already maxes the DAC | +| DSD native + DoP | Yes | Yes | parity - reuse driver ioctls | +| Quad balanced DACs | Yes | Yes | direct ioctl control for bit-perfect / HW volume | +| USB host (OTG) | Yes | No (device-only) | USB storage / external USB-DAC transport | +| UAC2 gadget | Yes | Yes (UAC mode) | expose/control USB-DAC from diskOS | +| USB-C PD | Yes (AW35615) | basic charge | PD-aware fast charge / power-role UI | +| Physical keys | Yes (x2000_key) | Yes | map HW buttons in diskOS | +| WiFi 5 GHz | **No** | - | hard ceiling: 2.4 GHz only | +| GPU / VPU / HW JPEG | **No** | - | hard ceiling: video is CPU-only (MSA-assisted at best) | +| Thermal / DVFS | **Not exposed** | - | no power/thermal tuning via standard sysfs | +| LED | **None** | vestigial config | none - drop from plans | + +--- + +## 12. "Write our own mq_player / mq_ui?" - assessment + +**mq_ui: already done.** diskOS *is* our own UI; it reuses stock `mq_player` purely as an +audio engine over mqueue IPC. No reason to change that split for UI work. + +**mq_player: a full rewrite is high-effort but the local-playback core is feasible.** +What a from-scratch local player needs, and how hard each piece is: + +| Piece | Feasibility | Notes | +|---|---|---| +| Decode | **Easy** | reuse on-device `libavcodec.so.58` (same as stock) | +| PCM out | **Easy** | tinyalsa/libasound to card 0; up to 768k/64b | +| DAC control (rate/DSD/filter/volume/mute) | **Medium** | open `/dev/cs43131*` and replay the kernel-driver **ioctls** - must RE the ioctl numbers + sequences from `mq_player.asm` (bounded but real work). This is the **critical enabler**. | +| Native DSD / DoP | **Medium** | once DAC ioctls are known, feed the right format | +| MCU glue (keys/charge/USB-detect/power) | **Medium** | stock player talks to the FiiO MCU; our player must too (MCU command surface mapped during RE) | +| Streaming receivers (AirPlay/DLNA/Roon/QPlay/BT-sink) | **Hard** | large independent stacks - **do not rewrite**; keep stock or graft open-source (e.g. shairport-sync) | + +**Recommendation (hybrid, incremental):** +1. **Keep stock mq_player** as the engine for now - it already handles DAC/DSD/streaming/ + MCU and diskOS drives it fine over IPC. +2. **RE the `cs43131` ioctl interface regardless** - it's the key that lets diskOS (or a + future thin player) control bit-perfect output, hardware volume, DSD, and filters + directly. Highest-value RE next step. +3. **Only build our own *local* player** if we need something stock won't expose - e.g. + software parametric EQ, ReplayGain, custom gapless/crossfade, or a DSP chain - using + ffmpeg + tinyalsa + the RE'd DAC ioctls. Leave streaming to stock. + +The streaming receivers and MCU dependency are what make a *complete* replacement +expensive; the audiophile local-playback path is the tractable, high-value slice. + +--- + +## Appendix: probe commands (reproducible) +- SoC/mem: `cat /proc/cpuinfo /proc/meminfo`, `uname -a` +- Audio: `cat /proc/asound/{cards,pcm}`, `aplay -l`, `aplay -D hw:0,0 --dump-hw-params /dev/zero`, `amixer -c0 controls` +- I²C map: `for d in /sys/bus/i2c/devices/*/name; do echo "$d: $(cat $d)"; done` +- Power: `cat /sys/class/power_supply/*/{type,capacity,voltage_now,status}` +- Storage: `cat /proc/partitions /proc/mtd` +- USB: `ls /sys/class/udc`, `cat /sys/class/udc/*/state`, `ls /sys/kernel/config/usb_gadget/*` +- Wireless: `dmesg | grep -iE 'dhd|bcm|chip'`, `hciconfig -a`, `ls /lib/firmware/{wifi_bcm,bt_bcm}` +- Display/input: `cat /sys/class/graphics/fb0/{name,virtual_size,bits_per_pixel}`, `ls /dev/fb*`, `cat /proc/bus/input/devices` +- Engines: `ls /dev | grep -iE 'ipu|vpu|jpeg|adc|watchdog'` diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md new file mode 100644 index 0000000..6dd2720 --- /dev/null +++ b/docs/PRIVACY.md @@ -0,0 +1,61 @@ +# diskOS privacy and network disclosure + +This lists everything the installer and the diskOS UI (`mq_ui`) send over the network. The UI ships +as a binary-only component (source not yet published), so this is a good-faith disclosure based on the +code and observed behaviour, not a guarantee; it will be tightened when the UI source is published. + +## The installer (this repo) + +- **`./install.sh` uses `pip`** to upgrade `pip` itself and download two packages - `pyusb` and + `pycryptodome` - from **PyPI** into a local `.venv`. That `pip`/PyPI traffic is the only network + activity in setup. Point `pip` at your own mirror if you prefer, or pre-install the packages offline. +- **The installer app itself makes no network requests.** Building the image, decrypting/extracting + your firmware, saving the recovery image, and flashing over USB are all fully local. It does not + phone home and sends no telemetry. + +## The diskOS UI (`mq_ui`) on the device + +diskOS is a local music player. The online features below reach out **only when you use them**; each +request unavoidably reveals your device's public IP to the service it contacts. + +| Feature | Endpoint | Protocol | What is sent | +|---|---|---|---| +| **Weather** | `wttr.in` | **plain HTTP** | A location string if you set one, else nothing - wttr.in then **auto-geolocates you by IP** (approximate). Over HTTP, so treat it as visible on your network. | +| **Lyrics** | `lrclib.net` | HTTPS | The current track's **title and artist**, to find matching lyrics. | +| **Scrobbling (Last.fm)** | `ws.audioscrobbler.com` | HTTPS | If enabled and connected: the tracks you play (artist/title/album/timestamp) are reported to **your** Last.fm account. | +| **Last.fm sign-in** | `www.last.fm/api/auth/` | HTTPS | Only a QR code containing the approval URL is shown; **your phone**, not the device, opens it. `www.last.fm/api/account/create` is shown as text so you know where to get an API key. | + +### Last.fm credential setup happens over your LOCAL network in plaintext + +To connect Last.fm, diskOS runs a **temporary web server on the device** at +`http://:8080//`, shows a QR for it, and your phone posts your Last.fm +**API key and shared secret** to it. Important properties: +- It is **plain HTTP on your Wi-Fi LAN** - your API key/secret cross your local network unencrypted. + Do this on a network you trust. +- The server is **transient** (runs only during setup), bound to the Wi-Fi interface, and gated by a + random URL token. +- Your Last.fm **API key, secret, and session key are then stored on the device** under `/usr/data` + (device config); **offline scrobbles are queued** in `/usr/data/lastfm.queue` until they can be sent. + Nothing Last.fm-related is sent anywhere except your device, your phone (during setup), and Last.fm. + +> **Last.fm is BETA and unverified end-to-end** - see the README's Known Issues. The transport and +> signing are tested, but a full connect-and-scrobble round-trip to a live account has not been. + +### The stock FiiO player still runs alongside diskOS + +diskOS replaces only the UI; FiiO's original player process still runs underneath it. That stock +component - not diskOS - is responsible for any Bluetooth, firmware-update checks, and LAN media +endpoints (DLNA/AirPlay/Roon/QPlay-style discovery) the device may present on your local network. Those +behaviours are inherited from the stock firmware and are outside diskOS's code. + +## What diskOS does NOT do + +- No analytics, telemetry, or usage tracking. +- It does not upload your music library, your listening history (beyond Last.fm scrobbles if you turn + them on), or any personal files. +- If you never enable weather, lyrics, or Last.fm, diskOS itself makes no outbound requests. + +## Reporting + +Report anything that looks like unexpected data leaving the device via the process in +[`SECURITY.md`](../SECURITY.md). diff --git a/docs/RE_CATALOGUE.md b/docs/RE_CATALOGUE.md new file mode 100644 index 0000000..ecb5f19 --- /dev/null +++ b/docs/RE_CATALOGUE.md @@ -0,0 +1,427 @@ +# Snowsky Disc - mq_player / mq_ui Reverse-Engineering Catalogue + +Teardown of the stock V2.09 firmware binaries to document everything the player +and UI are capable of, so diskOS (our LVGL UI) can drive every feature. + +- Targets: stock `usr/bin/mq_player` (4126620 B) and `usr/bin/mq_ui` (4418300 B). + MIPS32 little-endian, **glibc dynamically linked** + (interp `/lib/ld-linux-mipsn8.so.1`; 28 DT_NEEDED incl libc.so.6/libsqlite3/FFmpeg - NOT + static musl; only *our* diskOS binary is static-musl). +- Disasm cache (regenerate with `mipsel-linux-gnu-objdump`): + disassembly, data, and string dumps of the two binaries. +- Verification: addresses below were spot-checked against the cache on 2026-06-25. + Items marked **(unverified)** have not yet been individually confirmed at the + instruction level. + +--- + +## ⚑ CORRECTIONS - full verification pass (2026-07) +Handler-level RE of both binaries; corrections spot-checked against the binary and applied inline below. +- **Binaries are glibc dynamic, not static musl** (interp `/lib/ld-linux-mipsn8.so.1`, 28 DT_NEEDED). Affects any replacement-binary / preload work. +- **IPC frame = `TAG(4) + TOTAL_LEN(4hex) + VALUE1(4hex) + optional payload`** - LEN is *total*, not payload length; class-2 cmds add VALUE2. Builder `mq_ui 0x43f82c` (`%s%04X%04X%s`). Replies `%s%04X%04X%s` to `/ui`. Queue `/player` maxmsg=20 msgsize=8192. +- **`06b3` = BT CODEC SELECT** (0=SBC 1=AAC 2/3/4=LDAC; worker 0x40eaf4 → `set_out_dev_sample_array(2,44100,44100)` for SBC/AAC, 96000 for LDAC). **`06b4` = LDAC quality only** (mob/std/high via /usr/data/bt_pipe_recv). ← the two were swapped in our old notes. +- **✅ BT AUDIO OUTPUT NOW WORKS (2026-08-03, live-fixed).** The DAP CAN transmit to a BT speaker (a2dp-source) - it just wasn't exposed and the code is fragile. The crash we hit was NOT the 06b3 codec value and NOT unfinished code: it was **skipping the mandatory `0666000C0006` pre-stop** before `0666000C0002`. Full working route sequence + the `--sbc-quality=medium` (stereo, no stutter) fix → **COMMAND_MAP.md "BT AUDIO OUTPUT" section**. The earlier "SBC fix frame = 06b3000C0000" was wrong/incomplete: diskOS uses `06b3001D0000` after the pre-stop + `06c1` init. +- **`06b1`/`06b2`** = BT sample-rate / bit-depth params (not list/query). +- **`0689`** = EQ preset *apply* (21-way engine 0x449544), not a DB-only/media-info op. +- **`0201`** = generic transport toggle (not Roon-specific). **`0657`/`0666`/`06b3` "close_player" = false positive** (shared teardown preamble). `0666`=route (2=BTSRC 4=SPDIF 6=DAC). +- **out_dev** enum has no BTSINK (that's an input/work-mode). 48k→44.1k resample is `libfiio_decoder` over FFmpeg `libswresample` - a stock path, so codec-feasible (CPU headroom still needs an on-device xrun benchmark). +- **IPC-hardening RISK:** stock parser trusts declared LEN and uses `strlen` over the real `mq_receive` byte count → diskOS must emit strictly-correct total lengths and never forward untrusted frames. +- Still UNVERIFIED / deep-RE TODO: exact `0642` receiver-mode values + AirPlay/DLNA/Roon/QPlay trigger sequences; `0715` runtime volume callback @0x822b14; full `SET_USB_MODE` wire encoding; `0722` OTA worker chain; CS43131 ioctl ABI; the remaining ~190 family-inferred tag meanings. + +--- + +## 1. How mq_player is driven - the command dispatch + +mq_player is a background command server. The UI never touches audio; it mails +text commands to a POSIX message queue **`/player`**, and the player mails status +frames back on **`/ui`**. + +### Wire format +- Command frame: `TAG(4 ASCII) + LEN(4 ASCII hex) + DATA`. +- Response builder @ **0x488d64** uses format `%s%04X%04X%s` + (tag, then two 16-bit hex fields, then payload). **(verified: prologue @0x488d64)** + +### Dispatch tables (verified structure) +Two arrays of 8-byte `{descriptor_ptr, handler_ptr}` entries: + +- **Table A @ 0x7c9d30** - primary handlers. Entry 0 = `{0x64f6e0, 0x411790}`. + ~131 entries. **(count unverified; structure verified)** +- **Table B @ 0x7ca150** - secondary/extended. Many entries have + `handler = 0x00000000` = **declared but unimplemented** (e.g. `{0x64fca8, NULL}`). + ~76 entries. **(count unverified; NULL-handler structure verified)** + +Note: 0x7b7870 / 0x7b7c90 (noted in older sessions) are **PLT stubs**, not the +real tables. ~194 tag-like ASCII strings exist in `.str` total; ~30 of the table +slots are NULL / reloc-only / unimplemented (NAS / qplay family). **(unverified)** + +A command arrives → tag matched against table → handler thunk → real handler. +Most handlers are tiny getters/setters over an in-memory settings struct. + +### Confirmed action commands (tag strings verified present in `.str`) +| Tag | Action | +|------|------------------------------------------| +| 0100 | play by list (list_type + index) | +| 0103 | seek | +| 0104 | favorite / unfavorite | +| 0201 | transport (play/pause/next/prev) | +| 0202 | request current state | +| 0622 | rescan SD / rebuild DB | +| 0657 | source/work-mode transition (NOT play-mode) | +| 0666 | set output route (2=BTSRC 4=SPDIF 6=local-DAC) | +| 0689 | select+APPLY EQ preset (21-way engine 0x449544; NOT DB-only) | +| 0715 | set volume | + +(0100/0103/0104/0201/0202/0622/0657/0666/0689/0715 all confirmed as ASCII tag +strings in `mq_player.str`. Full 207-tag enumeration is in the raw table dump and +should be transcribed here in a follow-up pass.) + +--- + +## 2. Audio pipeline + +Decode → process → output. + +### Decode +- **FFmpeg** stack for mp3/flac/wav/aac/aif/m4a/ape/ogg/wma. **(unverified)** +- **libfiio_decoder** for DSD / DFF / DSF / SACD-ISO; DTS. **(unverified)** +- libsndfile present. **(unverified)** + +### DSD / DoP +- Modes: NATIVE / DOP / D2P. DoP carrier up to 768k. Roon `configure_*` files in + `usr/project/config/roon/` corroborate (configure_768_dop, _384_d2p, etc.). + **(config files verified on disk; mode enum unverified)** + +### Output routing (`out_dev`, tag 0666) +LOCAL_ANALOG (CS43131 headphone DAC) / BTSRC / USB_HOST / SPDIF / I2S3. +(BTSINK is NOT an out_dev route - it is an input/work-mode; see §1 and the work-mode enum in §7.) +**(unverified)** + +### Volume - set_volume @ 0x489558 **(verified: prologue @0x489558)** +- Applied by shelling out to an amixer control named **`ICODEC HPOUTL GAIN`** + (string verified @ 0x264dd4 in `.str`). +- Uses inotify to react to external volume changes. **(unverified)** +- On-board MCU over SPI also carries gain/filter/mute opcodes + (SET_EQ_PARAMETER 0x100B etc.). **(unverified)** + +### Bluetooth +- bluealsa-based; source + sink; codecs sbc / aac / ldac. **(unverified)** +- **Stock BT stack = BLUEZ, decoded from `mq_player` strings** (NOT BSA - `bsa_server`/`bt_enable_bsa*.sh` + are dead code, wrong chip BCM4345C5, no caller; mq_player references bsa_server 0×). Full bring-up embedded + in mq_player: `brcm_patchram_plus --enable_lpm --enable_hci --no2bytes --tosleep 200000 --baudrate 3000000 + --patchram /lib/firmware/bt_bcm/BCM4343A1_001.002.009.1026.1055.hcd /dev/ttyS0` (downloads the chip fw patch + over UART → creates working hci0; nothing does this at boot) → `/usr/project/bluetoothd --noplugin=sap + --plugin=a2dp,avrcp --mode=source` → `bluealsa -S --device=hci0 --profile=a2dp-source --ldac-abr + --ldac-quality=standard --codec=sbc --initial-volume=48` → `hciconfig hci0 up/piscan/class 0x200414` → + `bluetoothctl agent on/default-agent/pairable on`. Sink mode = no-LPM patchram + `--mode=sink -p hfp-ag + --codec=sbc/aac/ldac`. The missing patchram step is why a naive BT enable produces a + poor scan. + +### Gapless +- `audio_track_update_play_gapless`. **(unverified)** + +### ReplayGain +- Stored / read from DB. **(unverified)** + +--- + +## 3. EQ - the apply path (the key unlock) + +The EQ is a **software parametric biquad cascade inside mq_player**, NOT a +hardware DAC feature. **CORRECTION:** `0689` does NOT merely write +to SQLite - its handler (`0x4961bc`) invokes the **21-way preset engine `0x449544`**, +updates the rate caps and applies the DSP, then replies `a639`. Custom bands are set +separately via `0678` (`0x44a658`). Both are live apply paths, not DB-only. + +### DSP engine (verified prologues exist) +- Coefficient calc @ **0x448144** - uses pow/sqrt/sincos to turn + {filterType, frequency, gain, qValue} into biquad coefficients. +- IIR cascade @ **0x447ed0** and @ **0x4483a4** - per-sample filter, 32-bit + saturation. **(verified: both are function prologues)** + +### Storage - PEQ table (SQL strings verified in `.str`) +Columns: `STYLE_NAME, MASTER_GAIN (REAL), PARAMS_JSON (text), STYLE_PRESET (int)`. +- PARAMS_JSON = array of bands `{filterType, frequency, gain, qValue}` (up to 10) + + a master gain. +- Confirmed SQL @ ~0x257cb8 region: + - `INSERT INTO PEQ (STYLE_NAME, MASTER_GAIN, PARAMS_JSON, STYLE_PRESET) VALUES ('%s', %.1f, '%s', %d)` + - `UPDATE PEQ SET STYLE_NAME = ?, MASTER_GAIN = %.1f, PARAMS_JSON = ? WHERE STYLE_PRESET = ?` + - `SELECT 1 FROM PEQ WHERE STYLE_PRESET = ? LIMIT 1` + +### To apply a custom EQ from diskOS +1. Write/UPDATE the PEQ row (bands JSON + master gain) for a STYLE_PRESET. +2. Send **0689** to select that preset. +The player loads bands → computes coeffs @0x448144 → runs the cascade +@0x447ed0/0x4483a4 on every sample before output. + +### ✅ WIRED + CONFIRMED in diskOS (2026-06-30) +Custom EQ is live: `eqcustom.c` (10-band UI, horizontal scroll) → `mdb_set_peq()` writes PEQ +slot 11 in the format below → `ui_apply_eq(11)` sends 0689 → **player applies, sound changes +on-device (user-verified).** `ui_apply_eq` clamp raised 0x0A→20 to reach user slots 11-20. + +### CAPTURED PARAMS_JSON format (live, V2.09, 2026-06-30) +Saved a stock "User 1" custom EQ (+12 @ 32 Hz, −12 @ 16 kHz) and read it back. Ground truth: +- **10 fixed bands**: 32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz, each ±12.0 dB; plus a **master ±12 dB** = the `MASTER_GAIN` REAL column. +- PARAMS_JSON = JSON **array of 10 objects**, e.g. one band: + `{"filterType":0,"frequency":32,"position":0,"gain":"12.0","qValue":"0.7"}` + - `filterType` = 0 (peaking) - **int**; `frequency` Hz - **int**; `position` 0-9 band index - **int**. + - **`gain` and `qValue` are JSON STRINGS** (quoted: `"12.0"`, `"-12.0"`, `"0.7"`), NOT numbers. gain is one-decimal dB; default qValue `"0.7"`. +- **User slot → STYLE_PRESET**: User 1-10 = **11-20** (User 1 = 11). Built-in presets = 0-10 (off,jazz,rock,r&b,hip-hop,pop,dance,classical,retro,sibilance-atten-1,sibilance-atten-2). Rows 160-169 exist but are unused/placeholder (`PARAMS_JSON` = literal string `"(null)"`). +- Stock writes via `UPDATE PEQ SET ... PARAMS_JSON = ? WHERE STYLE_PRESET = ?` then selects with **0689000C** (User 1 = `0689000C000B`). +- Other stock audio settings seen in the same menu (candidates for diskOS, map to documented name-cmds): Gain H/L, BT codec, SPDIF on/off, DAC digital filter (Fast/Slow LL, Fast/Slow PC, NOS, Wideband FF), DRE on/off. + +--- + +## 4. The `/ui` frames - player → UI + +mq_ui opens the **`/ui`** mqueue (name built @0x41b388, recv thread @0x415584, +`mq_receive` wrapper @0x41b5c8). Each frame = `TAG(4) + %x extension + JSON "other"` +(parser @0x415674: `strncpy …,4` then `sscanf %x`). Dispatch is a +`strncmp(cmd,tag,4)` chain in `ui_ctrl_response` (@0x41bfd0-0x41c098). Recv log +`[UI RECV]: %s` @0x272960. **(IPC path + format instruction-verified)** + +Player→UI frames use **`a`-prefixed** tags (replies/reports) + `06d*` (OTA). Tag +meaning is taken from the adjacent log string (string-inferred, high confidence). + +| TAG | meaning | payload | +|------|---------|---------| +| a620 | version reply | version `%s` | +| a710 | max-volume reply | `maxVolume` | +| a715 | volume / UAC srate change | `currentVolume` | +| aa1b | UAC sample-rate change | rate; `usbAudio` | +| a704/a705 | wifi connect status | state; `wifi_ssid` | +| a706 | net status | `%d` | +| a615 | language config | `%d` | +| a609 | theme config | `%d` | +| aa27 | charge-protect | `charge_protect` | +| aa24 | sys config | `%d`; `sys_count` | +| a634 | memory-play | `memoryPlay`/`memoryType` | +| a639 | EQ type | preset + `filterType/frequency/gain/qValue` | +| a6b6 | BT paired devices | count + list | +| a6c5/a6c3/a6c2/a6c1/a6c4/a6c0 | BT disc/connect/open replies | state + address | +| aa1c | power-key event | `%d` | +| aa22 | TF-card (SD) insert/remove | event `%d` | +| aa0c/aa0f | screen status | `%X` | +| a60a | tip/toast popup | tip code | +| a644 | now-playing / UI state update | song JSON (below) | +| a622 | SD rescan / DB-rebuild status | scan state | +| 06d0/06d1/06d2 | OTA progress / success / file-count | `%d` | + +Verified-present log strings: GET_VERSION_REPLY @0x27255c, GET_WIFI_CONNECT_STATUS +@0x272658, UAC_SRATE_CHANGE @0x27252c. + +**Now-playing JSON payload** (fields verified in `.str`): `song_name`, `song_artist_name` +(@0x271b90), `song_album_name`, `song_style_name`, `song_track`, `song_channel`, +`song_duration_time` (@0x271b34), `duration`, `songposition` (@0x271c2c), +`song_sample_rate`, `song_encoding_rate`, `song_bit_rate`, `song_mimetype`, +`song_file_path`, `is_sacd/is_cue/is_dsd`, `love` (favorite), `state` (play state), +`playerflag` (@0x271c08), `curlistlength`, `pos_id`, `playing_num`, +`work_mode` (@0x271b… 0x272b34), `battery` (@0x272c58). Album art → width/height/ +quality/rgb → `/usr/data/fiio/cover.png`. + +→ For diskOS status indicators (battery/BT/wifi) we read tags a704/a706 (wifi/net), +the BT a6c* family, and `battery` inside a644. **This is the data we were missing.** + +### mq_ui feature surface (subsystems) +IPC core (`/ui` in; `/player`,`/bt_control` out) · frame dispatch (cJSON) · local +browse/play (all-songs/album/artist/style/favorite/custom, `db_song_ctrl.c`) · +now-playing (`class_play_screen.c`, album art `jpg_to_png.c`) · Roon endpoint · +home/menus (app/system/audio/others/popup `.json`) · EQ (`equalizer.json`) · +Bluetooth (`bt.json`) · WiFi/NAS (`wpa_supplicant`, `hostapd`, `wl rssi`) · +system/version/OTA (`ota_update.c`, `set_local_time.c`) · FiiO Link (UDP +224.0.0.255 discovery + TCP control, device id "SNOWSKY DISC") · bundled zlog. + +--- + +## 5. Config + database schema + +SQLite 3.23.1, opened via `sqlite3_open_v2`. DB files (paths verified in `.str`): + +| File | Holds | +|------|-------| +| `/usr/data/fiio/db/sysconfig.db` | SYSCONFIG (settings, single row ID=1), CUSTOM_THEME, NAS_CONFIG | +| `/usr/data/fiio/db/song.db` | SONG, MY_LOVE, MEMORY_PLAY, PLAY_LIST, LIST_SONG_0/1/2, CUSTOM_PLAYLIST, PLAYLIST_INFO, PEQ | +| `/usr/data/fiio/db/dic.db` | HAN_PINYIN (CODE INTEGER, PINYIN char(1)) - CJK pinyin sort (on-disk verified) | +| `/usr/data/fiio/db/theme.db` | theme assets | +| `/usr/data/fiio/db/ebook.db` | e-book | +| `/usr/data/bluetooth.db` | bt_devices | + +Only `dic.db` ships in the rootfs; the rest are created at first boot under +`/usr/data/fiio/db/`. db→file binding is from co-located source names + init block +(high confidence for SONG-family→song.db, SYSCONFIG/theme/NAS→sysconfig.db). + +### SONG (song.db) - literal CREATE for MY_LOVE verified verbatim; SONG = same + IS_LOVE +ID(PK) · PATH · NAME · TITLE · ALBUM · ARTIST · GENRE · DISC · TRACK · IS_CUE · +IS_ISO · IS_DSD · OFFSET(BIGINT) · DURATION(BIGINT) · NAME_CODE · TITLE_CODE · +ALBUM_CODE · ARTIST_CODE · GENRE_CODE(pinyin sort keys) · ADD_TIME(INT8) · +SAMPLE_RATE · BIT_PER_SAMPLE · CHANNELS · BIT_RATE · SONG_MIMETYPE · +SONG_PRODUCTION_YEAR · IS_SELECT · ALBUM_ARTIST · ALBUM_ARTIST_CODE · +**IS_LOVE** (SONG only, added by upgrade ALTER - favorites flag). +MY_LOVE = same minus IS_LOVE, `UNIQUE(PATH,TRACK)`. **(CREATE verified @mq_ui.str:9793)** + +### CUSTOM_PLAYLIST (song.db) - literal CREATE verified @mq_ui.str:9790 +MY_LOVE columns + `PLAYLIST_ID` (after ID), `FOREIGN KEY(PLAYLIST_ID) REFERENCES +PLAYLIST_INFO(ID) ON DELETE CASCADE`, `UNIQUE(PLAYLIST_ID,PATH,TRACK)`. + +### PLAYLIST_INFO (song.db) - parent of CUSTOM_PLAYLIST. ID(PK) firm; name/count inferred (LOW confidence). + +### PLAY_LIST (song.db) - queue registry: ID(PK) · LIST_ID · LIST_NAME. + +### LIST_SONG_0/1/2 (song.db) - active play queues, built by `INSERT INTO LIST_SONG_%d … SELECT … FROM SONG` (verified). Cols: ID · LIST_ID · POS_ID(=MUSIC_ID join key) · PATH · NAME · TITLE · ALBUM · ARTIST · GENRE · DISC · TRACK · IS_CUE · IS_ISO · OFFSET · DURATION · ADD_TIME · IS_SELECT · SONG_TYPE · ALBUM_ARTIST. + +### MEMORY_PLAY (song.db) - resume state, single row. ID(=1) · MUSIC_ID · IS_PLAYING · POSITION · IS_CUE · IS_ISO · TRACK. **(UPDATE verified @mq_player.str:13412)** + +### PEQ (song.db) - see §3. ID · STYLE_NAME · MASTER_GAIN(REAL) · PARAMS_JSON · STYLE_PRESET. + +### CUSTOM_THEME (sysconfig.db) - ID · POS_ID · NAME · PATH · ALIAS · TYPE · IS_SYSTEM · ATTR · ALPHA · LOCK_TIME/DATE/BATTERY/ID3/MEM_TYPE · FRONT_COLOR. +### NAS_CONFIG (sysconfig.db) - ID · NAS_NAME · NAS_IP · USER · (password) · TYPE · AUTO_LOGIN. +### bt_devices (bluetooth.db) - ID · NAME · ADDR(MAC key) · CODEC · SAMPLING · VOLUME. + +### SYSCONFIG (sysconfig.db) - single row, all INT, `UPDATE SYSCONFIG set %s = %d where ID = 1` (verified @0x25a910) +Column names verified from descriptor @mq_player.str:13091+. Runtime pak dump +(verified @0x25e7a4): `DSD_MODE,OUT_DEV,VOLUME,WORK_MODE,LIGHT_LEVEL,LIGTH_ON_TIME +(sic),RGB_LEVEL,MUTE,VOL_MODE,RGB_STATUS,MEM_PLAY`. Full key set (names verified; +**enum value mappings NOT proven** - inferred from name + cross-ref to command tags): + +DSD_MODE · OUT_DEV/DEVICE_OUTPUT(→0666) · VOLUME · WORK_MODE · LIGHT_LEVEL · +LIGTH_ON_TIME(backlight timeout, sic) · RGB_LEVEL · MUTE · VOL_MODE · RGB_STATUS · +RGB_COLOUR · TRIGGER_IN · SYS_THEME · LANGUAGE · USB_MODE · NETWORK_MODE · +EQ_TYPE(→0689, =PEQ.STYLE_PRESET) · MAX_VOL · BALANCE_VOL · POWER_SAVE · +FILTER_TYPE(DAC digital filter) · PLAY_MODE(→0102) · MEMORY_PLAY(resume) · +FOLDER_JUMP · PLAY_GAP(gapless) · INPUT_MODE · VOL_KNOB_MODE · OTA_CFG · BATTERY · +BT_CODEC · SCREEN_ROT · PO_PRE_VOL/PO_VOL/PRE_VOL · THEME_MODE · CHARGE_PROTECT · +LOCK_THEME · TREBLE · BASS · WIFI_STATUS · BT_STATUS · LO_DISABLE · OS_MODE · +DSD_DECODE · SPDIF · AUTO_TIME · DRE_STATUS · LOCAL_IMG_ANIM · IMG_ANIM_BRIGHTNESS · +ARM_VERSION · MCU_OTA_FAILED_TIME · KEY_SINGLE/DOUBLE/LONG_CLICK_SLE(gesture→action) · +ARTIST_CLASS_TYPE · AUDIO_VOLUME_SET. +Config is committed to flash on write (`now try to save config to flash` @0x25a…). + +--- + +## 7. Network streaming + USB modes + COMPLETENESS + +**Major gap found:** the earlier sections missed the network-streaming receivers and several subsystems. The device is far more capable than §1-6 implied. + +### Work-mode / OUT_DEV enum (the master "audio source" list) - names VERIFIED +mq_player has ONE work-mode enum (pointer-array @ ~0x7bb0a0 data; names in `.str` @0x25f9e0+). Switching mode is **name-driven**: a supervisor `@0x444604` does `strcmp(name,"AIRPLAY"/"DLNA"/"ROON"/...)` and starts/stops that receiver. **Names verified present; integer indices unverified:** +`0 NO_DEFINED · 1 I2S3_OUT · 2 USB_HOST_NULL · 3 NO_WORK_MODE · 4 LOCALPLAYER · 5 BTSINK · 6 ANALOG · 7 UAC(USB-DAC) · 8 DLNA · 9 AIRPLAY · 10 ROON · 11 SPDIF_OPT · 12 SPDIF_RX · 13 DMR · 14 DMC · 15 DMS · 16 MIX · 17 I2S_IN · 18 STREAM_AUDIO` (+ QPLAY). sysconfig keys NETWORK_MODE (cfg+0x5c) and OUT_DEV both feed this. + +### AirPlay receiver - BUILT IN (was completely missed) +mq_player embeds a **full shairport-sync fork**: `fiio_airplay.c` + `src/shairport.c, rtsp.c, rtp.c, dacp.c, metadata.c, mdns_avahi.c, player.c` + `libshairplay.so`. Advertises **`_raop._tcp`/`_airplay._tcp`** as **"SNOWSKY DISC"**. Config: `/usr/project/config/airplay2/x2000_airplay2.conf` (Shairport-Sync style, AirPlay 2). Flow: supervisor sees mode "AIRPLAY" → sets `airplay_play=1` (@0x7efbe4) → `start_airplay@0x436008` → pthread `airplay_func@0x435fc0` → shairport loop. Needs **wlan0 up** (`start_switch_network_mode_thread@0x46e634` monitors `/sys/class/net/wlan0/operstate`=="up", then emits `/ui` tag **a706** = GET_NET_STATUS_REPLY - a706 is STATUS, NOT the trigger). Has `airplay_artwork_thread`, DACP remote. Audio → ALSA `snd_pcm_writei` to the DAC after out_dev switch. **TRIGGER COMMAND TAG: NOT yet pinned** (a `06xx` work-mode-switch carrying AIRPLAY=9, sent via `ui_send_cmd@0x43f82c` → mqueue `/player` idx 3, frame `TAG(4)+ext1(4hex)+len(4hex)+data`). Player also has a name-string command dispatch @0x482328 (e.g. `SET_USB_MODE`). **Next step to enable AirPlay from diskOS = read the mq_ui output/work-mode menu handler (callers of 0x43f82c) to capture the literal tag+payload for AIRPLAY/UAC/DLNA/ROON.** + +### Also built-in network receivers (missed): +- **DLNA/UPnP renderer** - `dlna_player.c`, UPnP RenderingControl/AVTransport SCPD. (mode DLNA=8) +- **Roon Ready endpoint** - `roon_player.c` + `libroon.so` RAAT (`roon_transport_*`, volume/seek/shuffle). (mode ROON=10) UI screen `ui_roon_play.c`. +- **QPlay** (Tencent/QQ Music) receiver - `mq_player_server_qplay.c`, MD5(Seed+PSK) auth, SetNetwork/SetLyric. +- **BT SINK** (phone→player A2DP in) - `bt_sink_server.c/bt_sink_control.c` + AVRCP. (mode BTSINK=5) + +### USB modes (partially missed) +USB_MODE sysconfig + `usb_mode_change_handler@0x48eb80` (named cmd `SET_USB_MODE`@dispatch 0x482328). Gadgets via configfs: **`uac_demo`** (USB-DAC, functions uac1.a/uac2.a, /dev/usb_dac, UAC_SRATE→/ui tag aa1b) = work-mode UAC(7); **`storage_demo`** (card-reader, mass_storage.0, lun.0/file=/dev/mmcblk0). `set_usb_host`@0x24da24. **USB_MODE integer values (charge/DAC/reader/host) NOT yet mapped.** + +### CORRECTIONS to earlier sections +- **NAS is FULLY IMPLEMENTED** (`nas_control.c`: `mount -t cifs vers=3.0`, NFS, `smbclient -L`; NAS_CONFIG live) - §1's "NAS/qplay = NULL/unimplemented" was WRONG. +- **Tag counts:** ~**221** distinct `/player` `0xxx` command tags + ~**102** `axxx` reply tags (mq_ui emits ~161 cmds, handles ~102 replies). §1/§4 documented ~10 + ~30 → **~95% of the command surface is still undocumented** (entire a4xx reply family untouched). +- Other missed subsystems: **lyrics** (`lrc_paser.c`, `/usr/data/fiio/encoder.lrc`!), generic stream/I2S_IN/capture (`stream_audio.c`,`player_capture.c`), animated gif screensaver (LOCAL_IMG_ANIM), serial-number (`sn_nb.c`), image loaders (bmp/jpeg/png/yabmp), SACD/DST internals, MCU OTA (`ENTER_MCU_UPDATE_MODE`), full SPI/MCU handler family. + +## 8. FULL command map - inventory complete, meanings mixed-confidence + +**V2.09 dispatch tables** (entry = `{tag_str_ptr, handler_thunk_ptr}` 8B; thunks tail-jump via GOT to real handler): +- **Table A @0x7c9d30 = 131 entries** (terminator @0x7ca148). Tags 06xx/07xx/08xx/0a51. +- **Table B @0x7ca150 = 75 entries** (terminator @0x7ca3a8). Tags 00xx/01xx/02xx/04xx/05xx. ~36 in-table NULL (declared-unimplemented), ~11 thunk-but-GOT-null, ~28 live. +- **MCU/SPI name-commands** via hw_ctrl.c @0x48d0b8 + sysconfig-key dispatch @0x488000 (NOT 0x482328 = that's the tag↔enum resolver). + +**⚠️ RELIABILITY:** tag list = reliable (mechanical table walk). Per-tag MEANINGS: ~36 string-VERIFIED, ~80 INFERRED-from-family, ~15 UNKNOWN(runtime-registered GOT=0). **Some conflict with the 1.95-era LIVE-tested set** (1.95 tables were @0x7b7870/0x7b7c90; V2.09 layout differs). **RESOLVED:** the `0657`/`0666`/`06b3` "close_player" reading was a false positive - all three call a shared teardown preamble at the start of a disruptive mode change; that teardown is NOT their meaning. `0657`=source/work-mode transition, `0666`=output route (2/4/6), `06b3`=BT codec select. **Treat remaining INFERRED meanings as needing verification; trust live tests + resolved-handler traces over family inference.** + +**String-VERIFIED V2.09 commands (Table A):** 0802=song count, 0620=get WIFI_MAC(/usr/data/fiio/nb.txt), 0704=wifi scan list, 0705=wifi connect(psid/passwd), 0800=write wpa_supplicant.conf, 0701=wifi init, 0700=close network card, 0720/0722=OTA(wget ota_patch), 0639/0689/0690/0634/0641=media-info query→a639 reply, 0675/0630=EQ/PEQ get(gain/filterType/frequency), **0678=set PEQ band(filterType/frequency/gain/qValue)**, 0621=DROP DB tables, 0624=mount/storage, 0815=set MEMORY_PLAY position, 0820/0821/0822=key single/double/long action, 0647=gapless, 0648=artist_class_type, **06b1=BT sample-rate param(0x40d4d8), 06b2=BT bit-depth param(0x40d66c), 06b3=BT CODEC SELECT(0=SBC 1=AAC 2/3/4=LDAC; 0x40eaf4; SBC frame `06b3000C0000` pins 44100), 06b4=LDAC QUALITY only(0x40dbe8; mob/std/high)**, 06b7=BT paired, 06c0=BT trust/power, 06c1=BT alias, 06c4=BT disconnect/remove. (0657/0666/06b3 close_player = RESOLVED false positive, see above.) +**Table B verified:** **0100=main PLAY** (jumptable @0x650e2c by list_type), 0201=transport play/pause toggle (generic, →0x419ff4; NOT Roon-specific), 0112=playlist add song, 0115=add to custom list, 0113=love-list delete, 0114=custom-list delete, 0117=playlist reorder(src/dst), 0408=file/folder browse, 0413=album query, 0414=style/genre query, 02xx=NAS scan/browse(mq_player_server_nas.c, many GOT-null/unimplemented). +**MCU/SPI name-commands (hw_ctrl.c, SPI to MCU):** GET/SET_DEVICE_MAX_VOL, SET_DEVICE_VOL, GET/SET_INPUT_MODE, SET_OUTPUT_MODE, SET_GAIN, GET/SET_DAC_FILTER, **SET_USB_MODE**, GET/SET_EQ_PRE, GET/SET_EQ_PARAMETER, SET_EQ_RESET, SAVE_EQ, SET_AUDIO_FORMAT, SET_FACTORY, **ENTER_MCU_UPDATE_MODE**, SET_MCU_POWER, SET_MUTE, SET_POWER_DOWN_TO_MCU (shutdown path, 63 call sites), GET/SET_ZERO_DATA_DETECT_TIME, BT_REPORT_RATE/STATE/CODEC_TO_MCU. sysconfig-key setters: RGB_COLOUR/TRIGGER_IN/SYS_THEME/LANGUAGE/USB_MODE/NETWORK_MODE/EQ_TYPE/MAX_VOL/BALANCE_VOL/POWER_SAVE/FILTER_TYPE/PLAY_MODE/FOLDER_JUMP/PLAY_GAP/INPUT_MODE/VOL_KNOB_MODE/OTA_CFG/PO_PRE_VOL/PO_VOL/PRE_VOL/TREBLE/BASS/LO_DISABLE/OS_MODE/DSD_DECODE. + +## 6. TODO (next passes) +- [x] ~~Live-verify 0657/0666~~ RESOLVED (handler-trace): 0657=source/work-mode transition, 0666=output route(2/4/6). "close_player" was a shared teardown preamble. +- [ ] **Pin the AirPlay trigger tag** (mq_ui work-mode menu → callers of ui_send_cmd@0x43f82c) - the one fact needed to enable AirPlay from diskOS. +- [x] **0622 is NOT a working rescan on V2.09:** diskOS "Rescan Library" sends 0622 → runs a long scan + (~13min, scan_songs_thread) but writes 0 rows to song.db (mtime unchanged). Clearing SONG + reboot also scanned but + wrote nothing. So song.db must be hand-built (laptop `tools/build_db.py` from sync_manifest.json → SONG + + PLAYLIST_INFO + CUSTOM_PLAYLIST; diskOS lib sorts by TEXT so *_CODE only affects play-queue order). The real scan + trigger (comm_scan_songs_thread) is still un-pinned. song.db schema is in this catalogue's table notes / dump from + song.db.bak. Stock leaves DURATION/SAMPLE_RATE/BIT_RATE/CHANNELS=0 and ALBUM=TITLE for tagless rips - match that. +- [x] **USB_MODE card-reader mode = VALUE 2 - LIVE-VERIFIED WORKING (transferred 32GB this way).** This is the clean + stock MSC path for file transfer, and it SIDESTEPS the dangerous 0666 entirely. Details: + - `usb_mode_change_handler` @ **0x48eb??** (log str "usb_mode_change_handler = %d" @vaddr 0x670358, ref'd + by `addiu v1,v1,856` @0x48eb84). Reads/writes current usb_mode global @ **0x822d20**. Branches on the + target mode value (s0): observed cases 2, 5, 6. + - On **usb_mode==2** (`bne s0,2` fall-through @0x48ebc4) it accesses + **`/sys/kernel/config/usb_gadget/storage_demo`** (lui 0x66 + 13620 = vaddr 0x663534) → builds a + `mass_storage.0` function, **lun.0/file = `/dev/mmcblk0`** (whole SD), toggles cdrom/nofua/removable/ro, + binds the UDC. (All configfs paths string-verified @ file 0x263534-0x263d8f.) + - **CLEAN handoff CONFIRMED:** before/while exporting it UNMOUNTS the SD - `unmount_udisk` @0x668ed4 (called + ~7× around 0x470134-0x470388) runs `umount %s` with 5 retries + `umount -lf %s 2>/dev/null` lazy-force + fallback on `/tmp/sdcard` (strs @0x268be8/0x266e40/0x268e40). So it properly releases mq_player's SD hold + (the EBUSY blocker) - no corruption, unlike a raw composite-gadget export. + - Trigger: **`SET_USB_MODE`** name-command (str @vaddr 0x66fe40; dispatch refs @0x48232c/0x4863ec/0x48d154; + builders @0x408044/0x40fa94) and/or the `USB_MODE` sysconfig field. EXACT payload/value-encoding for + SET_USB_MODE still to pin (one more trace), but the value is **2**. + - ⚠ CAVEAT (untested): a UDC binds ONE gadget at a time, so binding `storage_demo` almost certainly UNBINDS + our `serial_demo` ACM → **the serial shell will drop while in reader mode**; exiting reader mode (mode + switch / replug) should restore it. TEST LIVE WITH USER PRESENT (power-cycle fallback). This is still far + safer than 0666 (no player crash / MCU-reboot path). + - ⇒ This unblocks **music-transfer-over-USB** via the stock mechanism: set USB_MODE=2 → laptop sees the SD as + a USB drive → copy 32GB at USB speed + fsck → exit reader mode → in-device rescan. Replaces the unsafe + wifi-push (watchdog reboots) and the blocked raw-MSC attempt. +- [ ] Transcribe the full 221-tag command table + 102 a-frame replies. +- [ ] Transcribe the full 207-tag command table (A+B) with handler addresses. +- [ ] Verify audio/decoder/BT/DSD claims (§2) at instruction level (currently unverified). +- [ ] Resolve SYSCONFIG enum value→option integer mappings. +- [ ] Pin PLAYLIST_INFO's full column list (currently only ID firm). +- [ ] diskOS wiring: status indicators from a644/a704/a706/a6c* + custom-EQ via PEQ write + 0689. + +## 5b. SYSCONFIG - the master settings table (decoded 2026-06-30) +`/usr/data/fiio/db/sysconfig.db` → table **SYSCONFIG** (single row), one INT column per setting. +This is where every audio/system setting persists (the config-named commands write here). +Columns incl: DSD_MODE, OUT_DEV, VOLUME, WORK_MODE, LIGHT_LEVEL(brightness), LIGTH_ON_TIME, +MUTE, VOL_MODE, USB_MODE, NETWORK_MODE, EQ_TYPE(=0689 preset), MAX_VOL, BALANCE_VOL(channel +balance), POWER_SAVE, FILTER_TYPE(DAC filter), PLAY_MODE(=0102), MEMORY_PLAY, FOLDER_JUMP, +PLAY_GAP, INPUT_MODE, VOL_KNOB_MODE, BT_CODEC, SCREEN_ROT, PO_PRE_VOL/PO_VOL/PRE_VOL, +THEME_MODE, CHARGE_PROTECT, LOCK_THEME, TREBLE, BASS, WIFI_STATUS, BT_STATUS, LO_DISABLE, +OS_MODE, DSD_DECODE, SPDIF, AUTO_TIME, DRE_STATUS, DEVICE_OUTPUT, KEY_SINGLE/DOUBLE/LONG_CLICK_SLE, +ARTIST_CLASS_TYPE, AUDIO_VOLUME_SET. +Live sample (V2.09): FILTER_TYPE=1, DRE_STATUS=1, BALANCE_VOL=0, SPDIF=0, MAX_VOL=120, +BT_CODEC=3, MEMORY_PLAY=0, FOLDER_JUMP=0, PLAY_GAP=0, SCREEN_ROT=0, CHARGE_PROTECT=0, +ARTIST_CLASS_TYPE=0, TREBLE=0, BASS=0, OUT_DEV=6, OS_MODE=0, LO_DISABLE=0, DSD_MODE=1, +INPUT_MODE=1, VOL_MODE=1. (No explicit GAIN column - likely VOL_MODE or encoded in OUT_DEV; +confirm by toggling.) Filter enum (others.json 70-75): 0=FAST_LL,1=SLOW_LL,2=SLOW_PC,3=FAST_PC, +4=NON_OS,5=Wideband_FF. To wire a setting in diskOS: write its SYSCONFIG column + send its apply +command (verified: gapless 0647, BT codec 06b3 [0=SBC…4=LDAC-high], artist 0648, memory 0815, keys 0820-22; Gain/ +Filter/DRE/Balance apply-cmds TBD via strace-correlate of stock mq_ui - strace on mq_ui is SAFE, +only strace-on-mq_player triggers the MCU reboot). + +## 5c. Audio/DAC apply-commands - DECODED LIVE (2026-06-30, strace of stock mq_ui) +Captured by strace `mq_timedsend` on stock mq_ui while toggling each setting (SAFE - strace +on mq_ui, never mq_player). Frame format `000C`. Note: SYSCONFIG column +writes are DEFERRED (didn't update live), so these were correlated by the FRAME, not the DB. +Background-noise tags to ignore: 0807 (recurring 0/1 status), 0704 (wifi scan). +- **DRE** = `0812` - `0812000C0000` = ON, `0812000C0001` = OFF (startup sent 0 when DRE on). +- **Gain** = `0645` - `0645000C0000`/`0001` (Low/High; startup sent 0). +- **Channel balance** = `0713` - `0713000C01`; center = `0101`, nudging streamed 0105..010C + (one frame per step; 0x01 hi-byte = channel/side, lo-byte = level). +- **DAC Filter** = `0653` - enum = menu order (clean sweep): 0=FAST_LL, 1=SLOW_LL, 2=SLOW_PC, 3=FAST_PC, 4=NON_OS, 5=Wideband_FF. +- **SPDIF** = via output-route `0666` - SPDIF on = `0666000C0004`, analog = `0666000C0006` + (0666 = OUT_DEV/output route: LOCAL_ANALOG=6, SPDIF=4; mutually exclusive with headphone). +- **BT codec** = `06b3` (**CORRECTED**: mq_ui codec menu 0x464e90 sends 06b3, not 06b4): + codec-settings menu sends payload-less `06b3000C0000`=SBC `…0001`=AAC `…0002/3/4`=LDAC mob/std/high. + **But the ROUTE-to-speaker frame (live-captured 2026-08-03) is `06b3000X`** (X=codec, MAC payload + kept for stock frame-shape; worker ignores it). diskOS route uses `06b3001D0000` (SBC). `06b4` is + LDAC-quality only. **Full working BT-transmit sequence → COMMAND_MAP.md "BT AUDIO OUTPUT" section.** +- Also re-confirmed: `0666`=output route, `0642`=network mode (from startup sync). + +## 5d. More settings - DECODED LIVE 2026-06-30 (strace stock mq_ui, clean batch) +- **Channel balance** = `0713` - `0713000C`: center=`0000`; one side `00NN` (NN=step), other side `01NN`. (Same tag the audio-cluster capture saw.) Mixer-style, likely safe. +- **Gapless** = `0647` - `0647000C0001` on / `0000` off. (matches COMMAND_MAP 0647=set gapless) +- **Artist list grouping** = `0648` - `0648000C0000` Artist / `0001` Album-Artist. (ARTIST_CLASS_TYPE) +- **Memory playback (resume)** = `0684` - `0684000C00<0|1|2>` = Off / Position / Song. (0684 was "media getter" in COMMAND_MAP - now confirmed the MEMORY_PLAY setter) +- **Max volume** = `0711` - `0711000C00` where NN(hex)=cap, 0..0x78(120). +NOTE: only SPDIF (0666 route) wedges on a raw send; these are config/mixer commands, expected safe - but per the wedge lesson, apply on live user change only, don't blind-send at boot. + +## 5e. Sibilance EQ presets wired (2026-07-01, code-only, staged not-yet-deployed) +Stock has 11 EQ presets (equalizer.json 0-10); diskOS had 9. Added preset 9="Sibilance 1", +10="Sibilance 2" to OPT_EQ (settings.c) + T_EQ (npmenus.c), nopts 9->11, clamps q>8->q>10. +Uses the existing 0689 path (ui_apply_eq, clamp already 0..20) so 0689000C0009/000A select them. diff --git a/flash/assemble_stage1.py b/flash/assemble_stage1.py new file mode 100644 index 0000000..74fcc95 --- /dev/null +++ b/flash/assemble_stage1.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""Assemble the USB stage-1 from the GPL-built SPL + the W63AH6NKB DDR parameter block. + + stage1 = BDIF record + DDR record (35 W63AH6NKB reg words) + zero-pad to 0x800 + u-boot-spl.bin + +The mask ROM loads this at 0xb2401000 and executes the SPL at 0xb2401800; the SPL reads the +parameter records at 0xb2401000. Reproducible: same inputs -> identical output. + +Usage: assemble_stage1.py +""" +import struct, sys, hashlib + +def main(spl_path, ginfo_path, out_path): + g = open(ginfo_path, 'rb').read() + # BDIF (board-info) record verbatim: [magic][size=184][184B payload] = 0xc0 bytes + bdif = g[0x000:0x0c0] + if struct.unpack_from(' 0x7c0: + raise SystemExit("param block collides with the 0x7c0 diagnostic scratch") + spl = open(spl_path, 'rb').read() + buf = bytearray(ginfo) + b'\x00' * (0x800 - len(ginfo)) + spl + if len(buf) > 0x3000: + raise SystemExit("stage-1 exceeds the ~0x3000 TCSM window: %d bytes" % len(buf)) + open(out_path, 'wb').write(buf) + print("wrote %s: %d bytes (0x%X), TCSM headroom %d, sha256=%s" + % (out_path, len(buf), len(buf), 0x3000 - len(buf), hashlib.sha256(buf).hexdigest())) + +if __name__ == '__main__': + if len(sys.argv) != 4: + raise SystemExit(__doc__) + main(*sys.argv[1:]) diff --git a/flash/disc_spl_lpddr3.bin b/flash/disc_spl_lpddr3.bin new file mode 100644 index 0000000..f60c38e Binary files /dev/null and b/flash/disc_spl_lpddr3.bin differ diff --git a/flash/ginfo_w63ah6nkb.bin b/flash/ginfo_w63ah6nkb.bin new file mode 100644 index 0000000..c5dd15d Binary files /dev/null and b/flash/ginfo_w63ah6nkb.bin differ diff --git a/flash/my_write5.c b/flash/my_write5.c new file mode 100644 index 0000000..9fae0bc --- /dev/null +++ b/flash/my_write5.c @@ -0,0 +1,353 @@ +/* my_write.c - SFC SPI-NAND WRITE primitive self-test for GD5F2GM7 (X2000 Disc). + * Built on the PROVEN my_read.c framework (same SFC 0x7c cmd-index + CDT + clock/pins). + * v1: SINGLE-BLOCK round-trip self-test on the INACTIVE ro2 slot - STRICTLY contained: + * erase 1 block -> read back (expect 0xFF) -> program 4 pages w/ pattern -> read back -> compare. + * Reports everything in the dbg block @0xa0a00000. No host data needed (pattern generated on-device). + * Active stock slot (ro4) is never touched; this proves the primitive before any real flash. */ +typedef unsigned int u32; typedef unsigned char u8; +#define SFC_BASE 0xb3440000u +#define CPM_BASE 0xb0000000u +#define CPM_CLKGR 0x20u +#define CPM_SSICDR 0x74u +#define CPM_CPMPCR 0x14u +#define SFC_GLB 0x0000 +#define SFC_DEV_CONF 0x0004 +#define SFC_DEV_STA_EXP 0x0008 +#define SFC_DEV_STA_MSK 0x0010 +#define SFC_TRAN_CONF0 0x0014 +#define SFC_TRAN_LEN 0x002c +#define SFC_DEV_ADDR0 0x0030 +#define SFC_DEV_ADDR_PLUS0 0x0048 +#define SFC_MEM_ADDR 0x0060 +#define SFC_TRIG 0x0064 +#define SFC_SR 0x0068 +#define SFC_SCR 0x006c +#define SFC_INTC 0x0070 +#define SFC_CGE 0x0078 +#define SFC_CMD_IDX 0x007c +#define SFC_ARG0 0x0080 +#define SFC_ARG1 0x0084 +#define SFC_ARG2 0x0088 +#define SFC_ARG3 0x008c +#define SFC_UNK0 0x009c +#define SFC_RM_DR 0x1000 +#define GLB_TRAN_DIR_OFFSET 13 +#define GLB_THRESHOLD_OFFSET 7 +#define GLB_THRESHOLD_MSK (0x3f<<7) +#define GLB_PHASE_NUM_OFFSET 3 +#define GLB_PHASE_NUM_MSK (0x7<<3) +#define THRESHOLD 32 +#define END (1<<4) +#define TRAN_REQ (1<<3) +#define RECE_REQ (1<<2) +#define CLR_END (1<<4) +#define CLR_TREQ (1<<3) +#define CLR_RREQ (1<<2) +#define TRIG_START (1<<0) +#define TRIG_STOP (1<<1) +#define TRIG_FLUSH (1<<2) +#define CMD_RDID 0x9f +#define CMD_PARD 0x13 +#define CMD_FRCH 0x0b +#define CMD_GET_FEATURE 0x0f +#define CMD_SET_FEATURE 0x1f +#define CMD_RESET 0xff +#define CMD_WREN 0x06 +#define CMD_PLOAD 0x02 +#define CMD_PEXEC 0x10 +#define CMD_BERASE 0xd8 +#define ADDR_STATUS 0xc0 +#define ADDR_PROTECT 0xa0 +#define ADDR_FEATURE 0xb0 +#define ADDRLEN 2u +#define GUARD 800000u +#define ST_OIP 0x01 +#define ST_EFAIL 0x04 +#define ST_PFAIL 0x08 +static inline void w32(u32 a,u32 v){*(volatile u32*)a=v;} +static inline u32 r32(u32 a){return *(volatile u32*)a;} +#define sfc_writel(v,o) w32(SFC_BASE+(o),(u32)(v)) +#define sfc_readl(o) r32(SFC_BASE+(o)) +#define GPIO_PORTE 0xb0010400u +#define SFC_PINS 0x003f0000u +static void mux_sfc_pins(void){ + w32(GPIO_PORTE+0x18,SFC_PINS); w32(GPIO_PORTE+0x28,SFC_PINS); + w32(GPIO_PORTE+0x38,SFC_PINS); w32(GPIO_PORTE+0x48,SFC_PINS); +} +static u32 g_id,g_len,g_arg0,g_arg1,g_arg2,g_arg3; +static void send_raw(int dir){ + u32 v; + sfc_writel(TRIG_STOP,SFC_TRIG); + v=sfc_readl(SFC_CMD_IDX); v=(v&~0x3fu)|(g_id&0x3fu); sfc_writel(v,SFC_CMD_IDX); + sfc_writel(g_arg0,SFC_ARG0); sfc_writel(g_arg1,SFC_ARG1); + sfc_writel(g_arg2,SFC_ARG2); sfc_writel(g_arg3,SFC_ARG3); + v=sfc_readl(SFC_CMD_IDX)&0x7fffffffu; if(g_len)v|=0x80000000u; sfc_writel(v,SFC_CMD_IDX); + if(g_len){ v=sfc_readl(SFC_CMD_IDX)&~0x40000000u; v|=((u32)dir&1u)<<30; sfc_writel(v,SFC_CMD_IDX); } + {u32 g=sfc_readl(SFC_GLB); g&=~((1u< unlock never landed */ + else if(c==0x13){ g_id=5; g_arg1=addr; g_arg2=ADDR_STATUS; } + else if(c==0x0b){ g_id=7; g_arg0=addr; } + else if(c==0x06){ g_id=8; } /* WRITE_ENABLE: cmd-only */ + else if(c==0x02){ g_id=9; g_arg0=addr; } /* PROGRAM_LOAD: COL addr, data-out */ + else if(c==0x10){ g_id=10; g_arg1=addr; } /* PROGRAM_EXECUTE: ROW addr (same path as PAGE_READ row) */ + else if(c==0xd8){ g_id=11; g_arg1=addr; } /* BLOCK_ERASE: ROW addr */ + else g_id=0x3f; + g_len=len; send_raw(dir); +} +static int clr_end(void){u32 g=0; while(!(sfc_readl(SFC_SR)&END)){if(++g>GUARD)return -1;} sfc_writel(CLR_END,SFC_SCR); return 0;} +static int rd(u32*d,u32 length){ + u32 done=0,fn,sr,len=(length+3)/4,g=0; int i; + while(doneGUARD)return -1; sr=sfc_readl(SFC_SR); + if(sr&RECE_REQ){ sfc_writel(CLR_RREQ,SFC_SCR); fn=((len-done)>THRESHOLD)?THRESHOLD:(len-done); + for(i=0;i<(int)fn;i++){*d++=sfc_readl(SFC_RM_DR);done++;} g=0;} } + return clr_end(); +} +static int wr(u32*d){u32 g=0; while(!(sfc_readl(SFC_SR)&TRAN_REQ)){if(++g>GUARD)return -1;} sfc_writel(CLR_TREQ,SFC_SCR); sfc_writel(*d,SFC_RM_DR); return clr_end();} +static int wr_bulk(u32*s,u32 length){ /* transmit `length` bytes (PROGRAM_LOAD data phase) */ + u32 done=0,fn,sr,len=(length+3)/4,g=0; int i; + while(doneGUARD)return -1; sr=sfc_readl(SFC_SR); + if(sr&TRAN_REQ){ sfc_writel(CLR_TREQ,SFC_SCR); fn=((len-done)>THRESHOLD)?THRESHOLD:(len-done); + for(i=0;i<(int)fn;i++){sfc_writel(*s++,SFC_RM_DR);done++;} g=0;} } + return clr_end(); +} +static u32 g_mpll,g_cdr,g_src; +static void set_clock(void){ + u32 reg=CPM_BASE+CPM_SSICDR, v=r32(reg); + u32 src=(v>>30)&3; + u32 pcr=r32(CPM_BASE+(src?CPM_CPMPCR:0x10)); + u32 m=((pcr>>20)&0xfff)+1, n=((pcr>>14)&0x3f)+1, od=1u<<((pcr>>11)&7u); + u32 pll=(24u*m*2u)/n/od, cdr=((pll+49u)/50u-1u)&0xff; + g_mpll=pll; g_cdr=cdr; g_src=src; + w32(CPM_BASE+CPM_CLKGR, r32(CPM_BASE+CPM_CLKGR)&~(1u<<2)); + v&=~(0xffu|(3u<<27)); v|=(1u<<29)|cdr; + w32(reg,v); + {u32 g=0; while((r32(reg)&(1u<<28))){if(++g>GUARD)break;}} +} +static void sfc_reset_regs(void){ + int n; u32 v; + for(n=0;n<6;n++){sfc_writel(0,SFC_TRAN_CONF0+n*4);sfc_writel(0,SFC_UNK0+n*4);sfc_writel(0,SFC_DEV_ADDR0+n*4);sfc_writel(0,SFC_DEV_ADDR_PLUS0+n*4);} + sfc_writel(0,SFC_DEV_CONF);sfc_writel(0,SFC_DEV_STA_EXP);sfc_writel(0,SFC_DEV_STA_MSK); + sfc_writel(0,SFC_TRAN_LEN);sfc_writel(0,SFC_MEM_ADDR);sfc_writel(0,SFC_TRIG); + sfc_writel(0,SFC_SCR);sfc_writel(0,SFC_INTC);sfc_writel(0,SFC_CGE);sfc_writel(0,SFC_RM_DR); + v=sfc_readl(SFC_GLB); v=(v&~3u)|2u; sfc_writel(v,SFC_GLB); + sfc_writel(TRIG_STOP,SFC_TRIG); + sfc_writel(0x1f,SFC_SCR); sfc_writel(0x1f,SFC_INTC); + sfc_writel((1<<1)|(1<<0)|(1<<2)|(2<<5),SFC_DEV_CONF); + v=sfc_readl(SFC_GLB); v&=~(GLB_THRESHOLD_MSK|(1u<<6)|3u); + v|=(THRESHOLD<6 */ + cdt_write(6, CDT_LINK(0,2,0), (1u<<25)|CDT_XFER(1,0,1,0x0f), 0,1); /* hw OIP poll */ + cdt_write(7, CDT_LINK(0,0,0), CDT_XFER(2,8,1,0x0b), 0,0); /* READ_CACHE */ + cdt_write(8, CDT_LINK(0,0,0), CDT_XFER(0,0,0,0x06), 0,0); /* WRITE_ENABLE: cmd-only */ + cdt_write(9, CDT_LINK(0,2,0), CDT_XFER(2,0,1,0x02), 0,0); /* PROGRAM_LOAD: COL(2B), data-out, dir set at run */ + cdt_write(10,CDT_LINK(0,1,0), CDT_XFER(3,0,0,0x10), 0,0); /* PROGRAM_EXECUTE: ROW(3B) */ + cdt_write(11,CDT_LINK(0,1,0), CDT_XFER(3,0,0,0xd8), 0,0); /* BLOCK_ERASE: ROW(3B) */ +} +static int nand_reset(void){u32 st,g=0; + cmd(CMD_RESET,0,0,0,0,0,0); if(clr_end())return -1; + do{if(++g>GUARD)return -2; cmd(CMD_GET_FEATURE,1,ADDR_STATUS,1,0,1,0); if(rd(&st,1))return -3;}while(st&ST_OIP); + return 0;} +static int set_features(void){u32 x; /* unlock block-protect + enable on-die ECC */ + cmd(CMD_SET_FEATURE,1,ADDR_PROTECT,1,0,1,1); x=0; if(wr(&x))return -1; /* PROTECT(0xA0)=0 */ + cmd(CMD_SET_FEATURE,1,ADDR_FEATURE,1,0,1,1); x=(1<<4); return wr(&x);} /* FEATURE(0xB0): ECC_EN */ +static int wait_ready(u32*stout){u32 st,g=0; + do{if(++g>GUARD)return -1; cmd(CMD_GET_FEATURE,1,ADDR_STATUS,1,0,1,0); if(rd(&st,1))return -2;}while(st&ST_OIP); + if(stout)*stout=st; return 0;} +static int read_page(u32 page,u32 col,u8*dst,u32 len){ + cmd(CMD_PARD,0,page,3,0,0,0); if(clr_end())return -2; + cmd(CMD_FRCH,len,col,ADDRLEN,8,1,0); return rd((u32*)dst,len);} +static int block_erase(u32 page){u32 st; int r; + cmd(CMD_WREN,0,0,0,0,0,0); if(clr_end())return -10; + cmd(CMD_BERASE,0,page,3,0,0,0); if(clr_end())return -11; + r=wait_ready(&st); if(r)return -12; + if(st&ST_EFAIL)return -13; + return 0;} +static int program_page(u32 page,u8*src){u32 st; int r; + cmd(CMD_WREN,0,0,0,0,0,0); if(clr_end())return -20; + cmd(CMD_PLOAD,2048,0,2,0,1,1); if(wr_bulk((u32*)src,2048))return -21; /* load cache @col0 */ + cmd(CMD_PEXEC,0,page,3,0,0,0); if(clr_end())return -22; /* commit row */ + r=wait_ready(&st); if(r)return -23; + if(st&ST_PFAIL)return -24; + return 0;} +/* LEAN writer (my_write2): programs NPAGES from host-loaded DRAM @SRC into NAND + * starting at START_PAGE, erasing the covered blocks first. Reads back page 0 and + * compares to SRC for one-run feedback. This is the REAL write path (data from DRAM), + * not a self-generated pattern - host loads SRC, runs this, verifies via dbg + reader. */ +/* my_write3 - MULTI-BLOCK writer for the real rootfs flash. + * Programs NLOGBLOCKS logical 128KB blocks from host-loaded DRAM @SRC into physical + * blocks starting at START_BLOCK, SKIPPING the factory bad block SKIP_BLOCK (left untouched + * so its OOB bad-marker survives). This reconstructs the mtdblock_bbt logical->physical mapping. */ +#ifndef START_BLOCK +#define START_BLOCK 80u /* rootfs @0xA00000 / 0x20000 */ +#endif +#ifndef NLOGBLOCKS +#define NLOGBLOCKS 580u /* diskos_*_v228.bin = 76021760 B / 128KB = 580 blocks (override via -D) */ +#endif +#ifndef SKIP_BLOCK +#define SKIP_BLOCK 383u /* verified factory bad block in the rootfs region (dump ground truth) */ +#endif +#define PAGE_SZ 2048u +#define SRC 0xa1000000u /* host-loaded squashfs (padded to NLOGBLOCKS*128KB), via usbboot --download */ +/* my_write5 - PORTABLE robust bad-block-aware writer (my_write4 + an in-writer OOB scan). + * Unlike my_write4 (which hardcoded SKIP_BLOCK=383, this unit's bad block), my_write5 first + * SCANS each physical block's OOB bad-marker to learn THIS unit's factory-bad blocks, then + * skips exactly those - so it maps logical->physical correctly on any device. For every good + * physical block it does + * erase -> program 64 pages -> READBACK-VERIFY all 64 pages, retrying the WHOLE block (with a + * NAND reset + re-unlock between attempts) up to MAXTRIES. This recovers transient SFC/timeout + * failures like the ones that killed the previous flash on blocks 80/81 (which are NOT OOB + * bad and were written fine by the V2.09 flash). FAILS CLOSED: if a block cannot be written+ + * verified after MAXTRIES, it stops immediately and reports - it never silently mis-maps. */ +#define MAXTRIES 6u +#define PART_END (START_BLOCK + NLOGBLOCKS + 64u) /* generous physical ceiling (guards runaway) */ +#define MAXBAD 64u /* = PART_END-START_BLOCK-NLOGBLOCKS: max bad blocks the image can absorb */ +/* linear membership test over the scanned bad-block list (nbad is tiny in practice) */ +static int is_bad(u32 pb, const u32 *bl, u32 n){ u32 i; for(i=0;i0 && nand_reset()!=0) continue; + m=0xFFFFFFFFu; + if(read_page(pb*64u, PAGE_SZ, (u8*)&m, 4u)==0){ *mk=(u8)(m & 0xFFu); return 0; } + } + return -1; +} +/* Bad-block verdict from the OOB marker, FAIL-CLOSED: 0=good, 1=bad, -1=undeterminable. + * EVERY verdict - good AND bad - is confirmed by two independent reads that must agree, so a + * single successful-but-corrupted read can't produce a false-good (missed bad block) OR a + * false-bad (skipped good block); either would mis-map against the kernel's BBT. An unreadable + * or self-inconsistent marker returns -1 and the caller aborts. */ +static int oob_verdict(u32 pb){ + u8 a, b; + if(oob_read1(pb, &a)!=0) return -1; + if(oob_read1(pb, &b)!=0) return -1; + if(a!=b) return -1; + return (a==0xFFu) ? 0 : 1; +} +/* Erase + program 64 pages + verify-readback a single physical block from src. 0=ok, else rc<0. */ +static int write_block_verify(u32 pb, const u8 *src, u32 *tries_out){ + u32 t, k, i; + int er, pr; + u8 rb[2048] __attribute__((aligned(4))); + u32 page = pb*64u; + for(t=0;t0){ + if(nand_reset()!=0) continue; + if(set_features()!=0) continue; + } + er = block_erase(page); + if(er) continue; /* erase fail -> retry (re-inits) */ + pr = 0; + for(k=0;k<64u;k++){ pr = program_page(page+k, (u8*)(src + k*PAGE_SZ)); if(pr) break; } + if(pr) continue; /* program fail -> retry */ + /* verify every page against the source; a READ failure fails the attempt (stale rb + * must never be allowed to compare-equal and pass). */ + pr = 0; + for(k=0;k<64u && !pr;k++){ + if(read_page(page+k, 0, rb, PAGE_SZ)!=0){ pr = 1; break; } /* read error -> retry */ + for(i=0;i fail closed */ +} +__attribute__((section(".text.entry"))) void my_write(void){ + volatile u32 *dbg=(volatile u32*)0xa0a00000u; + u32 lb=0, pb=START_BLOCK, skipped=0, retried=0, maxtries=0, feat=0, tries; + u32 nbad=0, badlist[MAXBAD], b; + int rr; + for(lb=0; lb<256u; lb++) dbg[lb]=0; /* clear the whole 1KB dbg region */ + lb=0; + dbg[0]=0x4004E005u; /* magic: my_write5 (portable) ran */ + mux_sfc_pins(); set_clock(); sfc_reset_regs(); cdt_init(); + dbg[3]=(u32)nand_reset(); + dbg[4]=(u32)set_features(); + cmd(CMD_GET_FEATURE,1,ADDR_FEATURE,1,0,1,0); rd(&feat,1); dbg[15]=feat; + dbg[5]=START_BLOCK; dbg[6]=NLOGBLOCKS; + /* ABORT if the NAND/ECC state didn't come up - never write blind. */ + if(dbg[3]!=0 || dbg[4]!=0 || (feat & 0x10u)==0){ + dbg[16]=0xDEAD0001u; /* init-fail abort code */ + dbg[9]=0x55555555u; return; + } + /* ---- PORTABLE SCAN PASS ------------------------------------------------------------ + * Learn THIS unit's factory-bad blocks from their OOB bad-marker (byte0 of page0 spare + * != 0xFF, read with on-die ECC disabled) - the SAME criterion the kernel's mtdblock_bbt + * uses to build the logical->physical map. Replaces the hardcoded SKIP_BLOCK so the mapping + * is correct on ANY device. The identical read is proven by bbscan (it found block 383). */ + /* Try to disable on-die ECC so the spare returns raw; record the readback (informational). + * On this NAND ECC may stay enabled (readback still 0x10) yet the marker byte still reads + * RAW - so ECC state is NOT a correctness gate; each marker read is validated per-block. */ + cmd(CMD_SET_FEATURE,1,ADDR_FEATURE,1,0,1,1); { u32 z=0; wr(&z); } + cmd(CMD_GET_FEATURE,1,ADDR_FEATURE,1,0,1,0); rd(&feat,1); dbg[22]=feat; + for(b=START_BLOCK; b fail closed */ + dbg[9]=0x55555555u; return; } + if(v == 1){ if(nbad < MAXBAD){ badlist[nbad]=b; dbg[40u+nbad]=b; } /* list -> dbg[40..40+MAXBAD) */ + nbad++; } + } + dbg[20]=nbad; + /* More bad blocks than the image can absorb -> dead NAND or a mis-scan. Fail closed. */ + if(nbad > MAXBAD){ dbg[16]=0xDEAD0004u; dbg[9]=0x55555555u; return; } + /* Re-enable on-die ECC + re-assert block-unlock for the program/verify pass; REQUIRE it. */ + rr = set_features(); + cmd(CMD_GET_FEATURE,1,ADDR_FEATURE,1,0,1,0); rd(&feat,1); dbg[21]=feat; + if(rr!=0 || (feat & 0x10u)==0){ dbg[16]=0xDEAD0005u; dbg[9]=0x55555555u; return; } + /* ------------------------------------------------------------------------------------ */ + while(lb=PART_END){ dbg[16]=0xDEAD0002u; dbg[17]=pb; dbg[18]=lb; + dbg[9]=0x55555555u; return; } /* out of physical space -> abort */ + tries=0; + rr = write_block_verify(pb, (const u8*)(SRC + (u32)lb*64u*PAGE_SZ), &tries); + if(rr){ /* FAIL CLOSED */ + dbg[16]=0xDEAD0003u; /* persistent block-write failure */ + dbg[17]=pb; dbg[18]=lb; dbg[19]=tries; + dbg[9]=0x55555555u; return; /* stop - do NOT continue mis-mapped */ + } + if(tries>1u){ retried++; if(tries>maxtries) maxtries=tries; } + lb++; pb++; + dbg[1]=lb; /* progress */ + dbg[11]=pb; + } + dbg[7]=0; /* 0 = all blocks written+verified */ + dbg[8]=0; + dbg[10]=skipped; /* bad blocks skipped within the written span (<= dbg[20]=nbad) */ + dbg[11]=pb; /* last physical block +1 */ + dbg[12]=*(volatile u32*)SRC; /* first source word */ + dbg[13]=r32(CPM_BASE+CPM_SSICDR); + dbg[16]=0x600DF10Cu; /* SUCCESS sentinel */ + dbg[17]=retried; /* # blocks that needed a retry */ + dbg[18]=maxtries; /* worst-case retry count */ + dbg[9]=0x55555555u; /* DONE - written LAST */ +} diff --git a/flash/my_write5_dram.bin b/flash/my_write5_dram.bin new file mode 100755 index 0000000..c7599f0 Binary files /dev/null and b/flash/my_write5_dram.bin differ diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..d251443 --- /dev/null +++ b/install.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# diskOS installer - one-time setup. +# +# Creates a local Python virtual environment (.venv) next to this script and installs the two +# pip dependencies (pyusb, pycryptodome) into it. After this you run the tool with ./diskos-installer. +# +# What this does NOT install (they come from your system, not from us): +# - Python 3 itself (install via your OS package manager if missing) +# - Tk/tkinter (only needed for the GRAPHICAL installer; CLI works without it) +# - libusb-1.0 (needed to talk to the device in mask-ROM mode) +# The script checks for these and tells you exactly what to install if any are missing. +set -eu + +HERE=$(CDPATH= cd "$(dirname "$0")" && pwd) +cd "$HERE" + +# Do NOT run this as root. The installer keeps per-user state and a saved recovery image under your +# home directory; running setup as root would create them root-owned and in the wrong place. +if [ "$(id -u)" = "0" ]; then + echo "error: don't run install.sh as root (no sudo). Run it as your normal user;" >&2 + echo " only the one-time udev rule needs root (see README, 'USB permissions')." >&2 + exit 1 +fi + +# Scratch file for capturing venv-creation errors: created private + removed on exit, never a +# fixed /tmp path (a predictable name in a world-writable dir invites a symlink attack). +ERRTMP=$(mktemp "${TMPDIR:-/tmp}/diskos_venv.XXXXXX") || { echo "error: mktemp failed" >&2; exit 1; } +trap 'rm -f "$ERRTMP"' EXIT INT TERM + +# ---- locate a usable Python 3 ------------------------------------------------- +PY="" +for c in python3 python; do + if command -v "$c" >/dev/null 2>&1; then + if "$c" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, 8) else 1)' 2>/dev/null; then + PY=$(command -v "$c"); break + fi + fi +done +if [ -z "$PY" ]; then + echo "error: Python 3.8+ not found." >&2 + echo " Debian/Ubuntu: sudo apt install python3 python3-venv" >&2 + echo " Fedora: sudo dnf install python3" >&2 + echo " macOS: brew install python3 (or install from python.org)" >&2 + exit 1 +fi +echo "==> using $("$PY" --version 2>&1) at $PY" + +# ---- create the venv ---------------------------------------------------------- +if [ ! -x "$HERE/.venv/bin/python" ] && [ ! -x "$HERE/.venv/Scripts/python.exe" ]; then + echo "==> creating virtual environment in .venv/" + if ! "$PY" -m venv "$HERE/.venv" 2>"$ERRTMP"; then + echo "error: could not create the virtual environment:" >&2 + sed 's/^/ /' "$ERRTMP" >&2 2>/dev/null || true + echo " Debian/Ubuntu often needs: sudo apt install python3-venv" >&2 + exit 1 + fi +else + echo "==> reusing existing .venv/" +fi +VENV_PY="$HERE/.venv/bin/python" +[ -x "$VENV_PY" ] || VENV_PY="$HERE/.venv/Scripts/python.exe" # windows layout + +# ---- install pinned dependencies --------------------------------------------- +echo "==> installing dependencies (pyusb, pycryptodome)" +"$VENV_PY" -m pip install --upgrade pip >/dev/null 2>&1 || true +"$VENV_PY" -m pip install -r "$HERE/requirements.txt" + +# ---- capability checks (warn, do not fail) ----------------------------------- +echo "==> checking optional system components" + +# Tk (GUI only). A venv inherits the base interpreter's tkinter, so this reflects the system Python. +if "$VENV_PY" -c 'import tkinter' 2>/dev/null; then + echo " ok: tkinter present - the graphical installer will work" +else + echo " note: tkinter NOT found - the GRAPHICAL installer will not run (the CLI still works)." + echo " To enable the GUI, install your system's Tk package, then re-run this script:" + echo " Debian/Ubuntu: sudo apt install python3-tk" + echo " Fedora: sudo dnf install python3-tkinter" + echo " macOS: brew install python-tk (or use python.org's Python, which bundles Tk)" +fi + +# libusb-1.0 backend for pyusb (needed to see the device in mask-ROM mode). +if "$VENV_PY" -c 'import ctypes.util,sys; sys.exit(0 if ctypes.util.find_library("usb-1.0") else 1)' 2>/dev/null; then + echo " ok: libusb-1.0 present - device detection will work" +else + echo " note: libusb-1.0 NOT found - the tool cannot detect the device until you install it:" + echo " Debian/Ubuntu: sudo apt install libusb-1.0-0" + echo " Fedora: sudo dnf install libusbx" + echo " macOS: brew install libusb" +fi + +echo +echo "Setup complete. Run the installer with:" +echo " ./diskos-installer doctor # check host + tools + device" +echo " ./diskos-installer gui # graphical installer (needs tkinter)" +echo " ./diskos-installer --help # all commands" diff --git a/licenses/BSD-3-Clause-pyusb.txt b/licenses/BSD-3-Clause-pyusb.txt new file mode 100644 index 0000000..461a796 --- /dev/null +++ b/licenses/BSD-3-Clause-pyusb.txt @@ -0,0 +1,27 @@ +Copyright 2009–2017 Wander Lairson Costa +Copyright 2009–2021 PyUSB contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/CC-BY-4.0.txt b/licenses/CC-BY-4.0.txt new file mode 100644 index 0000000..da6ab6c --- /dev/null +++ b/licenses/CC-BY-4.0.txt @@ -0,0 +1,396 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/licenses/GPL-2.0.txt b/licenses/GPL-2.0.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/licenses/GPL-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/licenses/LGPL-2.1.txt b/licenses/LGPL-2.1.txt new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/licenses/LGPL-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/licenses/LICENSE-TJpgDec.txt b/licenses/LICENSE-TJpgDec.txt new file mode 100644 index 0000000..93a7758 --- /dev/null +++ b/licenses/LICENSE-TJpgDec.txt @@ -0,0 +1,17 @@ +TJpgDec - Tiny JPEG Decompressor - license (verbatim from the source header shipped in LVGL) + +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03 (C)ChaN, 2021 +/-----------------------------------------------------------------------------/ +/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2021, ChaN, all right reserved. +/ +/ * The TJpgDec module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------*/ diff --git a/licenses/LICENSE-dropbear.txt b/licenses/LICENSE-dropbear.txt new file mode 100644 index 0000000..a3edf7d --- /dev/null +++ b/licenses/LICENSE-dropbear.txt @@ -0,0 +1,114 @@ +Dropbear contains a number of components from different sources, hence there +are a few licenses and authors involved. All licenses are fairly +non-restrictive. + + +The majority of code is written by Matt Johnston, under the license below. + +Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the +same license: + +Copyright (c) 2002-2020 Matt Johnston +Portions copyright (c) 2004 Mihnea Stoenescu +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +===== + +LibTomCrypt and LibTomMath are written by Tom St Denis and others, see +libtomcrypt/LICENSE and libtommath/LICENSE. + +===== + +sshpty.c is taken from OpenSSH 3.5p1, + Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + All rights reserved + "As far as I am concerned, the code I have written for this software + can be used freely for any purpose. Any derived versions of this + software must be clearly marked as such, and if the derived work is + incompatible with the protocol description in the RFC file, it must be + called by a name other than "ssh" or "Secure Shell". " + +===== + +loginrec.c +loginrec.h +atomicio.h +atomicio.c +and strlcat() (included in util.c) are from OpenSSH 3.6.1p2, and are licensed +under the 2 point BSD license. + +loginrec is written primarily by Andre Lucas, atomicio.c by Theo de Raadt. + +strlcat() is (c) Todd C. Miller + +===== + +Import code in keyimport.c is modified from PuTTY's import.c, licensed as +follows: + +PuTTY is copyright 1997-2003 Simon Tatham. + +Portions copyright Robert de Bath, Joris van Rantwijk, Delian +Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, +Justin Bradford, and CORE SDI S.A. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +===== + +curve25519.c: + +Modified TweetNaCl version 20140427, a self-contained public-domain C library. +https://tweetnacl.cr.yp.to/ + +Contributors (alphabetical order) +Daniel J. Bernstein, University of Illinois at Chicago and Technische +Universiteit Eindhoven +Bernard van Gastel, Radboud Universiteit Nijmegen +Wesley Janssen, Radboud Universiteit Nijmegen +Tanja Lange, Technische Universiteit Eindhoven +Peter Schwabe, Radboud Universiteit Nijmegen +Sjaak Smetsers, Radboud Universiteit Nijmegen + +Acknowledgments +This work was supported by the U.S. National Science Foundation under grant +1018836. "Any opinions, findings, and conclusions or recommendations expressed +in this material are those of the author(s) and do not necessarily reflect the +views of the National Science Foundation." +This work was supported by the Netherlands Organisation for Scientific +Research (NWO) under grant 639.073.005 and Veni 2013 project 13114. diff --git a/licenses/MIT-JSMN.txt b/licenses/MIT-JSMN.txt new file mode 100644 index 0000000..769894d --- /dev/null +++ b/licenses/MIT-JSMN.txt @@ -0,0 +1,17 @@ +MIT License +Copyright (c) 2010 Serge Zaitsev +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/MIT-LVGL.txt b/licenses/MIT-LVGL.txt new file mode 100644 index 0000000..f877abb --- /dev/null +++ b/licenses/MIT-LVGL.txt @@ -0,0 +1,8 @@ +MIT licence +Copyright (c) 2021 LVGL Kft + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/MIT-musl.txt b/licenses/MIT-musl.txt new file mode 100644 index 0000000..a0193c4 --- /dev/null +++ b/licenses/MIT-musl.txt @@ -0,0 +1,34 @@ +musl libc - license +==================== + +The diskOS UI binary (`payload/mq_ui`) is statically linked against musl libc. +musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- + +The musl COPYRIGHT file additionally credits ~80 individual authors and notes +that certain files are, or were, licensed under compatible permissive terms +(BSD, ISC, public domain). The complete, authoritative COPYRIGHT file (including +the full author list and per-file notes) is distributed with musl and available +at: https://git.musl-libc.org/cgit/musl/plain/COPYRIGHT diff --git a/licenses/MIT-qrcodegen.txt b/licenses/MIT-qrcodegen.txt new file mode 100644 index 0000000..8136dce --- /dev/null +++ b/licenses/MIT-qrcodegen.txt @@ -0,0 +1,22 @@ +QR Code generator library - license (verbatim from the source header shipped in LVGL) + +QR Code generator library (C) + +Copyright (c) Project Nayuki. (MIT License) +https://www.nayuki.io/page/qr-code-generator-library + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +- The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. +- The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall the + authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising from, + out of or in connection with the Software or the use or other dealings in the + Software. diff --git a/licenses/OFL-1.1-FontAwesome.README.txt b/licenses/OFL-1.1-FontAwesome.README.txt new file mode 100644 index 0000000..1a27480 --- /dev/null +++ b/licenses/OFL-1.1-FontAwesome.README.txt @@ -0,0 +1,15 @@ +Font Awesome Free glyphs are embedded in the diskOS UI binary (payload/mq_ui) via LVGL's built-in +symbol font (LV_SYMBOL_*). Font Awesome Free licensing: + - Fonts: SIL OFL 1.1 + - Icons: CC BY 4.0 + - Code: MIT + +The verbatim texts are shipped in this directory: + - OFL-1.1-FontAwesome.txt = Font Awesome's own LICENSE.txt (Copyright (c) Fonticons, Inc.), + which contains the full SIL OFL 1.1 text (with the "Font Awesome" + reserved font name) plus the MIT code license and the CC BY reference. + - CC-BY-4.0.txt = the full Creative Commons Attribution 4.0 International legal code. + +Canonical sources: + - https://github.com/FortAwesome/Font-Awesome (LICENSE.txt) + - https://creativecommons.org/licenses/by/4.0/legalcode.txt diff --git a/licenses/OFL-1.1-FontAwesome.txt b/licenses/OFL-1.1-FontAwesome.txt new file mode 100644 index 0000000..e69c5e3 --- /dev/null +++ b/licenses/OFL-1.1-FontAwesome.txt @@ -0,0 +1,165 @@ +Fonticons, Inc. (https://fontawesome.com) + +-------------------------------------------------------------------------------- + +Font Awesome Free License + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +-------------------------------------------------------------------------------- + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) + +The Font Awesome Free download is licensed under a Creative Commons +Attribution 4.0 International License and applies to all icons packaged +as SVG and JS file types. + +-------------------------------------------------------------------------------- + +# Fonts: SIL OFL 1.1 License + +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +Copyright (c) 2024 Fonticons, Inc. (https://fontawesome.com) +with Reserved Font Name: "Font Awesome". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE +Version 1.1 - 26 February 2007 + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting — in part or in whole — any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +-------------------------------------------------------------------------------- + +# Code: MIT License (https://opensource.org/licenses/MIT) + +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +Copyright 2024 Fonticons, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +# Attribution + +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +-------------------------------------------------------------------------------- + +# Brand Icons + +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/licenses/OFL-1.1-Montserrat.txt b/licenses/OFL-1.1-Montserrat.txt new file mode 100644 index 0000000..79979ed --- /dev/null +++ b/licenses/OFL-1.1-Montserrat.txt @@ -0,0 +1,92 @@ +Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat), with Reserved Font Name 'Montserrat'. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/licenses/OFL-1.1-SourceHanSans.txt b/licenses/OFL-1.1-SourceHanSans.txt new file mode 100644 index 0000000..ec98e48 --- /dev/null +++ b/licenses/OFL-1.1-SourceHanSans.txt @@ -0,0 +1,93 @@ +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. +Source Han Sans is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/licenses/PSF-Python.txt b/licenses/PSF-Python.txt new file mode 100644 index 0000000..f26bcf4 --- /dev/null +++ b/licenses/PSF-Python.txt @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/README.md b/licenses/README.md new file mode 100644 index 0000000..855a429 --- /dev/null +++ b/licenses/README.md @@ -0,0 +1,42 @@ +# Bundled license texts + +This directory carries the full license texts for the third-party components the diskOS installer +bundles. See `../NOTICE.md` for the component→license mapping and the static-linking obligations. + +| Component (bundled) | License | Full text | +|---|---|---| +| usbboot | GPL-2.0-or-later | [`GPL-2.0.txt`](GPL-2.0.txt) | +| squashfs-tools 4.6.1 | GPL-2.0 | [`GPL-2.0.txt`](GPL-2.0.txt) | +| liblzo2 (static in mksquashfs/unsquashfs) | GPL-2.0-or-later | [`GPL-2.0.txt`](GPL-2.0.txt) | +| libusb-1.0.27 (static in usbboot) | LGPL-2.1 | [`LGPL-2.1.txt`](LGPL-2.1.txt) | +| glibc (static in usbboot/mksquashfs/unsquashfs) | LGPL-2.1-or-later | [`LGPL-2.1.txt`](LGPL-2.1.txt); corresponding source in `../corresponding-source/glibc_2.39*` (see `../NOTICE.md` for the relink path) | +| PyInstaller bootloader (only for a self-built onefile) | GPL-2.0 + bootloader exception | [`GPL-2.0.txt`](GPL-2.0.txt) + exception note in `../NOTICE.md` | +| zlib / liblzma (static in squashfs-tools) | zlib / public-domain | permissive notices travel with the squashfs-tools sources the build script fetches | +| pyusb | BSD-3-Clause | [`BSD-3-Clause-pyusb.txt`](BSD-3-Clause-pyusb.txt) (verbatim from pyusb 1.3.1) | +| pycryptodome | BSD-2-Clause + public-domain | [`pycryptodome-LICENSE.txt`](pycryptodome-LICENSE.txt) (verbatim from pycryptodome 3.23.0) | +| CPython | Python Software Foundation License | [`PSF-Python.txt`](PSF-Python.txt) (verbatim from the bundled CPython) | +| LVGL (diskOS UI) | MIT | [`MIT-LVGL.txt`](MIT-LVGL.txt) (verbatim from `native-ui/lvgl/LICENCE.txt`) | +| JSMN (diskOS UI JSON parser) | MIT | [`MIT-JSMN.txt`](MIT-JSMN.txt) (verbatim from `native-ui/ipodos-native/jsmn.h`, © 2010 Serge Zaitsev) | +| Font Awesome Free glyphs (via LVGL `LV_SYMBOL_*`) | OFL-1.1 (fonts) + CC-BY-4.0 (icons) | [`OFL-1.1-FontAwesome.txt`](OFL-1.1-FontAwesome.txt) (Fonticons' own LICENSE.txt: full OFL-1.1 + MIT) + [`CC-BY-4.0.txt`](CC-BY-4.0.txt) | +| musl libc (static in diskOS UI `mq_ui`) | MIT | [`MIT-musl.txt`](MIT-musl.txt) | +| SQLite (amalgamation in `mq_ui`) | Public domain | https://sqlite.org/copyright.html (no license text required) | +| QR Code generator, Nayuki (via LVGL, in `mq_ui`) | MIT | [`MIT-qrcodegen.txt`](MIT-qrcodegen.txt) (verbatim from the LVGL source header) | +| TJpgDec, ChaN (via LVGL, in `mq_ui`) | BSD-style | [`LICENSE-TJpgDec.txt`](LICENSE-TJpgDec.txt) (verbatim from the LVGL source header) | +| Montserrat font (via LVGL built-in fonts, in `mq_ui`) | OFL-1.1 | [`OFL-1.1-Montserrat.txt`](OFL-1.1-Montserrat.txt) | +| Source Han Sans SC (CJK fallback font in `mq_ui`) | OFL-1.1 | [`OFL-1.1-SourceHanSans.txt`](OFL-1.1-SourceHanSans.txt) | +| MD5, Peslyak/"Solar Designer" (in `mq_ui`) | Public domain | in the (unpublished) UI source; no license text required | +| dropbearmulti (Dropbear SSH 2022.83, Debug Mode) | MIT-style | [`LICENSE-dropbear.txt`](LICENSE-dropbear.txt) (verbatim upstream) | +| disc_spl_lpddr3.bin (X2000 stage-1 SPL) | GPL-2.0 | [`GPL-2.0.txt`](GPL-2.0.txt); corresponding source in `../spl-src/` (see `../SPL_SOURCE.md`) | + +All texts above are included **verbatim from the actual upstream sources** (not hand-written), with +their real copyright lines. **Copyleft texts (GPL-2.0, LGPL-2.1)** carry the redistribution +obligations (corresponding source + relink path; see `../NOTICE.md`). The only text not vendored here +is zlib/liblzma's permissive notice, which travels inside the squashfs-tools source the build script +fetches. + +## diskOS's own license +The **Python installer, build scripts, and docs** are licensed **MIT** - `Copyright (c) 2026 diskOS +contributors`. See [`../LICENSE`](../LICENSE). The **diskOS UI (`payload/mq_ui`)** ships as a +**binary-only** component (source not yet published, not MIT); see the README's *License* section. +Its embedded third-party notices - LVGL (MIT), JSMN (MIT), Font Awesome Free (OFL-1.1 fonts / +CC-BY-4.0 icons) - ship in this directory. diff --git a/licenses/THIRD_PARTY_BUNDLED.md b/licenses/THIRD_PARTY_BUNDLED.md new file mode 100644 index 0000000..f4c7de6 --- /dev/null +++ b/licenses/THIRD_PARTY_BUNDLED.md @@ -0,0 +1,40 @@ +# Third-party libraries bundled in a self-built onefile + +> **Scope:** this applies **only if you build a self-contained PyInstaller onefile yourself** +> (`build/build.sh`). The **published diskOS release does not ship such a binary** - it distributes +> source, and the Python runtime + these native libraries come from *your* system Python (see +> [`../NOTICE.md`](../NOTICE.md)), so this project does not redistribute them. If you *do* build and +> redistribute the onefile, **you** become the redistributor of everything it embeds. +> +> The onefile embeds the CPython runtime plus **~90 native shared libraries** its GUI/runtime pulls +> in (CPython + Tkinter GUI + pycryptodome + pyusb chains). The table below lists the principal +> permissive families with verbatim texts in [`bundled/`](bundled/); it is **illustrative, not a +> complete manifest** of all ~90 entries. To regenerate the full list for a given build, walk the +> extracted onefile's shared objects and map each to its distribution package. Note the GUI chain +> also pulls in **libudev (LGPL-2.1)** and **freetype (FTL/GPL dual)** - a CLI-only build +> (`excludes=['tkinter']`) avoids both. + +| Bundled library (`.so`) | License | Text | +|---|---|---| +| `libcrypto.so.3` (OpenSSL 3) | Apache-2.0 | [`bundled/libcrypto.copyright.txt`](bundled/libcrypto.copyright.txt) | +| `libbrotlicommon.so.1`, `libbrotlidec.so.1` (Brotli) | MIT | [`bundled/libbrotli.copyright.txt`](bundled/libbrotli.copyright.txt) | +| `libbsd.so.0` | BSD-2/3-Clause, ISC, etc. | [`bundled/libbsd.copyright.txt`](bundled/libbsd.copyright.txt) | +| `libbz2.so.1.0` (bzip2) | bzip2 (BSD-style) | [`bundled/libbz2.copyright.txt`](bundled/libbz2.copyright.txt) | +| `libcap.so.2` | BSD-3-Clause OR GPL-2.0-only (used under BSD) | [`bundled/libcap.copyright.txt`](bundled/libcap.copyright.txt) | +| `libX11.so.6` | MIT/X11 | [`bundled/libX11.copyright.txt`](bundled/libX11.copyright.txt) | +| `libXau.so.6` | MIT/X11 | [`bundled/libXau.copyright.txt`](bundled/libXau.copyright.txt) | +| `libXdmcp.so.6` | MIT/X11 | [`bundled/libXdmcp.copyright.txt`](bundled/libXdmcp.copyright.txt) | +| `libXext.so.6` | MIT/X11 | [`bundled/libXext.copyright.txt`](bundled/libXext.copyright.txt) | +| `libXft.so.2` | MIT/X11 | [`bundled/libXft.copyright.txt`](bundled/libXft.copyright.txt) | +| `libXrender.so.1` | MIT/X11 | [`bundled/libXrender.copyright.txt`](bundled/libXrender.copyright.txt) | +| `libXss.so.1` | MIT/X11 | [`bundled/libXss.copyright.txt`](bundled/libXss.copyright.txt) | +| `libBLT.2.5.so.8.6` (BLT, a Tk widget library) | BSD-style | [`bundled/libBLT.copyright.txt`](bundled/libBLT.copyright.txt) | + +CPython itself is PSF-licensed (see [`PSF-Python.txt`](PSF-Python.txt)); the Python packages `pyusb` +(BSD) and `pycryptodome` (BSD / public-domain) are covered in [`README.md`](README.md). + +These libraries are present **only in a self-built onefile**. The published release runs the +installer **from source** with your own Python (`./install.sh` then `./diskos-installer`), where they +come from *your* system's Python and are **not redistributed by this project**. (`bash build/build.sh` +is the separate, optional step that produces the onefile - if you run *that* and redistribute the +result, the obligations above become yours.) diff --git a/licenses/bundled/libBLT.copyright.txt b/licenses/bundled/libBLT.copyright.txt new file mode 100644 index 0000000..1bad876 --- /dev/null +++ b/licenses/bundled/libBLT.copyright.txt @@ -0,0 +1,258 @@ +This package was debianized by Gordon Russell on +Thu, 12 Mar 1998 11:19:54 +0000. + +It was originally downloaded from http://sourceforge.net/projects/blt +Now it's downloaded from http://sourceforge.net/projects/wize + +Files library/dd_protocols/* were deleted from the original distribution +because they are non-free. + +Files: * +Copyright: 1991-1998 by Lucent Technologies. +License: MIT-1 + +Files: generic/bltArrayObj.c +Copyright: 2000 Silicon Metrics, Inc. +License: MIT-4 + +Files: generic/bltHash.c, generic/bltHash.in +Copyright (c) 1991-1993 The Regents of the University of California. +Copyright (c) 1994 Sun Microsystems, Inc. +License: Tcl +Comment: Here and below the Tcl licence is omitted but the code or its + portions are clearly taken from the Tcl/Tk distributions. +Copyright: 2001 Silicon Metrics Corporation. +License: MIT-4 + +Files: generic/blt.h, generic/bltUnixMain.c, generic/bltWinDraw.c, generic/bltWinPrnt.c, generic/bltWinUtil.c +Copyright: 1991-1998 by Bell Labs Innovations for Lucent Technologies +License: MIT-1 + +Files: generic/bltInterp.h +Copyright: 1987-1993 The Regents of the University of California. +Copyright: 1993-1997 Lucent Technologies. +Copyright: 1994-1998 Sun Microsystems, Inc. +License: Tcl + +Files: generic/bltObjConfig.c, generic/bltObjConfig.h, generic/bltOldConfig.c +Copyright: 1990-1994 The Regents of the University of California +Copyright: 1994-1997 Sun Microsystems, Inc +License: Tcl + +Files: generic/bltParse.c +Copyright: 1987-1993 The Regents of the University of California +Copyright: 1994-1998 Sun Microsystems, Inc. +License: Tcl + +Files: generic/bltPool.c +Copyright: 2001 Silicon Metrics, Inc. +License: MIT-4 + +Files: generic/bltScrollbar.c +Copyright: 1990-1994 The Regents of the University of California +Copyright: 1994-1995 Sun Microsystems, Inc. +License: Tcl + +Files: generic/bltTed.c +Copyright: 1995 by AT&T Bell Laboratories +License: MIT-5 + +Files: generic/bltUnixMain.c +Copyright: 1993 The Regents of the University of California +License: MIT-6 +Copyright: 1991-1998 by Bell Labs Innovations for Lucent Technologies +License: MIT-1 + +Files: generic/bltUnixPipe.c, generic/bltWinDde.c +Copyright: 1997 by Sun Microsystems, Inc. +License: Tcl + +Files: generic/tkButton.c, generic/tkFrame.c, generic/tkMenubutton.c, generic/tkScrollbar.c +Copyright: 1990-1994 The Regents of the University of California +Copyright: 1994-1995 Sun Microsystems, Inc. +License: Tcl + +Files: generic/tkConsole.c, man/Blt_Tree.man3 +Copyright: 1995-1996 Sun Microsystems, Inc. +License: Tcl + +Files: html/tabset.html, html/tree.html, html/treeview.html +Copyright: 1995-1997 Roger E. Critchlow Jr. +License: MIT-1 +Comment: These files are created by the Roger E. Critchlow Jr. tool, which + automatically added the copyright line. + +Files: library/bltCanvEps.pro +Copyright: 1991-1997 Bell Labs Innovations for Lucent Technologies` +License: MIT-1 + +Files: library/bltGraph.pro +Copyright: 1989-1992 Regents of the University of California +License: MIT-6 +Copyright: 1991-1997 Bell Labs Innovations for Lucent Technologies +License: MIT-1 + +Files: man/*: +Copyright: 1991-1998 by Bell Labs Innovations for Lucent Technologies +License: MIT-1 + +Files: man/Blt_Vector.man3, man/tree.mann, man/vector.mann +Copyright: 1991-1997 by Lucent Technologies, Inc. +License: MIT-1 + +Files: man/hierbox.mann, man/hiertable.mann, man/treeview.mann +Copyright: 2001-2002 by Silicon Metrics Corporation. +License: MIT-4 + +Files: win/X11/keysymdef.h, win/X11/keysym.h, win/X11/X.h, win/X11/Xutil.h +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts +License: MIT-2 + +Files: win/X11/Xfuncproto.h +Copyright: 1989, 1991 by the Massachusetts Institute of Technology +License: MIT-3 + +Files: win/X11/Xlib.h +Copyright: 1985, 1986, 1987, 1991 by the Massachusetts Institute of Technology +License: MIT-3 + +License: MIT-1 + to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that the + copyright notice and warranty disclaimer appear in supporting documentation, + and that the names of Lucent Technologies any of their entities not be used + in advertising or publicity pertaining to distribution of the software + without specific, written prior permission. + + Lucent Technologies disclaims all warranties with regard to this software, + including all implied warranties of merchantability and fitness. In no event + shall Lucent Technologies be liable for any special, indirect or + consequential damages or any damages whatsoever resulting from loss of use, + data or profits, whether in an action of contract, negligence or other + tortuous action, arising out of or in connection with the use or performance + of this software. + +License: MIT-2 + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation, and that the names of Digital or MIT not be + used in advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + +License: MIT-3 + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of M.I.T. not be used in advertising + or publicity pertaining to distribution of the software without specific, + written prior permission. M.I.T. makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. + +License: MIT-4 + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and warranty + disclaimer appear in supporting documentation, and that the names + of Lucent Technologies any of their entities not be used in + advertising or publicity pertaining to distribution of the software + without specific, written prior permission. + + Silicon Metrics disclaims all warranties with regard to this + software, including all implied warranties of merchantability and + fitness. In no event shall Lucent Technologies be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether in + an action of contract, negligence or other tortuous action, arising + out of or in connection with the use or performance of this + software. + +License: Tcl + This software is copyrighted by the Regents of the University of + California, Sun Microsystems, Inc., Scriptics Corporation, + and other parties. The following terms apply to all files associated + with the software unless explicitly disclaimed in individual files. + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. + + GOVERNMENT USE: If you are acquiring this software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" + in the software and related documentation as defined in the Federal + Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + are acquiring the software on behalf of the Department of Defense, the + software shall be classified as "Commercial Computer Software" and the + Government shall have only "Restricted Rights" as defined in Clause + 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + authors grant the U.S. Government and others acting in its behalf + permission to use and distribute the software in accordance with the + terms specified in this license. + +License: MIT-5 + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and warranty + disclaimer appear in supporting documentation, and that the + names of AT&T Bell Laboratories any of their entities not be used + in advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + AT&T disclaims all warranties with regard to this software, including + all implied warranties of merchantability and fitness. In no event + shall AT&T be liable for any special, indirect or consequential + damages or any damages whatsoever resulting from loss of use, data + or profits, whether in an action of contract, negligence or other + tortuous action, arising out of or in connection with the use or + performance of this software. + +License: MIT-6 + Permission is hereby granted, without written agreement and without + license or royalty fees, to use, copy, modify, and distribute this + software and its documentation for any purpose, provided that the + above copyright notice and the following two paragraphs appear in + all copies of this software. + + IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/licenses/bundled/libX11.copyright.txt b/licenses/bundled/libX11.copyright.txt new file mode 100644 index 0000000..213f6ae --- /dev/null +++ b/licenses/bundled/libX11.copyright.txt @@ -0,0 +1,944 @@ +This package was downloaded from +https://xorg.freedesktop.org/releases/individual/lib/ + +The following is the 'standard copyright' agreed upon by most contributors, +and is currently the canonical license preferred by the X.Org Foundation. +This is a slight variant of the common MIT license form published by the +Open Source Initiative at https://opensource.org/licenses/mit-license.php + +Copyright holders of new code should use this license statement where +possible, and insert their name to this list. Please sort by surname +for people, and by the full name for other entities (e.g. Juliusz +Chroboczek sorts before Intel Corporation sorts before Daniel Stone). + +See each individual source file or directory for the license that applies +to that file. + +Copyright (C) 2003-2006,2008 Jamey Sharp, Josh Triplett +Copyright © 2009 Red Hat, Inc. +Copyright 1990-1992,1999,2000,2004,2009,2010 Oracle and/or its affiliates. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------- + +The following licenses are 'legacy' - usually MIT/X11 licenses with the name +of the copyright holder(s) in the license statement: + +Copyright 1984-1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +X Window System is a trademark of The Open Group. + + ---------------------------------------- + +Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1994, 1996 X Consortium +Copyright 2000 The XFree86 Project, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991 by +Digital Equipment Corporation + +Portions Copyright 1990, 1991 by Tektronix, Inc. + +Permission to use, copy, modify and distribute this documentation for +any purpose and without fee is hereby granted, provided that the above +copyright notice appears in all copies and that both that copyright notice +and this permission notice appear in all copies, and that the names of +Digital and Tektronix not be used in in advertising or publicity pertaining +to this documentation without specific, written prior permission. +Digital and Tektronix makes no representations about the suitability +of this documentation for any purpose. +It is provided ``as is'' without express or implied warranty. + + ---------------------------------------- + +Copyright (c) 1999-2000 Free Software Foundation, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +FREE SOFTWARE FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the Free Software Foundation +shall not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization from the +Free Software Foundation. + + ---------------------------------------- + +Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc. + All Rights Reserved + +This file is a component of an X Window System-specific implementation +of Xcms based on the TekColor Color Management System. TekColor is a +trademark of Tektronix, Inc. The term "TekHVC" designates a particular +color space that is the subject of U.S. Patent No. 4,985,853 (equivalent +foreign patents pending). Permission is hereby granted to use, copy, +modify, sell, and otherwise distribute this software and its +documentation for any purpose and without fee, provided that: + +1. This copyright, permission, and disclaimer notice is reproduced in + all copies of this software and any modification thereof and in + supporting documentation; +2. Any color-handling application which displays TekHVC color + cooordinates identifies these as TekHVC color coordinates in any + interface that displays these coordinates and in any associated + documentation; +3. The term "TekHVC" is always used, and is only used, in association + with the mathematical derivations of the TekHVC Color Space, + including those provided in this file and any equivalent pathways and + mathematical derivations, regardless of digital (e.g., floating point + or integer) representation. + +Tektronix makes no representation about the suitability of this software +for any purpose. It is provided "as is" and with all faults. + +TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE, +INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +(c) Copyright 1995 FUJITSU LIMITED +This is source code modified by FUJITSU LIMITED under the Joint +Development Agreement for the CDE/Motif PST. + + ---------------------------------------- + +Copyright 1992 by Oki Technosystems Laboratory, Inc. +Copyright 1992 by Fuji Xerox Co., Ltd. + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Oki Technosystems +Laboratory and Fuji Xerox not be used in advertising or publicity +pertaining to distribution of the software without specific, written +prior permission. +Oki Technosystems Laboratory and Fuji Xerox make no representations +about the suitability of this software for any purpose. It is provided +"as is" without express or implied warranty. + +OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS +LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1990, 1991, 1992, 1993, 1994 by FUJITSU LIMITED + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of FUJITSU LIMITED +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. +FUJITSU LIMITED makes no representations about the suitability of +this software for any purpose. +It is provided "as is" without express or implied warranty. + +FUJITSU LIMITED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL FUJITSU LIMITED BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + + +Copyright (c) 1995 David E. Wexelblat. All rights reserved + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL DAVID E. WEXELBLAT BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of David E. Wexelblat shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from David E. Wexelblat. + + ---------------------------------------- + +Copyright 1990, 1991 by OMRON Corporation + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name OMRON not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. OMRON makes no representations +about the suitability of this software for any purpose. It is provided +"as is" without express or implied warranty. + +OMRON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL OMRON BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991 by +Digital Equipment Corporation + +Portions Copyright 1990, 1991 by Tektronix, Inc + +Rewritten for X.org by Chris Lee + +Permission to use, copy, modify, distribute, and sell this documentation +for any purpose and without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. +Chris Lee makes no representations about the suitability for any purpose +of the information in this document. It is provided \`\`as-is'' without +express or implied warranty. + + ---------------------------------------- + +Copyright 1993 by Digital Equipment Corporation, Maynard, Massachusetts, +Copyright 1994 by FUJITSU LIMITED +Copyright 1994 by Sony Corporation + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Digital, FUJITSU +LIMITED and Sony Corporation not be used in advertising or publicity +pertaining to distribution of the software without specific, written +prior permission. + +DIGITAL, FUJITSU LIMITED AND SONY CORPORATION DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL, FUJITSU LIMITED +AND SONY CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + + +Copyright 1991 by the Open Software Foundation + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Open Software Foundation +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Open Software +Foundation makes no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +OPEN SOFTWARE FOUNDATION DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL OPEN SOFTWARE FOUNDATIONN BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1990, 1991, 1992,1993, 1994 by FUJITSU LIMITED +Copyright 1993, 1994 by Sony Corporation + +Permission to use, copy, modify, distribute, and sell this software and +its documentation for any purpose is hereby granted without fee, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of FUJITSU LIMITED and Sony Corporation +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. FUJITSU LIMITED and +Sony Corporation makes no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +FUJITSU LIMITED AND SONY CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL FUJITSU LIMITED OR SONY CORPORATION BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright (c) 1993, 1995 by Silicon Graphics Computer Systems, Inc. + +Permission to use, copy, modify, and distribute this +software and its documentation for any purpose and without +fee is hereby granted, provided that the above copyright +notice appear in all copies and that both that copyright +notice and this permission notice appear in supporting +documentation, and that the name of Silicon Graphics not be +used in advertising or publicity pertaining to distribution +of the software without specific prior written permission. +Silicon Graphics makes no representation about the suitability +of this software for any purpose. It is provided "as is" +without any express or implied warranty. + +SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON +GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH +THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1991, 1992, 1993, 1994 by FUJITSU LIMITED +Copyright 1993 by Digital Equipment Corporation + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of FUJITSU LIMITED and +Digital Equipment Corporation not be used in advertising or publicity +pertaining to distribution of the software without specific, written +prior permission. FUJITSU LIMITED and Digital Equipment Corporation +makes no representations about the suitability of this software for +any purpose. It is provided "as is" without express or implied +warranty. + +FUJITSU LIMITED AND DIGITAL EQUIPMENT CORPORATION DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +FUJITSU LIMITED AND DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1992, 1993 by FUJITSU LIMITED +Copyright 1993 by Fujitsu Open Systems Solutions, Inc. +Copyright 1994 by Sony Corporation + +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of FUJITSU LIMITED, +Fujitsu Open Systems Solutions, Inc. and Sony Corporation not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. +FUJITSU LIMITED, Fujitsu Open Systems Solutions, Inc. and +Sony Corporation make no representations about the suitability of +this software for any purpose. It is provided "as is" without +express or implied warranty. + +FUJITSU LIMITED, FUJITSU OPEN SYSTEMS SOLUTIONS, INC. AND SONY +CORPORATION DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL FUJITSU OPEN SYSTEMS SOLUTIONS, INC., FUJITSU LIMITED +AND SONY CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1987, 1988, 1990, 1993 by Digital Equipment Corporation, +Maynard, Massachusetts, + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + ---------------------------------------- + +Copyright 1993 by SunSoft, Inc. +Copyright 1999-2000 by Bruno Haible + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the names of SunSoft, Inc. and +Bruno Haible not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. SunSoft, Inc. and Bruno Haible make no representations +about the suitability of this software for any purpose. It is +provided "as is" without express or implied warranty. + +SunSoft Inc. AND Bruno Haible DISCLAIM ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS, IN NO EVENT SHALL SunSoft, Inc. OR Bruno Haible BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1991 by the Open Software Foundation +Copyright 1993 by the TOSHIBA Corp. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of Open Software Foundation and TOSHIBA +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Open Software +Foundation and TOSHIBA make no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +OPEN SOFTWARE FOUNDATION AND TOSHIBA DISCLAIM ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL OPEN SOFTWARE FOUNDATIONN OR TOSHIBA BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1988 by Wyse Technology, Inc., San Jose, Ca., + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name Wyse not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +WYSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + ---------------------------------------- + + +Copyright 1991 by the Open Software Foundation +Copyright 1993, 1994 by the Sony Corporation + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of Open Software Foundation and +Sony Corporation not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. +Open Software Foundation and Sony Corporation make no +representations about the suitability of this software for any purpose. +It is provided "as is" without express or implied warranty. + +OPEN SOFTWARE FOUNDATION AND SONY CORPORATION DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OPEN +SOFTWARE FOUNDATIONN OR SONY CORPORATION BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1992, 1993 by FUJITSU LIMITED +Copyright 1993 by Fujitsu Open Systems Solutions, Inc. + +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of FUJITSU LIMITED and +Fujitsu Open Systems Solutions, Inc. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. +FUJITSU LIMITED and Fujitsu Open Systems Solutions, Inc. makes no +representations about the suitability of this software for any purpose. +It is provided "as is" without express or implied warranty. + +FUJITSU LIMITED AND FUJITSU OPEN SYSTEMS SOLUTIONS, INC. DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL FUJITSU OPEN SYSTEMS +SOLUTIONS, INC. AND FUJITSU LIMITED BE LIABLE FOR ANY SPECIAL, INDIRECT +OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1993, 1994 by Sony Corporation + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Sony Corporation +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. +Sony Corporation makes no representations about the suitability of +this software for any purpose. It is provided "as is" without +express or implied warranty. + +SONY CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL SONY CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1986, 1998 The Open Group +Copyright (c) 2000 The XFree86 Project, Inc. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM OR THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Except as contained in this notice, the name of the X Consortium or of the +XFree86 Project shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization from the X Consortium and the XFree86 Project. + + ---------------------------------------- + +Copyright 1990, 1991 by OMRON Corporation, NTT Software Corporation, + and Nippon Telegraph and Telephone Corporation +Copyright 1991 by the Open Software Foundation +Copyright 1993 by the FUJITSU LIMITED + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of OMRON, NTT Software, NTT, and +Open Software Foundation not be used in advertising or publicity +pertaining to distribution of the software without specific, +written prior permission. OMRON, NTT Software, NTT, and Open Software +Foundation make no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +OMRON, NTT SOFTWARE, NTT, AND OPEN SOFTWARE FOUNDATION +DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT +SHALL OMRON, NTT SOFTWARE, NTT, OR OPEN SOFTWARE FOUNDATION BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 1988 by Wyse Technology, Inc., San Jose, Ca, +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL AND WYSE DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL DIGITAL OR WYSE BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + + +Copyright 1991, 1992 by Fuji Xerox Co., Ltd. +Copyright 1992, 1993, 1994 by FUJITSU LIMITED + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Fuji Xerox, +FUJITSU LIMITED not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. Fuji Xerox, FUJITSU LIMITED make no representations +about the suitability of this software for any purpose. +It is provided "as is" without express or implied warranty. + +FUJI XEROX, FUJITSU LIMITED DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL FUJI XEROX, +FUJITSU LIMITED BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 2006 Josh Triplett + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + ---------------------------------------- + +(c) Copyright 1996 by Sebastien Marineau and Holger Veit + + + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +HOLGER VEIT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Except as contained in this notice, the name of Sebastien Marineau or Holger Veit +shall not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Holger Veit or +Sebastien Marineau. + + ---------------------------------------- + +Copyright 1990, 1991 by OMRON Corporation, NTT Software Corporation, + and Nippon Telegraph and Telephone Corporation +Copyright 1991 by the Open Software Foundation +Copyright 1993 by the TOSHIBA Corp. +Copyright 1993, 1994 by Sony Corporation +Copyright 1993, 1994 by the FUJITSU LIMITED + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of OMRON, NTT Software, NTT, Open +Software Foundation, and Sony Corporation not be used in advertising +or publicity pertaining to distribution of the software without specific, +written prior permission. OMRON, NTT Software, NTT, Open Software +Foundation, and Sony Corporation make no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +OMRON, NTT SOFTWARE, NTT, OPEN SOFTWARE FOUNDATION, AND SONY +CORPORATION DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT +SHALL OMRON, NTT SOFTWARE, NTT, OPEN SOFTWARE FOUNDATION, OR SONY +CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright 2000 by Bruno Haible + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Bruno Haible not +be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Bruno Haible +makes no representations about the suitability of this software for +any purpose. It is provided "as is" without express or implied +warranty. + +Bruno Haible DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL Bruno Haible BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright © 2003 Keith Packard + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Keith Packard not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. Keith Packard makes no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + +Copyright (c) 2007-2009, Troy D. Hanson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------- + +Copyright 1992, 1993 by TOSHIBA Corp. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of TOSHIBA not be used in advertising +or publicity pertaining to distribution of the software without specific, +written prior permission. TOSHIBA make no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +TOSHIBA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +TOSHIBA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + + ---------------------------------------- + +Copyright IBM Corporation 1993 + +All Rights Reserved + +License to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of IBM not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS, AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS, IN NO EVENT SHALL +IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + ---------------------------------------- + +Copyright 1990, 1991 by OMRON Corporation, NTT Software Corporation, + and Nippon Telegraph and Telephone Corporation + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of OMRON, NTT Software, and NTT +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. OMRON, NTT Software, +and NTT make no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +OMRON, NTT SOFTWARE, AND NTT, DISCLAIM ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS, IN NO EVENT SHALL OMRON, NTT SOFTWARE, OR NTT, BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/bundled/libXau.copyright.txt b/licenses/bundled/libXau.copyright.txt new file mode 100644 index 0000000..60b7465 --- /dev/null +++ b/licenses/bundled/libXau.copyright.txt @@ -0,0 +1,24 @@ +This package was downloaded from +https://xorg.freedesktop.org/releases/individual/lib/ + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. diff --git a/licenses/bundled/libXdmcp.copyright.txt b/licenses/bundled/libXdmcp.copyright.txt new file mode 100644 index 0000000..f4cec19 --- /dev/null +++ b/licenses/bundled/libXdmcp.copyright.txt @@ -0,0 +1,26 @@ +This package was downloaded from +http://xorg.freedesktop.org/releases/individual/lib/ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Keith Packard, MIT X Consortium diff --git a/licenses/bundled/libXext.copyright.txt b/licenses/bundled/libXext.copyright.txt new file mode 100644 index 0000000..60cc18d --- /dev/null +++ b/licenses/bundled/libXext.copyright.txt @@ -0,0 +1,199 @@ +This package was downloaded from +http://xorg.freedesktop.org/releases/individual/lib/ + +Copyright 1986, 1987, 1988, 1989, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Copyright (c) 1996 Digital Equipment Corporation, Maynard, Massachusetts. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING, +BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Digital Equipment Corporation +shall not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Digital +Equipment Corporation. + +Copyright (c) 1997 by Silicon Graphics Computer Systems, Inc. +Permission to use, copy, modify, and distribute this +software and its documentation for any purpose and without +fee is hereby granted, provided that the above copyright +notice appear in all copies and that both that copyright +notice and this permission notice appear in supporting +documentation, and that the name of Silicon Graphics not be +used in advertising or publicity pertaining to distribution +of the software without specific prior written permission. +Silicon Graphics makes no representation about the suitability +of this software for any purpose. It is provided "as is" +without any express or implied warranty. +SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON +GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH +THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Copyright 1992 Network Computing Devices + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of NCD. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. NCD. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +NCD. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NCD. +BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Copyright 1991,1993 by Digital Equipment Corporation, Maynard, Massachusetts, +and Olivetti Research Limited, Cambridge, England. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Digital or Olivetti +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL AND OLIVETTI DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THEY BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +Copyright 1986, 1987, 1988 by Hewlett-Packard Corporation + +Permission to use, copy, modify, and distribute this +software and its documentation for any purpose and without +fee is hereby granted, provided that the above copyright +notice appear in all copies and that both that copyright +notice and this permission notice appear in supporting +documentation, and that the name of Hewlett-Packard not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +Hewlett-Packard makes no representations about the +suitability of this software for any purpose. It is provided +"as is" without express or implied warranty. + +This software is not subject to any license of the American +Telephone and Telegraph Company or of the Regents of the +University of California. + +Copyright (c) 1994, 1995 Hewlett-Packard Company + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL HEWLETT-PACKARD COMPANY BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the Hewlett-Packard +Company shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization from the Hewlett-Packard Company. + +Copyright Digital Equipment Corporation, 1996 + +Permission to use, copy, modify, distribute, and sell this +documentation for any purpose is hereby granted without fee, +provided that the above copyright notice and this permission +notice appear in all copies. Digital Equipment Corporation +makes no representations about the suitability for any purpose +of the information in this document. This documentation is +provided ``as is'' without express or implied warranty. + +Copyright (c) 1999, 2005, 2006, Oracle and/or its affiliates. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +Copyright (c) 1989 X Consortium, Inc. and Digital Equipment Corporation. +Copyright (c) 1992 X Consortium, Inc. and Intergraph Corporation. +Copyright (c) 1993 X Consortium, Inc. and Silicon Graphics, Inc. +Copyright (c) 1994, 1995 X Consortium, Inc. and Hewlett-Packard Company. + +Permission to use, copy, modify, and distribute this documentation for +any purpose and without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. +Digital Equipment Corporation, Intergraph Corporation, Silicon +Graphics, Hewlett-Packard, and the X Consortium make no +representations about the suitability for any purpose of the +information in this document. This documentation is provided ``as is'' +without express or implied warranty. diff --git a/licenses/bundled/libXft.copyright.txt b/licenses/bundled/libXft.copyright.txt new file mode 100644 index 0000000..ca4f16a --- /dev/null +++ b/licenses/bundled/libXft.copyright.txt @@ -0,0 +1,31 @@ +This package was downloaded from +http://xorg.freedesktop.org/releases/individual/lib/ + +This package was originally Debianized by Colin Walters + on Sun, 13 Oct 2002 15:01:50 -0400. + +Later versions were Debianized by Branden Robinson . + +Upstream maintainer: Keith Packard + +Copyright and license: + +Copyright © 2001,2003 Keith Packard + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Keith Packard not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. Keith Packard makes no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/bundled/libXrender.copyright.txt b/licenses/bundled/libXrender.copyright.txt new file mode 100644 index 0000000..7a7c4df --- /dev/null +++ b/licenses/bundled/libXrender.copyright.txt @@ -0,0 +1,41 @@ +This package was downloaded from +https://xorg.freedesktop.org/releases/individual/lib/ + +Copyright © 2001,2003 Keith Packard + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Keith Packard not be used in +advertising or publicity pertaining to distribution of the software without +specific, written prior permission. Keith Packard makes no +representations about the suitability of this software for any purpose. It +is provided "as is" without express or implied warranty. + +KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +Copyright © 2000 SuSE, Inc. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of SuSE not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. SuSE makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE +BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/bundled/libXss.copyright.txt b/licenses/bundled/libXss.copyright.txt new file mode 100644 index 0000000..268a5f1 --- /dev/null +++ b/licenses/bundled/libXss.copyright.txt @@ -0,0 +1,52 @@ +This package was downloaded from +https://xorg.freedesktop.org/releases/individual/lib/ + +Copyright (c) 1992 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright (C) 2003 The XFree86 Project, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the XFree86 Project +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +from the XFree86 Project. diff --git a/licenses/bundled/libbrotli.copyright.txt b/licenses/bundled/libbrotli.copyright.txt new file mode 100644 index 0000000..7e6f0ac --- /dev/null +++ b/licenses/bundled/libbrotli.copyright.txt @@ -0,0 +1,30 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: brotli +Source: https://github.com/google/brotli + +Files: * +Copyright: 2009, 2010, 2013-2015 by the Brotli Authors +License: MIT + +Files: debian/* +Copyright: 2015 Tomasz Buchert +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/licenses/bundled/libbsd.copyright.txt b/licenses/bundled/libbsd.copyright.txt new file mode 100644 index 0000000..7814d8f --- /dev/null +++ b/licenses/bundled/libbsd.copyright.txt @@ -0,0 +1,558 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: + * +Copyright: + Copyright © 2004-2024 Guillem Jover +License: BSD-3-clause + +Files: + include/bsd/err.h + include/bsd/stdlib.h + include/bsd/sys/param.h + include/bsd/unistd.h + src/bsd_getopt.c + src/err.c + src/fgetln.c + src/progname.c +Copyright: + Copyright © 2005, 2008-2012, 2019 Guillem Jover + Copyright © 2005 Hector Garcia Alvarez + Copyright © 2005 Aurelien Jarno + Copyright © 2006 Robert Millan + Copyright © 2018 Facebook, Inc. +License: BSD-3-clause + +Files: + include/bsd/netinet/ip_icmp.h + include/bsd/sys/bitstring.h + include/bsd/sys/queue.h + include/bsd/sys/time.h + include/bsd/timeconv.h + include/bsd/vis.h + man/bitstring.3bsd + man/errc.3bsd + man/explicit_bzero.3bsd + man/fgetln.3bsd + man/fgetwln.3bsd + man/fpurge.3bsd + man/funopen.3bsd + man/getbsize.3bsd + man/heapsort.3bsd + man/nlist.3bsd + man/pwcache.3bsd + man/queue.3bsd + man/radixsort.3bsd + man/reallocarray.3bsd + man/reallocf.3bsd + man/setmode.3bsd + man/strmode.3bsd + man/strnstr.3bsd + man/strtoi.3bsd + man/strtou.3bsd + man/unvis.3bsd + man/vis.3bsd + man/wcslcpy.3bsd + src/getbsize.c + src/heapsort.c + src/merge.c + src/nlist.c + src/pwcache.c + src/radixsort.c + src/setmode.c + src/strmode.c + src/strnstr.c + src/strtoi.c + src/strtou.c + src/unvis.c +Copyright: + Copyright © 1980, 1982, 1986, 1989-1994 + The Regents of the University of California. All rights reserved. + Copyright © 1992 Keith Muller. + Copyright © 2001 Mike Barcroft + . + Some code is derived from software contributed to Berkeley by + the American National Standards Committee X3, on Information + Processing Systems. + . + Some code is derived from software contributed to Berkeley by + Peter McIlroy. + . + Some code is derived from software contributed to Berkeley by + Ronnie Kon at Mindcraft Inc., Kevin Lew and Elmer Yglesias. + . + Some code is derived from software contributed to Berkeley by + Dave Borman at Cray Research, Inc. + . + Some code is derived from software contributed to Berkeley by + Paul Vixie. + . + Some code is derived from software contributed to Berkeley by + Chris Torek. + . + Copyright © UNIX System Laboratories, Inc. + All or some portions of this file are derived from material licensed + to the University of California by American Telephone and Telegraph + Co. or Unix System Laboratories, Inc. and are reproduced herein with + the permission of UNIX System Laboratories, Inc. +License: BSD-3-clause-Regents + +Files: + src/vis.c +Copyright: + Copyright © 1989, 1993 + The Regents of the University of California. All rights reserved. + . + Copyright © 1999, 2005 The NetBSD Foundation, Inc. + All rights reserved. +License: BSD-3-clause-Regents and BSD-2-clause-NetBSD + +Files: + include/bsd/libutil.h +Copyright: + Copyright © 1996 Peter Wemm . + All rights reserved. + Copyright © 2002 Networks Associates Technology, Inc. + All rights reserved. +License: BSD-3-clause-author + +Files: + man/timeradd.3bsd +Copyright: + Copyright © 2009 Jukka Ruohonen + Copyright © 1999 Kelly Yancey + All rights reserved. +License: BSD-3-clause-John-Birrell + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the author nor the names of any co-contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +Files: + man/setproctitle.3bsd +Copyright: + Copyright © 1995 Peter Wemm + All rights reserved. +License: BSD-5-clause-Peter-Wemm + Redistribution and use in source and binary forms, with or without + modification, is permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice immediately at the beginning of the file, without modification, + this list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. This work was done expressly for inclusion into FreeBSD. Other use + is permitted provided this notation is included. + 4. Absolutely no warranty of function or purpose is made by the author + Peter Wemm. + 5. Modifications may be freely made to this file providing the above + conditions are met. + +Files: + include/bsd/stringlist.h + man/arc4random.3bsd + man/fmtcheck.3bsd + man/humanize_number.3bsd + man/stringlist.3bsd + man/timeval.3bsd + src/fmtcheck.c + src/humanize_number.c + src/stringlist.c + src/strtonum.c +Copyright: + Copyright © 1994, 1997-2000, 2002, 2008, 2010, 2014 + The NetBSD Foundation, Inc. + Copyright © 2013 John-Mark Gurney + All rights reserved. + . + Copyright © 2014 The NetBSD Foundation, Inc. + All rights reserved. + . + Some code was derived from software contributed to The NetBSD Foundation + by Taylor R. Campbell. + . + Some code was contributed to The NetBSD Foundation by Allen Briggs. + . + Some code was contributed to The NetBSD Foundation by Luke Mewburn. + . + Some code is derived from software contributed to The NetBSD Foundation + by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. + . + Some code is derived from software contributed to The NetBSD Foundation + by Julio M. Merino Vidal, developed as part of Google's Summer of Code + 2005 program. + . + Some code is derived from software contributed to The NetBSD Foundation + by Christos Zoulas. + . + Some code is derived from software contributed to The NetBSD Foundation + by Jukka Ruohonen. +License: BSD-2-clause-NetBSD + +Files: + include/bsd/sys/endian.h + man/byteorder.3bsd + man/closefrom.3bsd + man/expand_number.3bsd + man/flopen.3bsd + man/getpeereid.3bsd + man/pidfile.3bsd + src/expand_number.c + src/pidfile.c + src/reallocf.c + src/timeconv.c +Copyright: + Copyright © 1998, M. Warner Losh + All rights reserved. + . + Copyright © 2001 Dima Dorfman. + All rights reserved. + . + Copyright © 2001 FreeBSD Inc. + All rights reserved. + . + Copyright © 2002 Thomas Moestl + All rights reserved. + . + Copyright © 2002 Mike Barcroft + All rights reserved. + . + Copyright © 2005 Pawel Jakub Dawidek + All rights reserved. + . + Copyright © 2005 Colin Percival + All rights reserved. + . + Copyright © 2007 Eric Anderson + Copyright © 2007 Pawel Jakub Dawidek + All rights reserved. + . + Copyright © 2007 Dag-Erling Coïdan Smørgrav + All rights reserved. + . + Copyright © 2009 Advanced Computing Technologies LLC + All rights reserved. + . + Copyright © 2011 Guillem Jover +License: BSD-2-clause + +Files: + src/flopen.c +Copyright: + Copyright © 2007-2009 Dag-Erling Coïdan Smørgrav + All rights reserved. +License: BSD-2-clause-verbatim + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer + in this position and unchanged. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +Files: + include/bsd/sys/tree.h + man/fparseln.3bsd + man/tree.3bsd + src/fparseln.c +Copyright: + Copyright © 1997 Christos Zoulas. + All rights reserved. + . + Copyright © 2002 Niels Provos + All rights reserved. +License: BSD-2-clause-author + +Files: + include/bsd/readpassphrase.h + man/readpassphrase.3bsd + man/strlcpy.3bsd + man/strtonum.3bsd + src/arc4random.c + src/arc4random_linux.h + src/arc4random_uniform.c + src/arc4random_unix.h + src/arc4random_win.h + src/closefrom.c + src/freezero.c + src/getentropy_aix.c + src/getentropy_bsd.c + src/getentropy_hpux.c + src/getentropy_hurd.c + src/getentropy_linux.c + src/getentropy_osx.c + src/getentropy_solaris.c + src/getentropy_win.c + src/readpassphrase.c + src/reallocarray.c + src/recallocarray.c + src/strlcat.c + src/strlcpy.c + test/explicit_bzero.c + test/strtonum.c +Copyright: + Copyright © 2004 Ted Unangst and Todd Miller + All rights reserved. + . + Copyright © 1996 David Mazieres + Copyright © 1998, 2000-2002, 2004-2005, 2007, 2010, 2012-2015 + Todd C. Miller + Copyright © 2004 Ted Unangst + Copyright © 2004 Otto Moerbeek + Copyright © 2008 Damien Miller + Copyright © 2008, 2010-2011, 2016-2017 Otto Moerbeek + Copyright © 2013 Markus Friedl + Copyright © 2014 Bob Beck + Copyright © 2014 Brent Cook + Copyright © 2014 Pawel Jakub Dawidek + Copyright © 2014 Theo de Raadt + Copyright © 2014 Google Inc. + Copyright © 2015 Michael Felt + Copyright © 2015, 2022 Guillem Jover +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Files: + src/inet_net_pton.c +Copyright: + Copyright © 1996 by Internet Software Consortium. +License: ISC-Original + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + +Files: + src/setproctitle.c +Copyright: + Copyright © 2010 William Ahern + Copyright © 2012 Guillem Jover +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + +Files: + src/explicit_bzero.c + src/chacha_private.h +Copyright: + None +License: public-domain + Public domain. + +Files: + man/mdX.3bsd +Copyright: + None +License: Beerware + "THE BEER-WARE LICENSE" (Revision 42): + wrote this file. As long as you retain this notice you + can do whatever you want with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + +License: BSD-3-clause-Regents + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +License: BSD-3-clause-author + Redistribution and use in source and binary forms, with or without + modification, is permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: BSD-2-clause-NetBSD + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +License: BSD-2-clause-author + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: BSD-2-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/licenses/bundled/libbz2.copyright.txt b/licenses/bundled/libbz2.copyright.txt new file mode 100644 index 0000000..6c5e0a9 --- /dev/null +++ b/licenses/bundled/libbz2.copyright.txt @@ -0,0 +1,48 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: bzip2 +Source: http://www.bzip.org/ + +Files: * +Copyright: 1996-2010 Julian R Seward +License: BSD-variant + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + . + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + . + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: debian/* +Copyright: 2018 Nicolas Boulenguez + 2012-2015 Santiago Ruano Rincón + 2014 Canonical Ltd. + 2004-2011 Anibal Monsalve Salazar + 1999-2002 Philippe Troin + 1997-1999 Anthony Fok +License: GPL-2 + The full text of the GNU General Public License version 2 + can be found in /usr/share/common-licenses/GPL-2. diff --git a/licenses/bundled/libcap.copyright.txt b/licenses/bundled/libcap.copyright.txt new file mode 100644 index 0000000..eb579c9 --- /dev/null +++ b/licenses/bundled/libcap.copyright.txt @@ -0,0 +1,112 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: libcap +Upstream-Contact: Andrew G. Morgan +Source: https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/ + +Files: * +Copyright: 1997-2016 Andrew G. Morgan +License: BSD-3-clause or GPL-2 + +Files: libcap/cap_text.c +Copyright: 1997-2008 Andrew G. Morgan + 1997 Andrew Main +License: BSD-3-clause or GPL-2 + +Files: libcap/include/sys/capability.h +Copyright: 1997-2008 Andrew G. Morgan + 1997 Aleph One +License: BSD-3-clause or GPL-2 + +Files: libcap/include/sys/securebits.h +Copyright: 2010 Serge Hallyn +License: BSD-3-clause or GPL-2 + +Files: progs/old/sucap.c +Copyright: 1998 Finn Arne Gangstad +License: BSD-3-clause or GPL-2 + +Files: contrib/* +Copyright: 2006, Matt Kern + 2008, Andrew G. Morgan + 2008, Chris Friedhoff +License: BSD-3-clause or GPL-2 + +Files: debian/* +Copyright: 2014, Daniel Baumann + 2014-2022, Christian Kastner +License: BSD-3-clause or GPL-2+ + +Files: debian/manpages/* +Copyright: 1997-2014 Andrew G. Morgan + 2011 Scott Schaefer +License: BSD-3-clause or GPL-2 + +Files: debian/patches/* +Copyright: 2011, Andrew Straw + 2011, Zhi Li + 2014-2016, Christian Kastner + 2015, Helmut Grohne +License: BSD-3-clause or GPL-2+ + +License: BSD-3-clause + Redistribution and use in source and binary forms of libcap, with + or without modification, are permitted provided that the following + conditions are met: + . + 1. Redistributions of source code must retain any existing copyright + notice, and this entire permission notice in its entirety, + including the disclaimer of warranties. + . + 2. Redistributions in binary form must reproduce all prior and current + copyright notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + . + 3. The name of any author may not be used to endorse or promote + products derived from this software without their specific prior + written permission. + . + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + +License: GPL-2 + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 2 of the License. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-2 file. + +License: GPL-2+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-2 file. diff --git a/licenses/bundled/libcrypto.copyright.txt b/licenses/bundled/libcrypto.copyright.txt new file mode 100644 index 0000000..3883f88 --- /dev/null +++ b/licenses/bundled/libcrypto.copyright.txt @@ -0,0 +1,70 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: OpenSSL +Source: https://www.openssl.org + +Files: * +Copyright: 1995-2020, The OpenSSL Project Authors + 1995-1998, Eric A. Young, Tim J. Hudson + 2004-2014 Akamai Technologies. + 2008 Andy Polyakov + 2017 BaishanCloud. + 2015 CloudFlare Inc. + 2014-2016 Cryptography Research Inc. + 2012-2014 Daniel J. Bernstein + 2004 EdelKey Project. + 2011 Google Inc. + 2018-2019 IBM Corp. + 2012,2014 Intel Corporation. + 2012-2016 Jean-Philippe Aumasson + 2007 KISA(Korea Information Security Agency). + 2004 Kungliga Tekniska Högskolan + 2017 National Security Research Institute. + 2006 Network Resonance Inc. + 2005,2007-2020 Nokia + 2006 NTT (Nippon Telegraph and Telephone Corporation) + 2002,2017-2020 Oracle and/or its affiliates. + 1995 Patrick Powell + 2019 Red Hat Inc. + 2017 Ribose Inc. + 2004,2018 Richard Levitte + 2011 RTFM Inc. + 2012 Samuel Neves + 2015-2020 Siemens AG + 2002 The OpenTSA Project. + 2013-2014 Timo Teräs + 2016 Viktor Dukhovni . + 2016 VMS Software Inc. +License: Apache-2.0 + +License: Apache-2.0 + Licensed under the Apache License 2.0 (the "License"). You may not use + this file except in compliance with the License. You can obtain a copy + in the file LICENSE in the source distribution or at + https://www.openssl.org/source/license.html + . + On Debian systems, the complete text of the Apache 2.0 License + can be found in `/usr/share/common-licenses/Apache-2.0' + +Files: debian/* +Copyright: Christoph Martin, Kurt Roeckx, Sebastian Andrzej Siewior +License: Apache-2.0 + +Files: external/perl/Text-Template-1.56/* +Copyright: 2013, Mark Jason Dominus . +License: Artistic or GPL-1+ + +License: Artistic + This program is free software; you can redistribute it and/or modify + it under the terms of the Artistic License, which comes with Perl. + . + On Debian systems, the complete text of the Artistic License can be + found in `/usr/share/common-licenses/Artistic'. + +License: GPL-1+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + . + On Debian systems, the complete text of version 1 of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-1'. diff --git a/licenses/pycryptodome-LICENSE.txt b/licenses/pycryptodome-LICENSE.txt new file mode 100644 index 0000000..3008ff7 --- /dev/null +++ b/licenses/pycryptodome-LICENSE.txt @@ -0,0 +1,61 @@ +The source code in PyCryptodome is partially in the public domain +and partially released under the BSD 2-Clause license. + +In either case, there are minimal if no restrictions on the redistribution, +modification and usage of the software. + +Public domain +============= + +All code originating from PyCrypto is free and unencumbered software +released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +BSD license +=========== + +All direct contributions to PyCryptodome are released under the following +license. The copyright of each piece belongs to the respective author. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/payload/S97diskos_install b/payload/S97diskos_install new file mode 100755 index 0000000..c301868 --- /dev/null +++ b/payload/S97diskos_install @@ -0,0 +1,228 @@ +#!/bin/sh +# diskOS first-boot installer. Runs at S97 - BEFORE S98FIIO launches the UI, and after +# S21mount_ubifs has mounted /usr/data. Installs the diskOS UI into /usr/data (which survives +# rootfs flashes) from - in deterministic precedence - (1) the copy EMBEDDED in this rootfs at +# /opt/diskos/mq_ui (present after a diskOS flash; needs no SD card), else (2) /diskos/mq_ui as +# a fallback source. EITHER source is copied ONLY after verifying it against the manifest baked into +# this rootfs (/etc/diskos_manifest): exact size, ELF32-LE, MIPS machine, and SHA-256. This stops a +# corrupt, wrong, or substituted UI from being copied in and run as root. Read-only SD mount. +# +# FAIL-CLOSED CONTRACT: the diskOS boot override in fiio_init.sh runs our UI ONLY when BOTH +# /usr/data/mq_ui AND the /usr/data/mq_player symlink exist; otherwise it falls back to the STOCK +# rootfs UI. So the load-bearing safety lever is the mq_player symlink: whenever we cannot PROVE +# /usr/data/mq_ui matches the manifest (missing/bad manifest, failed verify, failed repair), we +# remove that symlink (and move the binary aside) so the stock UI runs instead of an unverified +# binary. Every SD-touching op is time-bounded so a faulty card can delay but never hang boot. +# +# RESIDUAL LIMITS (documented, not shell-fixable): a process wedged in uninterruptible (D-state) +# kernel I/O cannot be killed by any signal, so a truly dead SD controller can still stall this +# script until the kernel gives up on the I/O; and if BusyBox lacks the `timeout` applet the +# bounds degrade to best-effort. Neither is reachable from userspace shell. +# EARLIEST fail-closed trap - the FIRST executable statement in the script (only comments precede +# it; no file operation runs before it). For every catchable TERMINATING signal it arms an inline +# handler that performs the load-bearing action (drop the mq_player symlink -> boot override +# disabled; move the binary aside) and exit 1, so even a signal during setup can't leave a +# pre-existing unverified override enabled for S98. Ordered most-likely-first (TERM/HUP/INT) because +# POSIX sh has no atomic multi-signal trap: the sub-microsecond gradual-arming window across the +# loop is an irreducible shell limitation, minimized by arming the boot-relevant signals first. +# Excludes SIGKILL/SIGSTOP (uncatchable) and the non-terminating/stop/ignore-default signals +# (TSTP/TTIN/TTOU/WINCH/URG/CHLD/CONT). The trailing `7` is SIGEMT: busybox ash rejects the NAME +# "EMT" but the deployment arch is MIPS where SIGEMT=7, so it's armed numerically (SIGSTKFLT is +# undefined on MIPS, so nothing further is needed). Per-signal arming (`2>/dev/null || true`) so a +# name an odd build rejects can't abort the set. Superseded below by the guarded trap once +# quarantine() exists. +SIGS="TERM HUP INT QUIT PWR ABRT ALRM PIPE USR1 USR2 XCPU XFSZ ILL TRAP BUS FPE SEGV SYS VTALRM PROF IO 7" +_qtrap='rm -rf /usr/data/mq_player 2>/dev/null; [ -e /usr/data/mq_ui ] && mv -f /usr/data/mq_ui /usr/data/mq_ui.rejected 2>/dev/null; exit 1' +# Arm the standard signals FIRST - this loop is the first executable statement (only the two var +# assignments above, which touch no files, precede it), so the boot-relevant signals (TERM/HUP/INT) +# are covered immediately. +for s in $SIGS; do trap "$_qtrap" "$s" 2>/dev/null || true; done +# THEN arm each real-time signal (SIGRTMIN..SIGRTMAX, 32..127 on MIPS Linux) AND record it in SIGS +# within the SAME iteration - so no batch list-build ever precedes arming. RT signals are catchable +# and default-terminate; nothing sends them to a boot init script, armed only for completeness. +# Per-signal arming skips any number the running kernel doesn't define (a 64-signal host arms +# 32..64; the MIPS device arms 32..127). +n=32; while [ "$n" -le 127 ]; do trap "$_qtrap" "$n" 2>/dev/null || true; SIGS="$SIGS $n"; n=$((n+1)); done + +LOG=/usr/data/diskos_install.log +log() { echo "$(date 2>/dev/null || true) S97: $*" >> "$LOG" 2>/dev/null; } + +# override_on: true iff the boot hook would launch our UI (it needs BOTH regular files present). +override_on() { [ -f /usr/data/mq_ui ] && [ -f /usr/data/mq_player ]; } + +# quarantine: fail-closed, with a checked postcondition. Drop the mq_player path FIRST (that alone +# disables the override), move the binary aside, then PROVE the override is off; if it somehow +# isn't (e.g. rm/mv failed), remove the binary itself as a last resort and, if even that fails, +# log CRITICAL and return non-zero rather than silently claiming safety. Returns 0 iff the override +# is provably disabled. +quarantine() { + rm -rf /usr/data/mq_player 2>/dev/null # clear file/dir/symlink at the path + [ -e /usr/data/mq_ui ] && mv -f /usr/data/mq_ui /usr/data/mq_ui.rejected 2>/dev/null + if override_on; then + rm -f /usr/data/mq_ui 2>/dev/null # last resort: remove the override binary + if override_on; then + log "CRITICAL: could NOT disable diskOS override${1:+ ($1)} (fs unwritable?) -- unverified UI MAY run" + return 1 + fi + fi + log "quarantined${1:+ ($1)} -> stock UI will run" + return 0 +} + +# Now that quarantine() exists, UPGRADE the early inline trap to the guarded one: while installed!=1 +# a signal quarantines (with the checked postcondition + CRITICAL logging) and exits non-zero; once +# installed=1 it skips quarantine so a completed install is never torn down. A stray signal before +# install merely downgrades to the STOCK UI (safe), never up to running something unverified. +# disarm() clears it on success paths. SIGKILL/SIGSTOP + D-state I/O are non-trappable residuals; +# and if /usr/data is unwritable, quarantine cannot unlink the override AND this script cannot stop +# the SysV dispatcher reaching S98 - unrecoverable-from-shell, logged CRITICAL (a true boot +# fail-stop belongs in the rootfs boot hook, tracked separately, not this S-script). +disarm() { for s in $SIGS; do trap - "$s" 2>/dev/null || true; done; } +installed=0 +for s in $SIGS; do trap '[ "$installed" = 1 ] || quarantine "signal"; exit 1' "$s" 2>/dev/null || true; done + +# TO: run a command under a hard time bound. Prefer SIGKILL (-s KILL) so a stuck-but-killable op +# is force-terminated, not just SIGTERM'd. Falls back to running directly only if `timeout` is +# absent (logged once) - the one case we cannot bound from shell. +warned_to=0 +TO() { + if command -v timeout >/dev/null 2>&1; then + timeout -s KILL 60 "$@" + else + [ "$warned_to" = 1 ] || { log "WARNING: no 'timeout' applet -> SD ops are UNBOUNDED this boot"; warned_to=1; } + "$@" + fi +} + +# publish_symlink: make an already-verified /usr/data/mq_ui runnable (exec bit + mq_player link) and +# PROVE the symlink resolves to exactly /usr/data/mq_ui. Nukes whatever sits at the mq_player path +# first (a pre-existing dir/file/stale symlink would otherwise make `ln -sf` succeed without +# publishing the right target). Returns non-zero on any failure so the caller can quarantine. +publish_symlink() { + chmod +x /usr/data/mq_ui 2>/dev/null || return 1 # a 0644 hash-match would boot-select but not exec + rm -rf /usr/data/mq_player 2>/dev/null # remove any file/dir/symlink at the path + ln -sf /usr/data/mq_ui /usr/data/mq_player 2>/dev/null || return 1 + [ "$(readlink /usr/data/mq_player 2>/dev/null)" = /usr/data/mq_ui ] || return 1 # prove exact target + return 0 +} + +MAN=/etc/diskos_manifest +# No/'malformed manifest = we cannot verify anything -> fail closed (quarantine any existing override). +[ -f "$MAN" ] || { quarantine "no manifest" || exit 1; disarm; exit 0; } +MSHA=$(grep '^SHA256=' "$MAN" | cut -d= -f2) +MSIZE=$(grep '^SIZE=' "$MAN" | cut -d= -f2) +[ -n "$MSHA" ] && [ -n "$MSIZE" ] || { quarantine "malformed manifest" || exit 1; disarm; exit 0; } + +# verify_ui : 0 only if it exactly matches the manifest (size, ELF32-LE, MIPS, sha256). +verify_ui() { + f="$1"; [ -f "$f" ] || return 1 + sz=$(stat -c%s "$f" 2>/dev/null); [ -n "$sz" ] || sz=$(wc -c < "$f" 2>/dev/null | tr -d ' ') + [ "$sz" = "$MSIZE" ] || { log "verify $f: size $sz != $MSIZE"; return 1; } + [ "$(od -An -tx1 -N4 "$f" | tr -d ' ')" = "7f454c46" ] || { log "verify $f: not ELF"; return 1; } + [ "$(od -An -tx1 -j4 -N2 "$f" | tr -d ' ')" = "0101" ] || { log "verify $f: not ELF32-LE"; return 1; } # EI_CLASS=32,EI_DATA=LE + [ "$(od -An -tx1 -j18 -N2 "$f" | tr -d ' ')" = "0800" ] || { log "verify $f: not MIPS"; return 1; } + [ "$(sha256sum "$f" | cut -d' ' -f1)" = "$MSHA" ] || { log "verify $f: sha256 mismatch"; return 1; } + return 0 +} + +rm -f /usr/data/.mq_ui.tmp 2>/dev/null # never trust a leftover temp from a prior interrupted run + +# FAST PATH: an already-installed matching UI -> just repair exec bit + symlink and boot. +if verify_ui /usr/data/mq_ui; then + if publish_symlink; then + installed=1; disarm # a completed install: disarm before exit so no late-signal quarantine + log "already installed + verified -> repaired +x/symlink, booting diskOS" + exit 0 + fi + log "already-installed repair (chmod/ln) failed -> quarantining" + quarantine "repair failed" || exit 1 # can't guarantee it launches -> fall back to stock + disarm; exit 0 +fi + +# Reaching here means /usr/data/mq_ui is absent or does NOT match the manifest. Pre-emptively +# QUARANTINE any existing override NOW - BEFORE the SD window - so an interrupted copy can never +# leave the unverified binary enabled for S98 to launch. A successful SD install below republishes +# a verified one. (The whole-run trap above already covers signals; this closes the window +# deterministically even absent a signal.) +if [ -e /usr/data/mq_ui ] || [ -e /usr/data/mq_player ]; then + quarantine "unverified at boot" || exit 1 +fi + +# INSTALL PATH - SOURCE PRECEDENCE (deterministic, NOT newest-wins): try the copy EMBEDDED in this +# rootfs at /opt/diskos/mq_ui FIRST (present after a diskOS flash, needs no SD card); if it is absent +# OR its local copy fails manifest verification, fall through to /diskos/mq_ui as a recovery +# source. The embedded source only "wins" (copied=1, SD skipped) when its temp passes verify_ui - so +# a valid same-hash SD copy CAN rescue a corrupted embedded copy. The SD is a fallback SOURCE, not an +# override (no version/newer-wins). The winning temp is re-verified + published below. +copied=0; src= +EMBED=/opt/diskos/mq_ui +if [ -f "$EMBED" ]; then + # Embedded copy lives in the read-only rootfs we just flashed. TO-bound the cp for consistency + # (a NAND read fault shouldn't stall boot), and verify_ui the temp IN-BRANCH: only a verified + # embedded copy suppresses the SD fallback. + if TO cp "$EMBED" /usr/data/.mq_ui.tmp 2>/dev/null && verify_ui /usr/data/.mq_ui.tmp; then + copied=1; src=embedded; log "staged verified UI from embedded rootfs copy ($EMBED)" + else + rm -f /usr/data/.mq_ui.tmp 2>/dev/null + log "embedded UI absent or failed verify -> trying SD fallback" + fi +fi + +# SD FALLBACK: only when the rootfs carried no VERIFIED UI. Mount the SD read-only, copy +# /diskos/mq_ui to the LOCAL temp, unmount; mount/cp/umount are all TO-bounded; no verification +# ever runs against the (possibly faulty) card (the publish block below verifies the local copy). +if [ "$copied" != 1 ]; then + MP=/tmp/diskos_sd; mkdir -p "$MP"; mounted=0 + for dev in /dev/mmcblk0p1 /dev/mmcblk1p1 /dev/mmcblk0 /dev/mmcblk1; do + [ -b "$dev" ] || continue + for fs in exfat vfat; do + TO mount -t $fs -o ro "$dev" "$MP" 2>/dev/null && { mounted=1; break; } + done + [ "$mounted" = 1 ] && break + done + if [ "$mounted" = 1 ]; then + # No pre-stat of the SD path (that could hang) - let the bounded cp fail fast if it's absent. + if TO cp "$MP/diskos/mq_ui" /usr/data/.mq_ui.tmp 2>/dev/null; then + copied=1; src=SD + else + log "no readable /diskos/mq_ui (or copy timed out) -> nothing to install" + fi + if TO umount "$MP" 2>/dev/null || TO umount -l "$MP" 2>/dev/null; then :; else + log "WARNING: SD would not unmount (card may stay mounted; publish is unaffected - it uses the local copy)" + fi + else + log "no SD mounted -> nothing to install this boot" + fi +fi + +if [ "$copied" = 1 ]; then + # Verify the LOCAL copy, then publish atomically. installed=1 is gated on the CORRECTNESS steps + # only (verify -> chmod -> mv -> publish_symlink[proves exact target] -> re-verify); publishing + # is independent of whether the SD unmounted, since it works entirely on the local copy. + # Durability rides on /usr/data being sync-mounted ubifs; the explicit sync is TO-bounded and + # deliberately NOT part of the success gate. + if verify_ui /usr/data/.mq_ui.tmp \ + && chmod +x /usr/data/.mq_ui.tmp \ + && mv -f /usr/data/.mq_ui.tmp /usr/data/mq_ui \ + && publish_symlink \ + && verify_ui /usr/data/mq_ui; then + installed=1; log "installed verified mq_ui from ${src:-?} (size $MSIZE)" + TO sync 2>/dev/null || log "post-install sync slow/timed-out (ubifs is sync-mounted; already durable)" + else + log "SD copy failed verification/publish -> not installed" + fi +fi +rm -f /usr/data/.mq_ui.tmp 2>/dev/null # always clean the temp, incl. a timed-out/partial copy + +# FINALIZE (fail-closed): if we did not publish this boot, make sure only a manifest-verified UI +# can run. A verified-but-unpublished binary gets its exec bit + symlink repaired; anything that +# does NOT verify is quarantined so the stock UI runs. +if [ "$installed" != 1 ]; then + if verify_ui /usr/data/mq_ui; then + publish_symlink || quarantine "finalize repair failed" || exit 1 + elif [ -e /usr/data/mq_ui ] || [ -e /usr/data/mq_player ]; then + quarantine "unverified override" || exit 1 + fi +fi +disarm # clean end: fail-closed state is settled, disarm so no late signal quarantines it +exit 0 diff --git a/payload/S99usbserial b/payload/S99usbserial new file mode 100755 index 0000000..f926552 --- /dev/null +++ b/payload/S99usbserial @@ -0,0 +1,88 @@ +#!/bin/sh +# diskOS dev/recovery: USB CDC-ACM serial console on the device USB-C port. +# Host enumerates 0525:a4a7 -> /dev/ttyACM0 -> root shell (raw, no login). +# +# Robust against the failure that killed the previous attempt: the stock +# serial_config.sh binds the UDC exactly once and never retries, so a UDC that +# registers (or a host that plugs in) a moment later was missed. This runs a +# persistent supervisor: (re)bind whenever a UDC is present, keep a shell alive +# on ttyGS0. All steps best-effort + logged to a persistent (rw) partition. + +LOG=/usr/data/fiio/usbserial.log +G=/sys/kernel/config/usb_gadget/serial_demo + +log() { echo "$(date 2>/dev/null) $*" >> "$LOG" 2>/dev/null; } + +build_gadget() { + [ -d /sys/kernel/config/usb_gadget ] || mount -t configfs none /sys/kernel/config 2>/dev/null + [ -d "$G" ] && return 0 + mkdir -p "$G" || return 1 + echo 0x0525 > "$G/idVendor" + echo 0xa4a7 > "$G/idProduct" + echo 0x0200 > "$G/bcdUSB" + echo 0x2400 > "$G/bcdDevice" + echo 0x02 > "$G/bDeviceClass" + echo 0x00 > "$G/bDeviceSubClass" + echo 0x00 > "$G/bDeviceProtocol" + mkdir -p "$G/strings/0x409" + echo "INGENIC" > "$G/strings/0x409/manufacturer" + echo "diskOS Serial" > "$G/strings/0x409/product" + echo "diskos-serial" > "$G/strings/0x409/serialnumber" + mkdir -p "$G/configs/c.1/strings/0x409" + echo 120 > "$G/configs/c.1/MaxPower" + echo 0x80 > "$G/configs/c.1/bmAttributes" + echo "serial" > "$G/configs/c.1/strings/0x409/configuration" + mkdir -p "$G/functions/acm.0" + ln -sf "$G/functions/acm.0" "$G/configs/c.1/" 2>/dev/null + log "gadget built" +} + +# Best-effort nudge into peripheral mode. dr_mode=otg already auto-switches to +# device when plugged into a host (CC/pin), so this is only a safety net. Only +# the unambiguous "device" token is written -- never anything that could select +# host mode and break enumeration. +force_device_mode() { + for f in /sys/devices/platform/*otg*/dwc2_mode \ + /sys/devices/platform/*otg*/dwc2_force_dr_mode \ + /sys/devices/platform/*/13500000.otg/dwc2_mode; do + [ -e "$f" ] || continue + echo device > "$f" 2>/dev/null && log "wrote device-mode to $f" + done +} + +bound() { [ -n "$(cat "$G/UDC" 2>/dev/null)" ]; } + +bind_when_ready() { + udc=$(ls /sys/class/udc/ 2>/dev/null | head -n1) + [ -n "$udc" ] || return 1 + echo "$udc" > "$G/UDC" 2>/dev/null && { log "bound UDC=$udc"; return 0; } + return 1 +} + +# Delegate the ttyGS0 shell to diskos-debug (the SINGLE owner - fuser-guarded + self-respawning). +# Two independent shell respawners on one tty is the classic "answers once then goes silent" wedge, +# so this script no longer binds its own shell; it only builds the gadget and asks diskos-debug to +# attach the one shell. Idempotent: serial-on is a no-op if a shell already owns ttyGS0. +DEBUG=/usr/project/diskos-debug.sh +ensure_shell() { + [ -c /dev/ttyGS0 ] || return 0 + [ -x "$DEBUG" ] && "$DEBUG" serial-on >/dev/null 2>&1 +} + +main() { + : > "$LOG" 2>/dev/null + log "S99usbserial start" + build_gadget + force_device_mode + while true; do + bound || bind_when_ready + ensure_shell + sleep 2 + done +} + +case "$1" in + start) main & ;; + *) exit 1 ;; +esac +exit 0 diff --git a/payload/diskos-debug.sh b/payload/diskos-debug.sh new file mode 100755 index 0000000..2f4f530 --- /dev/null +++ b/payload/diskos-debug.sh @@ -0,0 +1,165 @@ +#!/bin/sh +# diskOS debug access. Two independent channels, both OFF unless explicitly enabled: +# - SSH (dropbear over WiFi) with a RANDOM per-enable password. We NEVER use or expose the +# stock root password: the caller (mq_ui) passes a fresh sha512 crypt hash, which we place in a +# private shadow file bind-mounted over /etc/shadow (the on-disk stock /etc/shadow is untouched). +# - SERIAL (a root shell on the USB gadget /dev/ttyGS0). Local USB only; no network exposure. +# State + keys live under /usr/data/sshd (persists across rootfs flashes). Idempotent; every op is +# best-effort so a missing tool or busy resource never wedges the caller. +# +# Usage: +# diskos-debug.sh ssh-on # start dropbear with this password hash +# diskos-debug.sh ssh-off # stop dropbear + drop the shadow overlay +# diskos-debug.sh serial-on # bind ONE root shell to /dev/ttyGS0 +# diskos-debug.sh serial-off # stop the serial shell +# diskos-debug.sh status # print: SSH=on/off SERIAL=on/off + +SELF=/usr/data/sshd +DB="$SELF/dropbearmulti" +KEYS="$SELF/keys" +SHADOW="$SELF/shadow" # our private shadow, bind-mounted over /etc/shadow while SSH is on +SERIALPID="$SELF/serial.pid" # pid of the single ttyGS0 shell supervisor +BUNDLED=/usr/project/dropbearmulti # read-only rootfs copy shipped in the image + +# FAIL-CLOSED test for the private shadow overlay. Returns 0 (present) if it is bind-mounted over +# /etc/shadow OR if we cannot tell (/proc/mounts unreadable); returns 1 (absent) ONLY when a readable +# /proc/mounts definitively shows no such mount. `grep` exit codes: 0=match, 1=no match, >=2=error; +# we must treat "no match" as absent but any error as still-present, so we never report a clean state +# on uncertainty. +overlay_present() { + grep -q ' /etc/shadow ' /proc/mounts 2>/dev/null + _g=$? + [ "$_g" -eq 1 ] && return 1 # readable, definitively no overlay + return 0 # matched (0) or read error (>=2) -> present/unknown, fail closed +} + +ensure_db() { + mkdir -p "$KEYS" 2>/dev/null + # Provision/refresh the dropbear binary from the shipped rootfs copy (survives flashes on /usr/data). + if [ ! -x "$DB" ] && [ -f "$BUNDLED" ]; then cp "$BUNDLED" "$DB" 2>/dev/null; chmod +x "$DB" 2>/dev/null; fi + [ -x "$DB" ] || return 1 + [ -s "$KEYS/ed25519_host_key" ] || "$DB" dropbearkey -t ed25519 -f "$KEYS/ed25519_host_key" >/dev/null 2>&1 + [ -s "$KEYS/rsa_host_key" ] || "$DB" dropbearkey -t rsa -s 2048 -f "$KEYS/rsa_host_key" >/dev/null 2>&1 + return 0 +} + +# FAIL-CLOSED: SSH starts only once the private-password overlay is PROVEN active. Any failure (no +# hash, stuck overlay, empty/garbled shadow, bind failure, dropbear not up) returns non-zero WITHOUT +# leaving dropbear authenticating against the stock /etc/shadow. +ssh_on() { + hash="$1" + [ -n "$hash" ] || { echo "err: no hash"; return 1; } + ensure_db || { echo "err: no dropbear binary"; return 1; } + # STOP any already-running dropbear BEFORE touching the overlay. Otherwise, in the window between + # unmounting the old overlay and binding the new one, a running daemon would authenticate against + # the STOCK /etc/shadow (and any later failure would leave it there). No daemon runs during the swap. + for p in $(pgrep -f "dropbearmulti dropbear" 2>/dev/null); do kill "$p" 2>/dev/null; done + i=0; while pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 && [ "$i" -lt 5 ]; do sleep 1; i=$((i+1)); done + for p in $(pgrep -f "dropbearmulti dropbear" 2>/dev/null); do kill -9 "$p" 2>/dev/null; done + pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 && { echo "err: could not stop existing dropbear"; return 1; } + # Drop any existing overlay and CONFIRM it is gone - reading /etc/shadow while it still aliases + # $SHADOW would let `>` truncate it to empty. If it will not unmount, refuse. + if overlay_present; then + umount /etc/shadow 2>/dev/null + overlay_present && { echo "err: shadow overlay stuck"; return 1; } + fi + [ -f /etc/shadow ] || { echo "err: no /etc/shadow"; return 1; } + # Build the private shadow (root line = our random hash) and PROVE it holds that hash before + # trusting it - never bind an empty or malformed shadow. + awk -v h="$hash" -F: 'BEGIN{OFS=":"} $1=="root"{$2=h} {print}' /etc/shadow > "$SHADOW" 2>/dev/null \ + || { echo "err: shadow gen failed"; return 1; } + chmod 600 "$SHADOW" 2>/dev/null + awk -F: -v h="$hash" '$1=="root" && $2==h{ok=1} END{exit ok?0:1}' "$SHADOW" 2>/dev/null \ + || { echo "err: shadow bad"; rm -f "$SHADOW"; return 1; } + mount -o bind "$SHADOW" /etc/shadow 2>/dev/null || mount --bind "$SHADOW" /etc/shadow 2>/dev/null + # CONFIRM the overlay is live BEFORE starting SSH; if the bind failed, dropbear would authenticate + # against the STOCK /etc/shadow (stock root password reachable over the network) - refuse. + grep -q ' /etc/shadow ' /proc/mounts 2>/dev/null || { echo "err: overlay failed, refusing SSH"; return 1; } + pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 || \ + "$DB" dropbear -p 22 -r "$KEYS/ed25519_host_key" -r "$KEYS/rsa_host_key" >/dev/null 2>&1 + if ! pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1; then + # dropbear failed to start: tear the overlay back down and CONFIRM it is gone (retry once). + # Report which state we ended in so the caller never assumes a clean OFF while it is still mounted. + umount /etc/shadow 2>/dev/null + overlay_present && { sleep 1; umount /etc/shadow 2>/dev/null; } + if overlay_present; then + echo "err: dropbear did not start AND overlay stuck"; return 2 + fi + echo "err: dropbear did not start"; return 1 + fi + echo on +} + +# FAIL-CLOSED: kill dropbear and CONFIRM it is gone BEFORE dropping the overlay (a surviving process +# would otherwise fall back to the stock /etc/shadow once the bind is removed). Report the REAL state. +ssh_off() { + for p in $(pgrep -f "dropbearmulti dropbear" 2>/dev/null); do kill "$p" 2>/dev/null; done + i=0; while pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 && [ "$i" -lt 4 ]; do sleep 1; i=$((i+1)); done + for p in $(pgrep -f "dropbearmulti dropbear" 2>/dev/null); do kill -9 "$p" 2>/dev/null; done + # brief settle, then drop the overlay. Whole path stays under ~6s so the UI's bounded call + # (12s, see debug_ui.c disable_dbg) always lets us finish the umount before it can kill us - + # otherwise the UI could report OFF while the overlay is still mounted. + i=0; while pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 && [ "$i" -lt 2 ]; do sleep 1; i=$((i+1)); done + # NEVER drop the overlay while a daemon might still be alive: if a process somehow survived SIGKILL, + # unmounting would drop it back onto the STOCK /etc/shadow. Keep the overlay (so it stays bound to + # OUR shadow) and fail instead. Only umount once dropbear is CONFIRMED gone. + if pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1; then + echo "err: dropbear still running - overlay kept, refusing to expose stock shadow"; return 1 + fi + overlay_present && umount /etc/shadow 2>/dev/null + if overlay_present; then + echo "err: overlay stuck"; return 1 + fi + echo off +} + +# True only if $SERIALPID names a LIVE process that is actually OUR supervisor. A bare `kill -0` would +# be fooled by PID reuse (some unrelated process now holding that number) - so we also confirm the +# process's cmdline still contains the ttyGS0 supervisor marker before trusting/signalling it. +serial_alive() { + [ -f "$SERIALPID" ] || return 1 + _sp=$(cat "$SERIALPID" 2>/dev/null); [ -n "$_sp" ] || return 1 + kill -0 "$_sp" 2>/dev/null || return 1 + tr '\0' ' ' < "/proc/$_sp/cmdline" 2>/dev/null | grep -q 'ttyGS0' +} + +serial_on() { + [ -c /dev/ttyGS0 ] || { echo "no ttyGS0"; return 1; } + # Exactly ONE supervisor owns ttyGS0. A plain `fuser` guard is NOT enough: between shell respawns + # the supervisor sleeps 1s without holding the tty, so a repeated serial-on (e.g. S99's loop) would + # see no owner and start a SECOND supervisor = the two-shell wedge. Guard on the SUPERVISOR pid + # (which stays alive across that gap, validated as really ours) via a pidfile instead. + if serial_alive; then echo on; return 0; fi + rm -f "$SERIALPID" 2>/dev/null # stale/reused pid - clear it so a real supervisor can start + setsid sh -c 'echo $$ > '"$SERIALPID"'; while true; do /bin/sh /dev/ttyGS0 2>&1; sleep 1; done' /dev/null 2>&1 & + # Confirm the supervisor actually came up (setsid/fork/pidfile-write can all fail) before claiming + # success - otherwise the caller would show "serial on" with no shell behind it. Bounded to ~2s + # (portable whole-second sleep) so it stays well under the UI's serial-on timeout. + serial_alive || sleep 1 + serial_alive || sleep 1 + if serial_alive; then echo on; return 0; fi + echo "err: serial supervisor did not start"; return 1 +} + +serial_off() { + # Only signal the pidfile's process if it is verifiably OUR supervisor (never a reused PID). + if serial_alive; then kill "$(cat "$SERIALPID" 2>/dev/null)" 2>/dev/null; fi + rm -f "$SERIALPID" 2>/dev/null + fuser -k /dev/ttyGS0 2>/dev/null + echo off +} + +status() { + s=off; pgrep -f "dropbearmulti dropbear" >/dev/null 2>&1 && s=on + r=off; serial_alive && r=on + echo "SSH=$s SERIAL=$r" +} + +case "$1" in + ssh-on) ssh_on "$2" ;; + ssh-off) ssh_off ;; + serial-on) serial_on ;; + serial-off) serial_off ;; + status) status ;; + *) echo "usage: $0 {ssh-on |ssh-off|serial-on|serial-off|status}"; exit 1 ;; +esac diff --git a/payload/dropbearmulti b/payload/dropbearmulti new file mode 100755 index 0000000..df1557b Binary files /dev/null and b/payload/dropbearmulti differ diff --git a/payload/mq_ui b/payload/mq_ui new file mode 100755 index 0000000..ba5c92b Binary files /dev/null and b/payload/mq_ui differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8f2858c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# Python runtime dependencies for the diskOS installer. +# install.sh pip-installs these into a local .venv. Versions are pinned so the +# vendored license texts in licenses/ match what actually gets installed. +pyusb==1.3.1 # mask-ROM USB device detection (BSD-3-Clause) +pycryptodome==3.23.0 # AES-decrypt of FiiO OTA chunks (BSD-2-Clause + public-domain) diff --git a/spl-src/patches/0001-baseline-burner_x2000-SPL-that-loads-executes-on-dev.patch b/spl-src/patches/0001-baseline-burner_x2000-SPL-that-loads-executes-on-dev.patch new file mode 100644 index 0000000..8e49096 --- /dev/null +++ b/spl-src/patches/0001-baseline-burner_x2000-SPL-that-loads-executes-on-dev.patch @@ -0,0 +1,120 @@ +From 08f128d82f8e86dac5ca277d4fe4cc3fd7fef204 Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Mon, 24 Aug 2026 22:51:34 +1000 +Subject: [PATCH 1/8] baseline: burner_x2000 SPL that loads+executes on device + (-fcommon + mask-ROM return shim) + +--- + arch/mips/cpu/xburst2/x2000/start.S | 23 +++++++++++++++++++++++ + config.mk | 2 +- + tools/ingenic-tools/mmc_params | Bin 0 -> 14472 bytes + 3 files changed, 24 insertions(+), 1 deletion(-) + create mode 100755 tools/ingenic-tools/mmc_params + +diff --git a/arch/mips/cpu/xburst2/x2000/start.S b/arch/mips/cpu/xburst2/x2000/start.S +index caab455..2dcc1e2 100755 +--- a/arch/mips/cpu/xburst2/x2000/start.S ++++ b/arch/mips/cpu/xburst2/x2000/start.S +@@ -167,6 +167,29 @@ cache_alloc_a_line: + li sp, 0x80001000 + #endif + ++#ifdef CONFIG_BURNER ++ /* ++ * DDR-only USB stage-1 (burner mode): board_init_f() brings up clocks and ++ * SDRAM and then RETURNS (see arch/mips/cpu/xburst2/x2000/soc.c, the ++ * CONFIG_BURNER branch). For usbboot to keep driving the USB session we ++ * must hand control back to the mask ROM afterwards. Preserve the mask-ROM ++ * return address (and sp) in a fixed TCSM scratch slot across the call -- ++ * board_init_f repoints/uses the stack internally, so a stack-relative save ++ * is not reliable for the outer return. 0xb24017f0 lies in the param/pad ++ * region below the code (offset 0x800) and is untouched by DDR init. ++ */ ++ li t0, 0xb24017f0 ++ sw ra, 0(t0) ++ sw sp, 4(t0) ++ jal board_init_f ++ nop ++ li t0, 0xb24017f0 ++ lw ra, 0(t0) ++ lw sp, 4(t0) ++ jr ra ++ nop ++#else + j board_init_f + nop ++#endif + +diff --git a/config.mk b/config.mk +index f71e145..d78be30 100755 +--- a/config.mk ++++ b/config.mk +@@ -195,7 +195,7 @@ endif + ARFLAGS = $(error update your Makefile to use cmd_link_o_target and not AR) + RELFLAGS= $(PLATFORM_RELFLAGS) + DBGFLAGS= -g # -DDEBUG +-OPTFLAGS= -Os #-fomit-frame-pointer ++OPTFLAGS= -Os -fcommon #-fomit-frame-pointer (-fcommon: 2013 tree relies on tentative defs; GCC>=10 defaults -fno-common) + + OBJCFLAGS += --gap-fill=0xff + +diff --git a/tools/ingenic-tools/mmc_params b/tools/ingenic-tools/mmc_params +new file mode 100755 +index 0000000000000000000000000000000000000000..78adebd875be1ac2ec4d41c346622d76fea5e1c4 +GIT binary patch +literal 14472 +zcmeHOZ)_Y_5r1bp^~H70ra0gvZM_P1)lhu7a~#V_Wa58G)>Rwa)&?pXuIsx!`|$l^ +zZ%v%4#MhV%|uTKe1!o{@(m%=FQu=x9@iEo+l36bFj?k6D;N8lLEEbTO`v-1~p6#fKJgaHo@^W +zajUoq^a`7k_D%_4m9kK_8vVqV17ch~HrBvfB@G#*@CXqxZapbhN(-Z?l{&_4z$WQ# +zEYLU@*z4u|9>d#2!6+4rOp|;kY$yjqTkscdw9&c}JZfgzZGQk#m2skbUUO1-?9Uc_W +zO%XXNqbSQuo%=&u9of_T;(D?J40LI)a``%F$|^=G{QztEWB#@HPp`w@y$;{74u2MS +z1zv8!oNMK6T!%joyaF$`>;q!En?yYl$(dSoG!h*djl{J?5{RskNSkqFgrRFOk;&?5 +zpkanY(b+H~DJ_9abX<$X6KO4(ctjV`WG1JJqejBiMKY)BqXGkP2DOPp_w;r5L|THo +zf~}?YN~I2I|aQb}Rm|GGXadyXvt4ky{;R<7rKDHI@<>FD&pMByRFdfUp +z6zC|t-tjt<{jlU`X+2{;M7+OK+L>p4VZY>YuH&3zx#HlR*Tn;od8~U +z@OWLZ%n^?tCtM@6op$hey|SEh@b>2n8Ri|lyk4cD=-}xi%QpPf!9#&d@`{76DzRR} +ztAJMluL52Lyb5>~@G9`HSKtpdw|}5c{?@NfZ}`BSWtZ=jZ*S<&&vY3W-lI^HlXeQia@ +z8>OWaCEaQ|4B6u~4ZXt7Z#DfUxLSYRrT@~UzwFX4y7cod{kc_o*Faz4-N6T-OiE0h +zu4uXwhNl*K8|%ZxpY%4`cg&Mknc1dJf8>XF>gvqyGXWtM=im(Hu{P@TKx2JOy{pa+ +ztvR_7NGP%Lxg%WE(y@s^(;I>@DL}_^>sKb?tufmxnF^#^8F3J-B-BudA0DSI{EA6{(%GG +zV)!L>rsIp?XKnr%d@=HFJjnjQ_W`5MZ0JVVbKTqwQ~4%r!&KgAU8!lvW4{*&!GDYG +zzxx)19XHg%viicc{pt(X%T?b+_0kQq4mx=46S4!pbw$oQj>q|)??AH{yK7LL?05#7 +zYT>F`txk752g+jKU*V);FxHD3z5}}N2*j51WBk%lFhUfz?Q5@ewE^jgR{^gAUIn}g +zcopy};8nn@fL8&p0$v6FM-}kFs6#UkOmIH>7 +zF{(r=6;*QCq!P`HrA?(nX$rNEE8WLTJ-41MnTd{qv1de&j_y@HgFlBk`V+ow<@@lr +zUi=DR?st}j-z_N1mNg3)0z8d8U=gtP_m0Ri#+Zb +zn19Ez?m&m$O8Wu55t^7M1Ud46B2arUP=9yL%}4!tv488HkMC;Sjx@&O?_g8lX9pq) +zg1iZ@Jn&g0NJ4<8;I#n!Fh1)a4Ah<~>kZUDUfvZ@CM$Xaon?os0!mk)zAI4M9q@NK +ze|@4(Jn$;uRluu&R{^gAUIn}gcopy};8o!Nssg+}j`zdiqXY^*i=b3XC~eT&MEaw& +ze{Kuuyzg!s>3+hkguMR_pOH}5{>K|u2K95a*^T$G)l!4^t}T$A_n<9c5C}ij)Js)t +zCS;&6F-7~>@aIkxe7-}$=PMN63-?i}ih3fL|11rJ$6X{p-YduXZzrDp{%pzfgSy%j +z{FZ2+ub=vZU*v3^>kf~cw!TQ`|32w_9@PDR4S0XIyTcGYgmx146Fy9sCCn4f61w~O +zM>_AfJk-;(SGjX=2p;dpl<+Rtp&Dv#8?)7x<1MYhP-`%}gA76hb0ay^Fts5Oge|hV +zkrlyo#?*tu>9JtefPJ!tdCWl#jU`}1bs|PoS9f2tsSOL;eMHNRh+ynkI(IB3(KMvx +zOS+LuWYVjY2-plgsbK)MvPlz1k$@vKgTonWp+=f(IDgm9z@S$8z1f!W$N>Ae#1OK7m^@DSduM7A8Mey~? +zX?UKpj>`aEBOJfp{(tu8>wvlaXLfW9e)V8 +z_uv}i@$)*w$oY94a*zK$h{qaoJg-xX?Nl%vBQ`mn*NX>%!y$7#uX~KV&Vh?9?)q!M +z;u^y7ylyb^`pe^IKgKk~=lF>hUxo(Ol;e4wWGs>+=Xam~S1G=m +z{CM4EJne|bnz`ej01n5+{g*b9(|W|8N7T*Vz5n +Date: Mon, 24 Aug 2026 22:55:29 +1000 +Subject: [PATCH 2/8] =?UTF-8?q?ddr:=20add=20LPDDR3=20support=20(mr11=20fie?= + =?UTF-8?q?ld,=20LPDDR3=20MR=20sequence,=20W63AH6NKB=20type-force)=20?= + =?UTF-8?q?=E2=80=94=20ported=20from=20tobunto=20X2000?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 20 ++++++++++++++++++++ + arch/mips/cpu/xburst2/ddr_reg_data.h | 2 ++ + 2 files changed, 22 insertions(+) + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index 5257e3a..e08a5df 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -454,6 +454,21 @@ static void ddrc_dfi_init(enum ddr_type type, int bypass) + ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); //MR2 + ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); //MR3 + ddr_writel(DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_ZQCL_CS0, DDRC_LMR); //ZQCL ++#undef DDRC_LMR_MR ++ break; ++ case LPDDR3: ++ /* LPDDR3 MR sequence - ported from tobunto/u-boot X2000 (matches the vendor ++ * disc_spl.bin trace): MR63,10,1,2,3,11 each followed by mdelay(1); no ZQ. */ ++#define DDRC_LMR_MR(n) \ ++ DDRC_DLMR_VALUE | DDRC_LMR_START | DDRC_LMR_CMD_LMR | \ ++ ((DDR_MR##n##_VALUE & 0xff) << 24) | \ ++ (((DDR_MR##n##_VALUE >> 8) & 0xff) << (16)) ++ ddr_writel(DDRC_LMR_MR(63), DDRC_LMR); mdelay(1); //set MRS reset ++ ddr_writel(DDRC_LMR_MR(10), DDRC_LMR); mdelay(1); //set IO calibration ++ ddr_writel(DDRC_LMR_MR(1), DDRC_LMR); mdelay(1); //set MR1 ++ ddr_writel(DDRC_LMR_MR(2), DDRC_LMR); mdelay(1); //set MR2 ++ ddr_writel(DDRC_LMR_MR(3), DDRC_LMR); mdelay(1); //set MR3 ++ ddr_writel(DDRC_LMR_MR(11), DDRC_LMR); mdelay(1); //set MR11 + #undef DDRC_LMR_MR + break; + default: +@@ -556,6 +571,11 @@ void sdram_init(void) + soc_ddr_init(); + + type = get_ddr_type(); ++ /* Snowsky Disc = Winbond W63AH6NKB-BI LPDDR3. The controller CFG.TYPE field reads as ++ * LPDDR2(5) for this part (the vendor SPL dispatches on the logical record type=3, not ++ * CFG.TYPE), so force the LPDDR3 MR sequence here. Only ddrc_dfi_init consumes `type` ++ * (the X2000 prev_ddr_init hook is a no-op). */ ++ type = LPDDR3; + clk_set_rate(DDR, gd->arch.gi->ddrfreq); + if(ddr_hook && ddr_hook->prev_ddr_init) + ddr_hook->prev_ddr_init(type); +diff --git a/arch/mips/cpu/xburst2/ddr_reg_data.h b/arch/mips/cpu/xburst2/ddr_reg_data.h +index 4878267..b48575f 100755 +--- a/arch/mips/cpu/xburst2/ddr_reg_data.h ++++ b/arch/mips/cpu/xburst2/ddr_reg_data.h +@@ -27,6 +27,7 @@ struct ddr_registers + uint32_t ddr_mr2; + uint32_t ddr_mr3; + uint32_t ddr_mr10; ++ uint32_t ddr_mr11; + uint32_t ddr_mr63; + uint32_t ddr_chip0_size; + uint32_t ddr_chip1_size; +@@ -59,6 +60,7 @@ extern struct ddr_registers *g_ddr_param; + #define DDR_MR2_VALUE g_ddr_param->ddr_mr2 + #define DDR_MR3_VALUE g_ddr_param->ddr_mr3 + #define DDR_MR10_VALUE g_ddr_param->ddr_mr10 ++#define DDR_MR11_VALUE g_ddr_param->ddr_mr11 + #define DDR_MR63_VALUE g_ddr_param->ddr_mr63 + #define DDR_CHIP_0_SIZE g_ddr_param->ddr_chip0_size + #define DDR_CHIP_1_SIZE g_ddr_param->ddr_chip1_size +-- +2.43.0 + diff --git a/spl-src/patches/0003-ddr-instrument-DDR-bring-up-with-TCSM-breadcrumbs-bo.patch b/spl-src/patches/0003-ddr-instrument-DDR-bring-up-with-TCSM-breadcrumbs-bo.patch new file mode 100644 index 0000000..e1b6bf4 --- /dev/null +++ b/spl-src/patches/0003-ddr-instrument-DDR-bring-up-with-TCSM-breadcrumbs-bo.patch @@ -0,0 +1,149 @@ +From 15c4698350fdd6ef22a15ce8daaab4b332f9762a Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Mon, 24 Aug 2026 23:17:06 +1000 +Subject: [PATCH 3/8] ddr: instrument DDR bring-up with TCSM breadcrumbs + + bounded polls (diagnostic build) + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 45 +++++++++++++++++++++++------ + 1 file changed, 36 insertions(+), 9 deletions(-) + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index e08a5df..91cf6a3 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -46,6 +46,23 @@ + #include + #include + #define CONFIG_DWC_DEBUG 0 ++ ++/* ---- DDR bring-up diagnostics (breadcrumbs to TCSM pad @0xb24017c0, below the ++ * start.S return-shim scratch at 0x17f0). usbboot uploads this region after the SPL ++ * returns to the mask ROM, so ONE device cycle localises any DDR-init stall. ++ * Layout: [0]=magic [1]=last stage reached [2]=first fail id [3]=status@fail [4]=fail count */ ++#define DDR_DIAG ((volatile unsigned int *)0xb24017c0) ++#define DDR_DIAG_MAGIC 0xD1A6C0DEu ++static inline void ddr_diag_init(void){ DDR_DIAG[0]=DDR_DIAG_MAGIC; DDR_DIAG[1]=0; DDR_DIAG[2]=0; DDR_DIAG[3]=0; DDR_DIAG[4]=0; __asm__ __volatile__("sync"); } ++static inline void ddr_diag_stage(unsigned int s){ DDR_DIAG[1]=s; __asm__ __volatile__("sync"); } ++static inline void ddr_diag_fail(unsigned int id, unsigned int status){ if(DDR_DIAG[2]==0){ DDR_DIAG[2]=id; DDR_DIAG[3]=status; } DDR_DIAG[4]++; __asm__ __volatile__("sync"); } ++/* bounded busy-wait: wait for (cond); on ~1e6-iter timeout record (id,statusexpr) and stop waiting ++ * (never spins forever, so the SPL always returns and the breadcrumb is readable). */ ++#define DDR_BOUND(cond, id, statusexpr) do { \ ++ unsigned int _bt = 1000000u; \ ++ while(!(cond)) { if(!--_bt){ ddr_diag_fail((id), (unsigned int)(statusexpr)); break; } } \ ++ } while(0) ++ + #define ddr_hang() do{ \ + printf("%s %d\n",__FUNCTION__,__LINE__); \ + hang(); \ +@@ -250,7 +267,7 @@ static void ddrp_pll_init(void) + ddr_writel(0, DDRP_INNOPHY_PLL_CTRL); + #endif + +- while(!(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & 1 << 3)); ++ DDR_BOUND(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & (1 << 3), 10 /*PLL_LOCK*/, ddr_readl(DDRP_INNOPHY_PLL_LOCK)); + } + + static void ddrp_register_cfg(void) +@@ -311,14 +328,15 @@ static void ddrp_hardware_calibration(void) + unsigned int timeout = 1000000; + /* ddr_writel(ddr_readl(DDRP_INNOPHY_TRAINING_CTRL) | 1, DDRP_INNOPHY_TRAINING_CTRL); */ + ddr_writel(1, DDRP_INNOPHY_TRAINING_CTRL); ++ /* bounded (fixes the original timeout-- unsigned wrap); diagnostic, no hang() */ + do + { + val = ddr_readl(DDRP_INNOPHY_CALIB_DONE); +- } while (((val & 0xf) != 0x3) && timeout--); ++ } while (((val & 0xf) != 0x3) && --timeout); + +- if(!timeout) { +- printf("timeout:INNOPHY_CALIB_DONE %x\n", ddr_readl(DDRP_INNOPHY_CALIB_DONE)); +- hang(); ++ if(((val & 0xf) != 0x3)) { ++ ddr_diag_fail(30 /*CALIB_DONE*/, val); ++ ddr_writel(0, DDRP_INNOPHY_TRAINING_CTRL); /* clear training on failure (Codex) */ + } + + ddr_writel(0, DDRP_INNOPHY_TRAINING_CTRL); +@@ -424,7 +442,7 @@ static void ddrc_dfi_init(enum ddr_type type, int bypass) + FUNC_ENTER(); + ddr_writel(DDRC_DWCFG_DFI_INIT_START, DDRC_DWCFG); // dfi_init_start high + ddr_writel(0, DDRC_DWCFG); // set buswidth 16bit +- while(!(ddr_readl(DDRC_DWSTATUS) & DDRC_DWSTATUS_DFI_INIT_COMP)); //polling dfi_init_complete ++ DDR_BOUND(ddr_readl(DDRC_DWSTATUS) & DDRC_DWSTATUS_DFI_INIT_COMP, 20 /*DFI_INIT_COMP*/, ddr_readl(DDRC_DWSTATUS)); //polling dfi_init_complete + + ddr_writel(0, DDRC_CTRL); //set dfi_reset_n high + ddr_writel(DDRC_CFG_VALUE, DDRC_CFG); +@@ -496,8 +514,7 @@ static void ddr_calibration(struct ddr_calib_value *dcv, int div) + val = REG32(CPM_DDRCDR); + val |= ((1 << 29) | (1 << 25)); + REG32(CPM_DDRCDR) = val; +- while((REG32(CPM_DDRCDR) & (1 << 24))) +- ; ++ DDR_BOUND(!(REG32(CPM_DDRCDR) & (1 << 24)), 40 /*DDRCDR_BUSY*/, REG32(CPM_DDRCDR)); + /* // Set clock divider */ + val = REG32(CPM_DDRCDR); + val &= ~(0xf); +@@ -505,7 +522,7 @@ static void ddr_calibration(struct ddr_calib_value *dcv, int div) + REG32(CPM_DDRCDR) = val; + + // Polling PHY_FREQ_DONE +- while(((ddr_readl(DDRC_DWSTATUS) & (1 << 3 | 1 << 1)) & 0xf) != 0xa); ++ DDR_BOUND(((ddr_readl(DDRC_DWSTATUS) & (1 << 3 | 1 << 1)) & 0xf) == 0xa, 50 /*PHY_FREQ_DONE*/, ddr_readl(DDRC_DWSTATUS)); + ddrp_hardware_calibration(); + /* ddrp_software_calibration(); */ + +@@ -567,6 +584,8 @@ void sdram_init(void) + int bypass = 0; + + debug("sdram init start\n"); ++ ddr_diag_init(); /* breadcrumb record @0xb24017c0 */ ++ ddr_diag_stage(1); /* entered sdram_init */ + + soc_ddr_init(); + +@@ -582,26 +601,33 @@ void sdram_init(void) + rate = clk_get_rate(DDR); + debug("DDR clk rate %d\n", rate); + ++ ddr_diag_stage(2); + ddrc_reset_phy(); + ++ ddr_diag_stage(3); + ddr_phy_init(); + dump_ddrp_register(); + ++ ddr_diag_stage(4); + ddrc_dfi_init(type, bypass); + ++ ddr_diag_stage(5); + ddrc_prev_init(); + + /** + * bypass = 1 or calibration = 0 + */ + bypass = 0; ++ ddr_diag_stage(6); + ddrp_calibration(bypass); + ++ ddr_diag_stage(7); + ddrc_post_init(); + + if(ddr_hook && ddr_hook->post_ddr_init) + ddr_hook->post_ddr_init(type); + ++ ddr_diag_stage(8); + get_dynamic_calib_value(rate); + + if(DDRC_AUTOSR_EN_VALUE) { +@@ -616,6 +642,7 @@ void sdram_init(void) + + dump_ddrc_register(); + /* DDRC address remap configure*/ ++ ddr_diag_stage(9); /* 9 = sdram_init completed (success if no fail id recorded) */ + debug("sdram init finished\n"); + } + +-- +2.43.0 + diff --git a/spl-src/patches/0004-ddr-bound-clk.c-DDR-clock-polls-constrain-dynamic-ca.patch b/spl-src/patches/0004-ddr-bound-clk.c-DDR-clock-polls-constrain-dynamic-ca.patch new file mode 100644 index 0000000..fb49cc9 --- /dev/null +++ b/spl-src/patches/0004-ddr-bound-clk.c-DDR-clock-polls-constrain-dynamic-ca.patch @@ -0,0 +1,144 @@ +From eb05eaef0f7dac0fb1f166f88d69e1b895eb2e1a Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Mon, 24 Aug 2026 23:46:16 +1000 +Subject: [PATCH 4/8] ddr: bound clk.c DDR-clock polls + constrain + dynamic-calib div; board_init_f breadcrumbs + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 1 + + arch/mips/cpu/xburst2/x2000/clk.c | 6 +++--- + arch/mips/cpu/xburst2/x2000/soc.c | 22 +++++++++++++++++++++ + arch/mips/include/asm/arch-x2000/ddr_diag.h | 17 ++++++++++++++++ + 4 files changed, 43 insertions(+), 3 deletions(-) + create mode 100644 arch/mips/include/asm/arch-x2000/ddr_diag.h + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index 91cf6a3..c9b19f8 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -563,6 +563,7 @@ static void get_dynamic_calib_value(unsigned int rate) + ddr_calibration(&dcv[cur_div], cur_div); + break; + } ++ if(div > 0xf) { ddr_diag_fail(60 /*calib div overflow - bad DDR rate*/, (unsigned int)rate); break; } + dcv[div].rate = drate; + dcv[div].refcnt = get_refcnt_value(div); + ddr_calibration(&dcv[div], div); +diff --git a/arch/mips/cpu/xburst2/x2000/clk.c b/arch/mips/cpu/xburst2/x2000/clk.c +index c700a03..30764b6 100755 +--- a/arch/mips/cpu/xburst2/x2000/clk.c ++++ b/arch/mips/cpu/xburst2/x2000/clk.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + + DECLARE_GLOBAL_DATA_PTR; +@@ -68,7 +69,7 @@ void clk_prepare(void) + /*set div max*/ + regval |= cgusetting[i].val; + writel(regval, reg); +- while (readl(reg) & (1 << cgusetting[i].busy)); ++ DDR_BOUND(!(readl(reg) & (1 << cgusetting[i].busy)), 4 /*cgu init busy*/, readl(reg)); + } else { + regval &= ~(1 << cgusetting[i].ce); + writel(regval, reg); +@@ -244,8 +245,7 @@ void clk_set_rate(int clk_id, unsigned long rate) + regval &= ~(3 << cgu->stop | 0xff); + regval |= ((1 << cgu->ce) | cdr); + writel(regval, reg); +- while (readl(reg) & (1 << cgu->busy)) +- ; ++ DDR_BOUND(!(readl(reg) & (1 << cgu->busy)), 5 /*DDR clk busy (clk_set_rate)*/, readl(reg)); + #ifdef DUMP_CGU_SELECT + printf("%s(0x%x) :0x%x\n",clk_name[clk_id] ,reg, readl(reg)); + #endif +diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c +index 54ff3b7..6b96c5b 100755 +--- a/arch/mips/cpu/xburst2/x2000/soc.c ++++ b/arch/mips/cpu/xburst2/x2000/soc.c +@@ -67,11 +67,23 @@ extern void ddr_test_refresh(unsigned int start_addr, unsigned int end_addr); + extern void flush_cache_all(void); + + ++/* DIAG: breadcrumb record @0xb24017c0 (shared with ddr_innophy.c). Set 1 to early-return ++ * before sdram_init to prove the mask-ROM return path works in isolation. */ ++#define DIAG_SKIP_SDRAM 0 ++#define BIF_CRUMB(s) do { *(volatile unsigned int*)0xb24017c4 = (s); __asm__ __volatile__("sync"); } while(0) ++ + void board_init_f(ulong dummy) + { + /* Set global data pointer */ + gd = &gdata; + ++ /* DIAG: mark that we reached board_init_f (magic + stage 0xF0) */ ++ *(volatile unsigned int*)0xb24017c0 = 0xD1A6C0DEu; ++ *(volatile unsigned int*)0xb24017c8 = 0; ++ *(volatile unsigned int*)0xb24017cc = 0; ++ *(volatile unsigned int*)0xb24017d0 = 0; ++ BIF_CRUMB(0xF0); ++ + /* Setup global info */ + #ifndef CONFIG_BURNER + gd->arch.gi = &ginfo; +@@ -80,6 +92,7 @@ void board_init_f(ulong dummy) + #endif + + gpio_init(); ++ BIF_CRUMB(0xF1); + + *(volatile unsigned int *)0xb0000020 = 0; //clk gate enable. + *(volatile unsigned int *)0xb0000028 = 0; //clk gate enable. +@@ -93,15 +106,24 @@ void board_init_f(ulong dummy) + #endif + debug("Timer init\n"); + timer_init(); ++ BIF_CRUMB(0xF2); + + debug("CLK stop\n"); + // clk_prepare(); + + debug("PLL init\n"); + pll_init(); ++ BIF_CRUMB(0xF3); + + debug("CLK init\n"); + clk_init(); ++ BIF_CRUMB(0xF4); ++ ++#if DIAG_SKIP_SDRAM ++ /* DIAG: return before DDR to test the mask-ROM return path in isolation. */ ++ BIF_CRUMB(0xFE); ++ return; ++#endif + + debug("SDRAM init\n"); + sdram_init(); +diff --git a/arch/mips/include/asm/arch-x2000/ddr_diag.h b/arch/mips/include/asm/arch-x2000/ddr_diag.h +new file mode 100644 +index 0000000..68654ec +--- /dev/null ++++ b/arch/mips/include/asm/arch-x2000/ddr_diag.h +@@ -0,0 +1,17 @@ ++#ifndef _ASM_ARCH_DDR_DIAG_H ++#define _ASM_ARCH_DDR_DIAG_H ++/* Shared DDR bring-up breadcrumb record @0xb24017c0 (TCSM pad below the start.S return ++ * shim scratch at 0x17f0). usbboot uploads it after the SPL returns to the mask ROM. ++ * Layout: [0]=magic [1]=stage [2]=first fail id [3]=status@fail [4]=fail count */ ++#define DDR_DIAG ((volatile unsigned int *)0xb24017c0) ++#define DDR_DIAG_MAGIC 0xD1A6C0DEu ++#define ddr_diag_fail_raw(id, status) do { \ ++ if(DDR_DIAG[2]==0){ DDR_DIAG[2]=(unsigned int)(id); DDR_DIAG[3]=(unsigned int)(status); } \ ++ DDR_DIAG[4]++; __asm__ __volatile__("sync"); \ ++ } while(0) ++/* bounded busy-wait: wait for (cond); on ~1e6-iter timeout record (id,statusexpr) and stop. */ ++#define DDR_BOUND(cond, id, statusexpr) do { \ ++ unsigned int _bt = 1000000u; \ ++ while(!(cond)) { if(!--_bt){ ddr_diag_fail_raw((id), (statusexpr)); break; } } \ ++ } while(0) ++#endif +-- +2.43.0 + diff --git a/spl-src/patches/0005-ddr-disable-verbose-DEBUG-DWC_DEBUG-printf-dumps-sli.patch b/spl-src/patches/0005-ddr-disable-verbose-DEBUG-DWC_DEBUG-printf-dumps-sli.patch new file mode 100644 index 0000000..a148f36 --- /dev/null +++ b/spl-src/patches/0005-ddr-disable-verbose-DEBUG-DWC_DEBUG-printf-dumps-sli.patch @@ -0,0 +1,35 @@ +From f51bc90392b4c0dc42456d876a37dc05926db445 Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Mon, 24 Aug 2026 23:48:18 +1000 +Subject: [PATCH 5/8] ddr: disable verbose DEBUG/DWC_DEBUG printf dumps (slim + SPL, no UART-wait in DDR path) + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index c9b19f8..b394dad 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -21,7 +21,7 @@ + * MA 02111-1307 USA + */ + +-#define DEBUG ++/* #define DEBUG */ /* off: shrink SPL + remove printf UART-waits from DDR path (breadcrumbs replace it) */ + /* #define DEBUG_READ_WRITE */ + #include + #include +@@ -45,7 +45,7 @@ + #endif + #include + #include +-#define CONFIG_DWC_DEBUG 0 ++/* #define CONFIG_DWC_DEBUG 0 */ /* off: use the empty dump_ddr*_register() no-ops (shrinks SPL) */ + + /* ---- DDR bring-up diagnostics (breadcrumbs to TCSM pad @0xb24017c0, below the + * start.S return-shim scratch at 0x17f0). usbboot uploads this region after the SPL +-- +2.43.0 + diff --git a/spl-src/patches/0006-ddr-fix-PHY-PLL-divisors-20-5-8-4-for-W63AH6NKB-LPDD.patch b/spl-src/patches/0006-ddr-fix-PHY-PLL-divisors-20-5-8-4-for-W63AH6NKB-LPDD.patch new file mode 100644 index 0000000..cf92490 --- /dev/null +++ b/spl-src/patches/0006-ddr-fix-PHY-PLL-divisors-20-5-8-4-for-W63AH6NKB-LPDD.patch @@ -0,0 +1,49 @@ +From 26f15d13ea9b7e92eaf2326909b202799797f440 Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Mon, 24 Aug 2026 23:52:48 +1000 +Subject: [PATCH 6/8] ddr: fix PHY PLL divisors 20/5 -> 8/4 for W63AH6NKB + LPDDR3 400MHz (calibration root cause) + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index b394dad..4c7b921 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -247,25 +247,22 @@ static void ddrp_pll_init(void) + { + unsigned int val; + ++ /* PHY PLL divisors for W63AH6NKB LPDDR3 @400MHz: FBDIV=8, PDIV=4 (from tobunto X2000, ++ * matches the vendor SPL). The tree's stock 20/5 sets the wrong PHY clock -> training ++ * (CALIB_DONE) never converges. */ + val = ddr_readl(DDRP_INNOPHY_PLL_FBDIV); + val &= ~(0xff); +- val |= 0x14; ++ val |= 0x8; + ddr_writel(val, DDRP_INNOPHY_PLL_FBDIV); + + val = ddr_readl(DDRP_INNOPHY_PLL_PDIV); + val &= ~(0xff); +- val |= 0x5; ++ val |= 4; + ddr_writel(val, DDRP_INNOPHY_PLL_PDIV); + +- /* ddr_writel(0x14, DDRP_INNOPHY_PLL_FBDIV); */ +- /* ddr_writel(0x5, DDRP_INNOPHY_PLL_PDIV); */ +-#ifdef DEBUG_READ_WRITE ++ /* toggle PLLPDEN via RMW (preserve other PLL_CTRL bits), as tobunto/vendor do */ + ddr_writel(ddr_readl(DDRP_INNOPHY_PLL_CTRL) | DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL); + ddr_writel(ddr_readl(DDRP_INNOPHY_PLL_CTRL) & ~DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL); +-#else +- ddr_writel(DDRP_PLL_CTRL_PLLPDEN, DDRP_INNOPHY_PLL_CTRL); +- ddr_writel(0, DDRP_INNOPHY_PLL_CTRL); +-#endif + + DDR_BOUND(ddr_readl(DDRP_INNOPHY_PLL_LOCK) & (1 << 3), 10 /*PLL_LOCK*/, ddr_readl(DDRP_INNOPHY_PLL_LOCK)); + } +-- +2.43.0 + diff --git a/spl-src/patches/0007-spl-strip-board_init_f-test-scaffolding-keep-bounded.patch b/spl-src/patches/0007-spl-strip-board_init_f-test-scaffolding-keep-bounded.patch new file mode 100644 index 0000000..4da0c03 --- /dev/null +++ b/spl-src/patches/0007-spl-strip-board_init_f-test-scaffolding-keep-bounded.patch @@ -0,0 +1,74 @@ +From e951f0e6e47ab7b0ffe4df2de1120685a11cd63a Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Tue, 25 Aug 2026 00:02:46 +1000 +Subject: [PATCH 7/8] spl: strip board_init_f test scaffolding (keep bounded + polls + sdram diag as hardening) + +--- + arch/mips/cpu/xburst2/x2000/soc.c | 21 --------------------- + 1 file changed, 21 deletions(-) + +diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c +index 6b96c5b..ab9e437 100755 +--- a/arch/mips/cpu/xburst2/x2000/soc.c ++++ b/arch/mips/cpu/xburst2/x2000/soc.c +@@ -67,23 +67,11 @@ extern void ddr_test_refresh(unsigned int start_addr, unsigned int end_addr); + extern void flush_cache_all(void); + + +-/* DIAG: breadcrumb record @0xb24017c0 (shared with ddr_innophy.c). Set 1 to early-return +- * before sdram_init to prove the mask-ROM return path works in isolation. */ +-#define DIAG_SKIP_SDRAM 0 +-#define BIF_CRUMB(s) do { *(volatile unsigned int*)0xb24017c4 = (s); __asm__ __volatile__("sync"); } while(0) +- + void board_init_f(ulong dummy) + { + /* Set global data pointer */ + gd = &gdata; + +- /* DIAG: mark that we reached board_init_f (magic + stage 0xF0) */ +- *(volatile unsigned int*)0xb24017c0 = 0xD1A6C0DEu; +- *(volatile unsigned int*)0xb24017c8 = 0; +- *(volatile unsigned int*)0xb24017cc = 0; +- *(volatile unsigned int*)0xb24017d0 = 0; +- BIF_CRUMB(0xF0); +- + /* Setup global info */ + #ifndef CONFIG_BURNER + gd->arch.gi = &ginfo; +@@ -92,7 +80,6 @@ void board_init_f(ulong dummy) + #endif + + gpio_init(); +- BIF_CRUMB(0xF1); + + *(volatile unsigned int *)0xb0000020 = 0; //clk gate enable. + *(volatile unsigned int *)0xb0000028 = 0; //clk gate enable. +@@ -106,24 +93,16 @@ void board_init_f(ulong dummy) + #endif + debug("Timer init\n"); + timer_init(); +- BIF_CRUMB(0xF2); + + debug("CLK stop\n"); + // clk_prepare(); + + debug("PLL init\n"); + pll_init(); +- BIF_CRUMB(0xF3); + + debug("CLK init\n"); + clk_init(); +- BIF_CRUMB(0xF4); + +-#if DIAG_SKIP_SDRAM +- /* DIAG: return before DDR to test the mask-ROM return path in isolation. */ +- BIF_CRUMB(0xFE); +- return; +-#endif + + debug("SDRAM init\n"); + sdram_init(); +-- +2.43.0 + diff --git a/spl-src/patches/0008-spl-robust-WDT-disable-clear-TCER.TCEN-TSSR-drop-dyn.patch b/spl-src/patches/0008-spl-robust-WDT-disable-clear-TCER.TCEN-TSSR-drop-dyn.patch new file mode 100644 index 0000000..3af197a --- /dev/null +++ b/spl-src/patches/0008-spl-robust-WDT-disable-clear-TCER.TCEN-TSSR-drop-dyn.patch @@ -0,0 +1,52 @@ +From 7caf048bae01c31460d4d675a52823e5b62ff091 Mon Sep 17 00:00:00 2001 +From: b0hemia <50309975+b0hemia@users.noreply.github.com> +Date: Tue, 25 Aug 2026 01:52:48 +1000 +Subject: [PATCH 8/8] spl: robust WDT disable (clear TCER.TCEN + TSSR) + drop + dynamic-calib sweep (flash-failure root cause) + +--- + arch/mips/cpu/xburst2/ddr_innophy.c | 8 +++++++- + arch/mips/cpu/xburst2/x2000/soc.c | 8 +++++++- + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/cpu/xburst2/ddr_innophy.c b/arch/mips/cpu/xburst2/ddr_innophy.c +index 4c7b921..b78c02b 100755 +--- a/arch/mips/cpu/xburst2/ddr_innophy.c ++++ b/arch/mips/cpu/xburst2/ddr_innophy.c +@@ -626,7 +626,13 @@ void sdram_init(void) + ddr_hook->post_ddr_init(type); + + ddr_diag_stage(8); +- get_dynamic_calib_value(rate); ++ /* Dropped get_dynamic_calib_value(rate): the vendor SPL does NOT do this multi-rate ++ * DVFS calibration sweep. Its result table is never consumed by the burner/writer path, ++ * and the extra frequency transitions + retraining can leave a marginal final PHY ++ * calibration that fails under the writer's sustained full-DRAM load (2026-08-25). The ++ * single ddrp_calibration() above already calibrates at the final 400MHz. */ ++ /* get_dynamic_calib_value(rate); */ ++ (void)rate; + + if(DDRC_AUTOSR_EN_VALUE) { + /* ddr_writel(DDRC_AUTOSR_CNT_VALUE, DDRC_AUTOSR_CNT); */ +diff --git a/arch/mips/cpu/xburst2/x2000/soc.c b/arch/mips/cpu/xburst2/x2000/soc.c +index ab9e437..6ea4b83 100755 +--- a/arch/mips/cpu/xburst2/x2000/soc.c ++++ b/arch/mips/cpu/xburst2/x2000/soc.c +@@ -83,7 +83,13 @@ void board_init_f(ulong dummy) + + *(volatile unsigned int *)0xb0000020 = 0; //clk gate enable. + *(volatile unsigned int *)0xb0000028 = 0; //clk gate enable. +- *(volatile unsigned int *)0xb000202c |= 1 << 16; // wdt disable. ++ /* Disable the watchdog ROBUSTLY (match the vendor SPL + the tree's hw_watchdog_disable): ++ * clear WDT_TCER.TCEN FIRST, then stop the WDT clock via TCU_TSSR (write-1, no RMW). ++ * The old code stopped only the clock, leaving the counter armed - if the mask ROM ++ * re-clocks it after we return, it fires and resets the device mid-flash (root cause of ++ * the 2026-08-25 flash failure: device reset ~36min into the writer and again idle). */ ++ *(volatile unsigned int *)0xb0002004 &= ~(1u << 0); // WDT_TCER.TCEN = 0 (disable counter) ++ *(volatile unsigned int *)0xb000202c = (1u << 16); // TCU_TSSR: stop WDT clock + + /* Init uart first */ + enable_uart_clk(); +-- +2.43.0 + diff --git a/spl-src/uboot-xburst-lpddr3-src.tar.gz b/spl-src/uboot-xburst-lpddr3-src.tar.gz new file mode 100644 index 0000000..b0d8c87 Binary files /dev/null and b/spl-src/uboot-xburst-lpddr3-src.tar.gz differ diff --git a/src/usbboot/Makefile b/src/usbboot/Makefile new file mode 100644 index 0000000..78b538e --- /dev/null +++ b/src/usbboot/Makefile @@ -0,0 +1,14 @@ +DEFINES= +CC?=gcc +CFLAGS=-g -std=c99 -Wall $(DEFINES) `pkg-config --cflags libusb-1.0` +LDFLAGS=`pkg-config --libs libusb-1.0` +SRC=$(wildcard *.c) +EXEC=$(SRC:.c=) + +all: $(EXEC) + +%: %.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +clean: + rm -fr $(EXEC) diff --git a/src/usbboot/README.md b/src/usbboot/README.md new file mode 100644 index 0000000..e8f161a --- /dev/null +++ b/src/usbboot/README.md @@ -0,0 +1,35 @@ +# usbboot (Ingenic X2000 mask-ROM USB loader) + +This is the host-side loader that talks to the Ingenic X2000's on-chip mask-ROM USB boot +mode (`a108:eaef`). diskOS's installer uses it to upload the stage-1 SPL and the on-device +NAND writer during a flash. + +## Provenance and license + +- **License:** GPL-2.0-or-later. +- **Copyright:** (c) 2021 Aidan MacDonald, (c) 2015 Amaury Pouly, and contributors. +- Part of the Ingenic X2000 community mask-ROM tooling lineage (the same tool family is used + to recover other X2000 boards). The copy here is included as GPL corresponding source; see + the repository's [`NOTICE.md`](../../NOTICE.md). + +## Local modifications for diskOS + +- Larger USB transfer chunks and a longer command timeout, so the ~90-minute on-device NAND + writer stage does not time out. +- Built fully statically for portability - see [`build/build-usbboot-static.sh`](../../build/build-usbboot-static.sh). + +## Building + +``` +make # dynamic build (needs libusb-1.0 dev headers) +``` + +For the portable static binary the installer ships, use +[`build/build-usbboot-static.sh`](../../build/build-usbboot-static.sh) instead, which pins the +libusb source and links it statically. + +## Note + +This tool drives raw mask-ROM USB and can erase/write device storage. It is intended to be +invoked by the diskOS installer, which adds device checks and the fail-closed flashing flow. +Running it directly is for developers who understand the Ingenic boot protocol. diff --git a/src/usbboot/usbboot.c b/src/usbboot/usbboot.c new file mode 100644 index 0000000..f26f235 --- /dev/null +++ b/src/usbboot/usbboot.c @@ -0,0 +1,917 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * Directly adapted from jz4760_tools/usbboot.c, + * Copyright (C) 2015 by Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VR_GET_CPU_INFO 0 +#define VR_SET_DATA_ADDRESS 1 +#define VR_SET_DATA_LENGTH 2 +#define VR_FLUSH_CACHES 3 +#define VR_PROGRAM_START1 4 +#define VR_PROGRAM_START2 5 +#define VR_GET_ACK 0x10 +#define VR_INIT 0x11 +#define VR_WRITE 0x12 +#define VR_READ 0x13 +#define VR_UPDATE_CFG 0x14 + +#define MAGIC_DEBUG ('D' << 24) | ('B' << 16) | ('G' << 8) | 0 +#define MAGIC_MMC ('M' << 24) | ('M' << 16) | ('C' << 8) | 0 +#define MAGIC_POLICY ('P' << 24) | ('O' << 16) | ('L' << 8) | ('I' << 0) + +typedef struct ParameterInfo { + uint32_t magic; + uint32_t size; + uint32_t data[0]; +} ParameterInfo; + +struct mmc_erase_range { + uint32_t start; + uint32_t end; +}; + +typedef struct mmc_param { + int mmc_open_card; + int mmc_erase; + uint32_t mmc_erase_range_count; + uint32_t blob[59]; // don't care, not formatting +} mmc_param; + +typedef struct debug_param { + uint32_t log_enabled; + uint32_t transfer_data_chk; + uint32_t write_back_chk; + uint32_t transfer_size; + uint32_t stage2_timeout; +} debug_param; + +typedef struct policy_param { + int use_nand_mgr; + int use_nand_mtd; + int use_mmc0; + int use_mmc1; + int use_mmc2; + uint32_t use_sfc_nor; + uint32_t use_sfc_nand; + uint32_t use_spi_nand; + uint32_t use_spi_nor; + uint32_t offsets[32]; +} policy_param;; + + +// burner commands +typedef struct update_cmd { + uint32_t length; + uint32_t unused[9]; // pad to 40 bytes +} UpdateCmd; + +typedef struct write_cmd { + uint64_t partition; + uint32_t ops; + uint32_t offset; + uint32_t length; + uint32_t crc; + uint32_t unused[4]; +} WriteCmd; + +typedef struct read_cmd { + uint64_t partition; + uint32_t ops; + uint32_t offset; + uint32_t length; + uint32_t unused[5]; +} ReadCmd; + + +/* Global variables */ +bool g_verbose = false; +libusb_device_handle* g_usb_dev = NULL; +int g_vid = 0, g_pid = 0; +int g_med = 2; /* storage medium index: 2=mmc0, 5=sfc_nor, 6=sfc_nand, 7=spi_nand. policy selects medium */ +long g_read_ops = -1; /* read-op override; -1 = default (g_med<<16). Decouples read-op from policy medium. */ +int g_handshake = 1; /* do the cloner's VR_READ post-handshake (bReq 0x19 read 8B, then trailing GET_ACK) */ +int g_strcmd = 0; /* cloner string protocol: send "cmd:read,..." via VR_UPDATE_CFG+bulk before VR_READ */ +const char *g_cfg1 = NULL, *g_cfg2 = NULL; /* captured SFC-NAND enable blobs to replay verbatim */ +int g_cmd_retries = 1; /* retry vendor/bulk transfers N times on error (env USBBOOT_RETRIES) for a flaky link */ +int g_cmd_to = 1000; /* per-attempt control timeout ms (env USBBOOT_TIMEOUT) */ + +/* Utility functions */ +void die(const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +void verbose(const char* msg, ...) +{ + if(!g_verbose) + return; + + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + printf("\n"); + va_end(ap); +} + +void open_usb(void) +{ + if(g_usb_dev) { + verbose("Closing USB device"); + libusb_close(g_usb_dev); + } + + if(g_vid == 0 || g_pid == 0) + die("Can't open USB device: vendor/product ID not specified"); + + verbose("Opening USB device %04x:%04x", g_vid, g_pid); + g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid); + if(!g_usb_dev) + die("Could not open USB device"); + + int ret = libusb_claim_interface(g_usb_dev, 0); + if(ret != 0) { + libusb_close(g_usb_dev); + die("Could not claim interface: %d", ret); + } + { char* e; if((e=getenv("USBBOOT_RETRIES"))) g_cmd_retries=atoi(e); if(g_cmd_retries<1) g_cmd_retries=1; + if((e=getenv("USBBOOT_TIMEOUT"))) g_cmd_to=atoi(e); if(g_cmd_to<10) g_cmd_to=1000; + if(g_cmd_retries>1) verbose("retry mode: %d tries x %dms", g_cmd_retries, g_cmd_to); } +} + +void ensure_usb(void) +{ + if(!g_usb_dev) + open_usb(); +} + +/* USB communication functions */ +void jz_get_cpu_info(void) +{ + ensure_usb(); + verbose("Issue GET_CPU_INFO"); + + uint8_t buf[9]; + int ret = libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + VR_GET_CPU_INFO, 0, 0, buf, 8, 1000); + /* libusb_control_transfer returns the byte count on success (8 here), not 0; + only negative values are errors. Treat "got fewer than 8 bytes" as failure. */ + if(ret != 8) + die("Can't get CPU info: %d", ret); + + buf[8] = 0; + printf("CPU info: %s\n", buf); +} + +void jz_upload(const char* filename, int length) +{ + if(length < 0) + die("invalid upload length: %d", length); + + ensure_usb(); + verbose("Transfer %d bytes from device to host", length); + + void* data = malloc(length); + int total = 0; + while(total < length) { + int xfered = 0; + int want = length - total; + if(want > (4<<20)) want = 4<<20; /* 4MB per bulk transfer */ + int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1, + (unsigned char*)data + total, want, &xfered, 600000); + if(ret != 0 && xfered == 0) + die("Transfer failed at %d/%d: %d", total, length, ret); + total += xfered; + } + if(total != length) + die("Transfer error: got %d bytes, expected %d", total, length); + + FILE* f = fopen(filename, "wb"); + if(f == NULL) + die("Can't open file '%s' for writing", filename); + + if(fwrite(data, length, 1, f) != 1) + die("Error writing transfered data to file"); + + fclose(f); + free(data); +} + +void bulk_transfer_out(void* data, int length) { + verbose("Transfer %d bytes from host to device", length); + int total = 0; + while(total < length) { + int xfered = 0; + int want = length - total; + if(want > (4<<20)) want = 4<<20; /* 4MB per bulk transfer */ + int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1, + (unsigned char*)data + total, want, &xfered, 600000); + if(ret != 0 && xfered == 0) + die("Transfer failed at %d/%d: %d", total, length, ret); + total += xfered; + } + if(total != length) + die("Transfer error: %d sent, expected %d", total, length); +} + +#define jz_vendor_out_func(name, type, fmt) \ + void name(unsigned long param) { \ + ensure_usb(); \ + verbose("Issue " #type fmt, param); \ + int ret=-1,_t; \ + for(_t=0;_t> 16, param & 0xffff, NULL, 0, g_cmd_to); \ + if(ret==0) break; \ + } \ + if(ret != 0) \ + die("Request " #type " failed: %d (after %d tries)", ret, g_cmd_retries); \ + } + +jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx") +jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx") +jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "") +jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx") +jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx") +jz_vendor_out_func(jz_init, INIT, " 0x%08lx") +#define jz_flush_caches() _jz_flush_caches(0) + +void jz_generic_out(uint8_t op, + unsigned long param, + unsigned char *data, + uint16_t len) { + ensure_usb(); + verbose("Issue 0x%x", op); + int ret = libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, + op, param >> 16, param & 0xffff, data, len, 1000); + if(ret != len) + die("Request 0x%x failed, only transfered: %d", op, ret); +} + +void jz_download(const char* filename) +{ + FILE* f = fopen(filename, "rb"); + if(f == NULL) + die("Can't open file '%s' for reading", filename); + + fseek(f, 0, SEEK_END); + int length = ftell(f); + fseek(f, 0, SEEK_SET); + + void* data = malloc(length); + if(fread(data, length, 1, f) != 1) + die("Error reading data from file"); + fclose(f); + + jz_set_data_length(length); + bulk_transfer_out(data, length); + + free(data); +} + +void jz_get_ack() { + ensure_usb(); + verbose("Issue VR_GET_ACK"); + + uint8_t buf[4]; + int ret = libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + VR_GET_ACK, 0, 0, buf, 4, 1000); + if(ret != 4) + die("Can't get ACK: %d", ret); +} + +void enable_mmc() { + ensure_usb(); + + ParameterInfo *policy_param_info = (ParameterInfo*) malloc(sizeof(ParameterInfo) + sizeof(policy_param)); + policy_param_info->magic = MAGIC_POLICY; + policy_param_info->size = sizeof(policy_param); + memset(policy_param_info->data, 0, sizeof(policy_param)); + policy_param *policy_cfg = (policy_param*)policy_param_info->data; + switch (g_med) { + case 0: policy_cfg->use_nand_mgr = 1; break; + case 1: policy_cfg->use_nand_mtd = 1; break; + case 2: policy_cfg->use_mmc0 = 1; break; + case 3: policy_cfg->use_mmc1 = 1; break; + case 4: policy_cfg->use_mmc2 = 1; break; + case 5: policy_cfg->use_sfc_nor = 1; break; + case 6: policy_cfg->use_sfc_nand = 1; break; + case 7: policy_cfg->use_spi_nand = 1; break; + case 8: policy_cfg->use_spi_nor = 1; break; + default: policy_cfg->use_mmc0 = 1; break; + } + + ParameterInfo *dbg_param_info = (ParameterInfo*) malloc(sizeof(ParameterInfo) + sizeof(debug_param)); + dbg_param_info->magic = MAGIC_DEBUG; + dbg_param_info->size = sizeof(debug_param); + debug_param *dbg = (debug_param*)dbg_param_info->data; + dbg->log_enabled = 1; + dbg->transfer_data_chk = 0; + dbg->write_back_chk = 0; + dbg->transfer_size = 0; + dbg->stage2_timeout = 0; + + ParameterInfo *mmc_param_info = (ParameterInfo*) malloc(sizeof(ParameterInfo) + sizeof(mmc_param)); + mmc_param_info->magic = MAGIC_MMC; + mmc_param_info->size = sizeof(mmc_param); + memset(mmc_param_info->data, 0, sizeof(mmc_param)); + + uint32_t data_size = 3 * 8 + policy_param_info->size + dbg_param_info->size + mmc_param_info->size; + unsigned char data[data_size]; + unsigned char *p = data; + uint32_t offset = 0; + memcpy(p + offset, dbg_param_info, 4 + 4 + dbg_param_info->size); + offset += 4 + 4 + dbg_param_info->size; + memcpy(p + offset, mmc_param_info, 4 + 4 + mmc_param_info->size); + offset += 4 + 4 + mmc_param_info->size; + memcpy(p + offset, policy_param_info, 4 + 4 + policy_param_info->size); + + UpdateCmd *update = (UpdateCmd*) malloc(sizeof(UpdateCmd)); + memset(update, 0, sizeof(UpdateCmd)); + update->length = data_size; + + jz_generic_out(VR_UPDATE_CFG, 0, (unsigned char*)update, sizeof(UpdateCmd)); + + bulk_transfer_out(p, data_size); + + free(policy_param_info); + free(dbg_param_info); + free(mmc_param_info); + free(update); + + jz_get_ack(); + jz_init(0); + jz_get_ack(); +} + +static void send_str_cmd(const char* s); + +void mmc_read(uint32_t offset, uint32_t length, unsigned char* out) { + /* Cloner string protocol: tell the burner to stage a flash read before we collect it. */ + if (g_strcmd) { + char cmd[160]; + uint32_t ops = (g_read_ops >= 0) ? (uint32_t)g_read_ops : 0; + snprintf(cmd, sizeof(cmd), + "cmd:read,med:%d,addr:0,offs:%u,len:%u,ops:%u,crc:0", + g_med, offset, length, ops); + send_str_cmd(cmd); + } + + ReadCmd *read = (ReadCmd*) malloc(sizeof(ReadCmd)); + memset(read, 0, sizeof(ReadCmd)); + read->ops = (g_read_ops >= 0) ? (uint32_t)g_read_ops : (uint32_t)(g_med << 16); // --ops overrides; policy selects medium + read->offset = offset; + read->length = length; + + jz_generic_out(VR_READ, 0, (unsigned char*)read, sizeof(ReadCmd)); + free(read); + + /* The cloner does a handshake the bare ballaswag read omits: between VR_READ + and the bulk-IN it issues a vendor-IN bRequest 0x19 reading 8 bytes + ([ack:4][crc:4]). The burner reads flash into its buffer and gates the bulk + transfer on this ack - without it the bulk-IN hangs. (core_64 get_ack_and_crc) */ + if (g_handshake) { + unsigned char ack[8]; + memset(ack, 0, sizeof(ack)); + int r = libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + 0x19, 0, 0, ack, 8, 10000); + verbose("VR_READ ack(0x19): ret=%d ack=%02x%02x%02x%02x crc=%02x%02x%02x%02x", + r, ack[0],ack[1],ack[2],ack[3], ack[4],ack[5],ack[6],ack[7]); + } + + int total = 0; + while (total < (int)length) { + int xfered = 0; + int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1, + out + total, length - total, &xfered, 600000); + if(ret != 0) + die("read bulk failed at %d/%u: %d", total, length, ret); + total += xfered; + } + + /* Trailing per-chunk ack the cloner reads after the data (bRequest 0x10). */ + if (g_handshake) { + unsigned char fack[4]; + memset(fack, 0, sizeof(fack)); + libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + VR_GET_ACK, 0, 0, fack, 4, 10000); + } +} + +/* Send a cloner STRING command ("cmd:read,med:6,addr:0,offs:...,len:...,ops:...,crc:0") + the way core_64::stage2_send_cmd_config does: VR_UPDATE_CFG(0x14) header + bulk-OUT the + ASCII string + GET_ACK. The cloner burner parses it (strcmp "read"/"write"/...) and sets up + the operation; a following VR_READ+0x19 then collects the data. */ +static void send_str_cmd(const char* s) { + int n = (int)strlen(s); + UpdateCmd u; memset(&u, 0, sizeof(u)); u.length = (uint32_t)n; + jz_generic_out(VR_UPDATE_CFG, 0, (unsigned char*)&u, sizeof(UpdateCmd)); + bulk_transfer_out((unsigned char*)s, n); + jz_get_ack(); + verbose("sent str cmd: \"%s\"", s); +} + +/* Send one UPDATE_CFG config blob (verbatim, captured from the cloner) + GET_ACK. */ +static void send_cfg_blob(const char* file) { + FILE* f = fopen(file, "rb"); + if(!f) die("can't open cfg blob '%s'", file); + fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); + unsigned char* buf = malloc(n); + if(fread(buf, n, 1, f) != 1) die("read cfg blob error"); + fclose(f); + UpdateCmd u; memset(&u, 0, sizeof(u)); u.length = (uint32_t)n; + jz_generic_out(VR_UPDATE_CFG, 0, (unsigned char*)&u, sizeof(UpdateCmd)); + bulk_transfer_out(buf, (int)n); + free(buf); + jz_get_ack(); +} + +/* Replay the cloner's captured SFC-NAND enable sequence: + cfg1(POLICY) -> GET_ACK -> bReq38(0x26) query -> cfg2(DDR+DBG+EFUSE+POLICY+SFC) -> GET_ACK -> INIT -> GET_ACK */ +static void enable_sfcnand_replay(void) { + ensure_usb(); + send_cfg_blob(g_cfg1); + unsigned char q[3] = {0}; + libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, + 38, 0, 0, q, 3, 1000); + send_cfg_blob(g_cfg2); + jz_init(0); + jz_get_ack(); +} + +/* Sweep candidate read-ops against the live burner. For each ops: VR_READ a small + page, do the 0x19 ack (fast feedback: ret-code != -38 means the op was accepted), + then a short bulk drain. The op whose ack-code is success AND streams bulk data wins. */ +void probe_ops(void) { + if (g_cfg1 && g_cfg2) enable_sfcnand_replay(); else enable_mmc(); + uint32_t cands[] = { + 0x060000, 0x0d0000, 0x010000, 0x000000, 0x020000, + 0x030000, 0x040000, 0x050000, 0x070000, 0x080000, + 0x090000, 0x0a0000, 0x0b0000, 0x0c0000, 0x0e0000, + 0x0d0601, 6, 13, 1, 2 + }; + uint32_t plen = 4096; + unsigned char *buf = malloc(plen); + printf("=== ops probe (offset 0, %u bytes/probe) ===\n", plen); + for (unsigned i = 0; i < sizeof(cands)/sizeof(cands[0]); i++) { + ReadCmd r; memset(&r, 0, sizeof(r)); + r.ops = cands[i]; r.offset = 0; r.length = plen; + jz_generic_out(VR_READ, 0, (unsigned char*)&r, sizeof(r)); + unsigned char ack[8]; memset(ack, 0, sizeof(ack)); + libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, + 0x19, 0, 0, ack, 8, 3000); + uint32_t a = ack[0]|(ack[1]<<8)|(ack[2]<<16)|((uint32_t)ack[3]<<24); + uint32_t c = ack[4]|(ack[5]<<8)|(ack[6]<<16)|((uint32_t)ack[7]<<24); + int xfered = 0; + int br = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN|1, buf, plen, &xfered, 2000); + unsigned char f[4] = {0}; + libusb_control_transfer(g_usb_dev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, + VR_GET_ACK, 0, 0, f, 4, 2000); + printf("ops=0x%06x ack=0x%08x crc=0x%08x bulk=%d xfered=%d %s\n", + cands[i], a, c, br, xfered, + (br == 0 && xfered > 0) ? "<<<<< DATA STREAMED" : ""); + } + free(buf); +} + +void mmc_read_partition(uint32_t offset, uint32_t length, const char* fname) { + if (g_cfg1 && g_cfg2) enable_sfcnand_replay(); else enable_mmc(); + if(length == 0) + die("invalid partition length: %d", length); + + printf("dumping parition at offset 0x%x, size 0x%x\n", offset, length); + + uint32_t chunk_size = 1024 * 1024 * 2; // 2mb + unsigned char chunk[chunk_size]; + + FILE* f = fopen(fname, "wb"); + if(f == NULL) + die("Can't open file '%s' for writing", fname); + + uint32_t cursor = offset; + uint32_t end = offset + length; + while (cursor < end) { + uint32_t read_size = (end - cursor) >= chunk_size + ? chunk_size + : (end - cursor); + + uint32_t start = (uint32_t)time(NULL); + mmc_read(cursor, read_size, chunk); + uint32_t duration = (uint32_t)time(NULL) - start; + + cursor += read_size; + + printf("%.2f%% completed (%.2fMB/s)\n", + (cursor - offset) / (float)length * 100, + (read_size / 1024 / 1024) / (float)duration); + + if (fwrite(chunk, read_size, 1, f) != 1) + die("Failed to write data to %x", fname); + } + + fclose(f); +} + +void mmc_write(uint32_t offset, uint32_t length, unsigned char* in) { + WriteCmd *write = (WriteCmd*) malloc(sizeof(WriteCmd)); + memset(write, 0, sizeof(WriteCmd)); + write->ops = 0x020000; // mmc + write->offset = offset; + write->length = length; + + jz_generic_out(VR_WRITE, 0, (unsigned char*)write, sizeof(WriteCmd)); + free(write); + + bulk_transfer_out(in, length); +} + +void swap_ota_partition(bool force) { + ensure_usb(); + enable_mmc(); + + uint32_t ota_len = 512; + unsigned char ota[ota_len]; + + mmc_read(0x100000, ota_len, ota); + + char ota_in[ota_len]; + memset(ota_in, 0, ota_len); + + if (strncmp((char*)ota, "ota:kernel2", 11) == 0) { + printf("Current OTA points at kernel2. Switching OTA to kernel\n"); + strcpy(ota_in, "ota:kernel\n\n"); + } else if (strncmp((char*)ota, "ota:kernel\n", 11) == 0) { + printf("Current OTA points at kernel. Switching OTA to kernel2\n"); + strcpy(ota_in, "ota:kernel2\n\n"); + } else { + if (!force) { + die("Exiting! Your OTA contains unexpected values, swapping OTA might not fix your issue."); + } + + printf("Unknown value in OTA. Forcing OTA to use ota:kernel\n"); + strcpy(ota_in, "ota:kernel\n\n"); + } + + mmc_write(0x100000, ota_len, (unsigned char*)ota_in); + printf("Switched OTA to %s", ota_in); +} + +/* Default settings */ +struct cpu_profile { + const char* name; + int vid, pid; + unsigned long s1_load_addr, s1_exec_addr; + unsigned long s2_load_addr, s2_exec_addr; +}; + +static const struct cpu_profile cpu_profiles[] = { + {"x2000", + 0xa108, 0xeaef, + 0xb2401000, 0xb2401800, + 0x80100000, 0x80100000}, + {NULL} +}; + +/* Simple "download and run" functions for dev purposes */ +unsigned long s1_load_addr = 0, s1_exec_addr = 0; +unsigned long s2_load_addr = 0, s2_exec_addr = 0; + +void apply_cpu_profile(const char* name) +{ + const struct cpu_profile* p = &cpu_profiles[0]; + for(p = &cpu_profiles[0]; p->name != NULL; ++p) { + if(strcmp(p->name, name) != 0) + continue; + + g_vid = p->vid; + g_pid = p->pid; + s1_load_addr = p->s1_load_addr; + s1_exec_addr = p->s1_exec_addr; + s2_load_addr = p->s2_load_addr; + s2_exec_addr = p->s2_exec_addr; + return; + } + + die("CPU '%s' not known", name); +} + +void run_stage1(const char* filename) +{ + if(s1_load_addr == 0 || s1_exec_addr == 0) + die("No stage1 binary settings -- did you specify --cpu?"); + jz_set_data_address(s1_load_addr); + jz_download(filename); + jz_program_start1(s1_exec_addr); +} + +void run_stage2(const char* filename) +{ + if(s2_load_addr == 0 || s2_exec_addr == 0) + die("No stage2 binary settings -- did you specify --cpu?"); + jz_set_data_address(s2_load_addr); + jz_download(filename); + jz_flush_caches(); + jz_program_start2(s2_exec_addr); +} + +void start_x2000_uboot() { + run_stage1("./spl.bin"); + sleep(1); + run_stage2("./uboot.bin"); +} + +/* Main functions */ +void usage() +{ + printf("\ +Usage: usbboot [options]\n\ +\n\ +Basic options:\n\ + --uboot Start uboot\n\ + --cpu Select device CPU type\n\ + --stage1 Download and execute stage1 binary\n\ + --stage2 Download and execute stage2 binary\n\ +\n\ +Advanced options:\n\ + --vid Specify USB vendor ID\n\ + --pid Specify USB product ID\n\ + --swap-ota Switch OTA between kernel/kernel2\n\ + --force-swap-ota Ignore unknown OTA value, write ota:kernel to OTA\n\ + --cpuinfo Ask device for CPU info\n\ + --addr Set data address\n\ + --length Set data length\n\ + --upload Transfer data from device (needs prior --length)\n\ + --download Transfer data to device\n\ + --start1 Execute stage1 code at address\n\ + --start2 Execute stage2 code at address\n\ + --flush-caches Flush device CPU caches\n\ + --renumerate Close and re-open the USB device\n\ + --wait