Detailed protocol surfaces, firmware evidence and scope for developers using
mediatek-optical. Start with the README for integration.
READ BUFFER mode 6 exposes the 2 MiB flash at file offsets with two differences from the image that was flashed:
- The boot page
0x0..0x400reads back as the image's page at0x10000. Flash stores the boot page in a form that never reads back. - The drive rewrites four regions at runtime and an update never overwrites
them: the UHD and BD host revocation lists, the settings page and the
calibration block (
layout::DRIVE_WRITTEN).
Across 205 OEM update images of both generations (LG, HP and ASUS), 204 store
the same boot page, and every drive-read difference falls in those five
ranges. oem::content_key hashes everything else, so a drive read and the OEM
image it came from share a key.
image::family names the board an image is built for (scheme
mediatek.hardware.v1). Every MT19xx build carries a hardware configuration
record in its code area: the MMC feature template the firmware answers GET
CONFIGURATION from, then 55 AA, UNIQUE and ten digits, the MTEKMT19xx
tag and the platform code. The family hashes the chip, the record's platform
code (JB7, JB8, BU3, ...), the bootloader the boot banner names, the
Core feature's interface, feature 0x0003 and whether a BD writing profile is
enabled. It reads nothing a drive read changes, so a drive read and the image
it was flashed from share a family.
Across 207 OEM images the definition yields 11 families. One model can span
two: a BH16NS40 1.03 (JB7, family 11e7527216405467) and a BH16NS40 1.05
(JB8, family 70f72c46f9603141) are different boards, and the vendor
updaters refuse to cross them. None means the family cannot be
established: a wrong size, no chip, a malformed banner, no or several
records, a record without a parseable MTEKMT19xx tag (the tag may name the
other chip), a tagged descriptor whose platform code differs from the
record's, or a broken feature template.
envelope::Envelope::load(bytes) recognizes plaintext and OEM AES input,
validates the canonical image, and retains the original bytes and both hashes.
Corrupt images, empty integrity tables, tables with an unrecognised entry
enabled word (only 1 = active and 0xFFFFFFFF = unused are defined) and raw
drive captures are rejected.
Its input_encoding() describes the file only: the running receiver determines
the required transfer encoding. Loading an envelope performs no drive I/O and
does not authorize writing.
update::InstalledFirmware::new(identity, bytes) binds captured installed bytes
to live identity. receiver::detect establishes the active entry and encoding
contract; Receiver::prepare(&envelope, &installed) checks target integrity,
controller generation and hardware family, then constructs an immutable transfer
plan. Unknown or conflicting receiver contracts fail before any write. File
encoding never chooses transfer encoding; header and body can require different
representations.
receiver::PreparedUpdate::flash_completed returns a Completion whose
ReadbackQuality explicitly distinguishes complete and limited readback, and
whose Startup::evidence() states how strongly a restart into the target was
observed. Every readable mismatch and every failed data write remains an error.
After COMMIT the sequence is unchanged: TEST UNIT READY (a failure is only reported), REQUEST SENSE, a TEST UNIT READY poll of at most 120 seconds (the OEM command window), REQUEST SENSE, read-back, then one INQUIRY and one READ BUFFER of the descriptor. Readiness only gates read-back. Neither a not-ready poll nor a unit-attention sense is counted as restart evidence: no recorded hardware fact establishes either as a restart signal. Success requires read-back, an application INQUIRY whose vendor, product and revision equal the target's, and the stored descriptor (bytes 0..0x58) equal to the target's. That descriptor is read through READ BUFFER, the same path as read-back, so it shows what was programmed, not what is running.
The post-write INQUIRY is compared with the receiver identity revalidated
immediately before the write (operating mode and INQUIRY bytes 8..36 only; a
Receiver::from_firmware receiver stores a synthesized INQUIRY whose other
bytes are not evidence):
StartupEvidence::LeftBoot: BOOT answered before the write; the target application answers now.StartupEvidence::IdentityChanged: the application reported a different vendor, product or revision before the write and reports the target's now. Whether old code could serve INQUIRY from newly programmed flash is not recorded; this is the strongest evidence INQUIRY offers.StartupEvidence::Unobserved: the application already reported the target's identity before the write (a reflash or a same-version build). INQUIRY cannot tell the old firmware still running apart from the target having started.flash_completedreturns this as reduced verification, not confirmed startup. Hosts must checkStartup::evidence()and, forUnobserved, advise a power cycle and re-identification before relying on the new firmware. No command is added to look for a restart, and nothing is retried.
When installed bytes are genuinely unavailable, the explicit
prepare_without_installed path retains target/chip/receiver checks but cannot
establish installed-family equality. The caller must obtain the corresponding
consent; the library never invents installed bytes from the target. A Current
firmware reference may supply offline receiver evidence, with explicit provenance
and physical metadata binding; post-write verification still uses live reads.
The installed binding, Current-reference revalidation (at detection and again
immediately before PREPARE) and post-write startup confirmation each compare the
complete 0x100-byte identity descriptor at 0x1ec000. The descriptor is programmed
from the image, not written by the drive. Read-back compares only the image's
CMAC-covered ranges; in every held MT19 image these exclude the descriptor, so
startup is the only post-write evidence for it. A differing descriptor is
receiver::Error::Changed before a write and StartupError::TargetMismatch
after one.
oem::rebuild turns a complete drive read into a flashable image:
- Exact: the read's content key matches a cataloged OEM build. The result is byte-identical to that vendor file.
- Chip default: an unknown build (a revision the catalog lacks, or a
modified image) gets its chip generation's default factory contents. Regions
the drive left erased stay erased. The result carries no per-unit data but
is a reconstruction, reported as such in
Provenance.
A boot page that is neither the stored page nor the mirror of a drive read is
refused: no image is produced whose stored boot page is unknown. The crate
ships no catalog; build one from OEM images with oem::Factory::from_image
and oem::content_key.
Enable drive, image and enc. Identify the physical drive, bind the captured
installed image, detect the receiver, load the target envelope::Envelope, then
prepare and execute. Both read and write transports must address the same drive,
with exclusive access held for the entire operation.
examples/flash.rs is a compiled generic host integration
example using Transport, FlashRuntime and FlashTransport. It returns the
structured completion result; its main does not access hardware. The low-level
drive::Session is a protocol primitive and supplies no compatibility or
completion guarantee.
Flash-sequence CDBs are 12 bytes, exactly as the OEM update tool issues them. Offsets are 24-bit big-endian.
| Command | Constructor | CDB |
|---|---|---|
| Memory read | cdb::read_memory(off, len) |
3C 06 00 <off> <len> 00 |
| Identity / feature | cdb::inquiry(n), cdb::get_configuration(code, n) |
12 …, 46 02 <code> … |
| Probe | cdb::probe() |
3C 06 00 1E C0 00 00 01 00 00 00 00 |
| Ready | cdb::test_unit_ready() |
00 × 12 |
| Enter update | cdb::enter_update() |
3B 01 00 00 00 00 00 00 00 0B 00 00 |
| Write chunk | cdb::transfer(off, len) |
3B 06 00 <off> 00 <len16> 00 00 00 |
| Commit | cdb::finish() |
3B 07 00 00 00 00 00 00 00 00 1B 12 |
| Status | cdb::request_sense() |
03 00 00 00 10 80 00 00 00 00 00 00 |
The drive erases and programs flash when the last chunk of a complete 2 MiB image lands. The commit is a trailing handshake.
The analysis feature models how drive firmware selects a command handler.
This is a static-analysis model derived from one disassembly, BH16NS40 1.04
(SHA-256 86a46340da93d1f5a1596ab6e42918a9d4e7cb46394408b5ae8fa60f41e0c22f;
the copy bc4e6126b880475d58c21e8639ea81f33facc3d4c8284bf6a739d8e8f6c75921
has the same layout). It has not been verified on hardware.
- The dispatcher scans one array root at a time. A registrar called with
BLsets the initial root. Tail-call (B) registrations, one per media type, replace it. - It first scans a RAM lookup cache that the registrar initialises. It assumes that a hit in a registered table is copied into the cache, so the cache holds no record that the flash tables do not already hold. This copy-on-hit behaviour is a modelling assumption.
- In a restricted state it scans only one flash table, which it is handed directly.
CommandDispatch::record() computes uniqueness over every table in the model,
and requires the selected record to be reached from the initial root and from
every media-type root. The model does not cover:
- whether a tail-call site is live;
- writers of the array or the cache other than the registrar;
- indirect registrations.
An update session writes drive firmware. A wrong image, or an interrupted
session, can leave the drive unusable. Verify an image with image::cmac
before flashing, and flash through receiver::PreparedUpdate, which refuses
an image for other hardware. The identity, feature and memory-read commands
are read-only.
1.75.
MIT. See LICENSE-MIT.
With drive, receiver::detect_mode(&mut transport) identifies BOOT versus
running firmware from INQUIRY. Pass that result to
receiver::detect_encoding(&mut transport, &mode) to inspect the active
receiver's code and obtain Encoding::Plaintext or Encoding::Encrypted.
For execution, use receiver::detect and receiver-bound preparation, which
selects wire encoding and automatically revalidates before writing. The caller
owns host policy; no manual encrypted/plaintext override is provided.
Detection sends only INQUIRY and READ BUFFER mode 6. It recognizes positive Thumb instruction sequences in the audited MT1939/MT1959 receiver ranges; unknown or conflicting signatures never default to plaintext. BOOT and application are evaluated independently, including images whose two receivers expect different formats. Read failures can prevent detection on a live drive; static corpus coverage does not establish physical-device read accessibility.
Explicit recovery::PreparedRecovery::flash_completed reports a failed PREPARE through
FlashRuntime::recovery_entry_failed and proceeds once to the selected image.
This is recovery host policy, not a claim that the receiver always rejects
PREPARE. Normal PreparedUpdate::flash_completed remains strict. STREAM
failures stop both paths, no firmware writes retry, and recovery still requires
readback verification and the target application identity, reporting the
restart evidence as above. Physical recovery remains unvalidated.
receiver::entry::inspect_boot_legacy(window, flash_address) and
inspect_boot_dual(window, flash_address) inspect PREPARE flag-copy grammars
with checked operands, literals and branch targets. The latter binds mode 1
and mode 6/buffer 0x67 to the same body and validates the unsupported-mode error
path. Results are entry evidence, not transfer authorization: completion,
dispatcher binding and image acceptance still need separate proof. All 136
published images match exactly one grammar (98 legacy, 38 dual entry). The full
1024-image hoard has 102 legacy and 38 dual-entry matches; 884 other images
remain unresolved. No error-continuation policy is inferred from these matches.
receiver::entry::application::inspect binds an application command table to
the unique MMIO update-flag clearing block. All 136 published images match:
25 inline Thumb tables and 111 compiler-helper tables, with 18 mode-1 targets
reaching the block through one branch. Helper candidates require a separate
bounded read checked by matches_helper. These bindings do not prove entry
guards, complete flag-copy semantics, opcode dispatch or completion behavior,
and do not authorize a transfer.
application::ModeBufferGuard recognizes the complete 40-byte mode/buffer
exclusion helper present in 73 published images. Its return value permits
mode 1/buffer 0; 63 images have no pre-table guard call in the independent
census. Helper matching alone does not prove the caller's argument setup or
rejection branch, and does not establish a receiver entry contract.
application::inspect_prologue binds the caller's mode/buffer arguments,
guard-result branch, CDB pointer and preloaded constants. inspect_flags
revalidates those inputs against the supplied bytes and checks every bit test,
OR operand, MMIO store, branch convergence and stack-restoring return. All 136
published images match both grammars; 3,280 instruction mutations are rejected.
These remain entry-body evidence: command dispatcher ownership, completion,
mode-6 alternatives and acceptance/programming rules need their own proofs.
application::inspect_mode_six binds the CDB buffer guard through up to three
compiler trampolines. In 38 images, buffer 0x67 reaches the same complete flag
writer as mode one; 98 images have a positively matched zero-buffer stream
guard. All 136 cases and 408 guard mutations are checked against the independent
census. This establishes the alternate entry body, not stream acceptance or
command completion.
application::inspect_registration binds a unique aligned WRITE BUFFER/type-1
record to the handler's Thumb pointer. All 136 published images match the
independent opcode/callback census. This is static table evidence; initializer,
lookup execution, pre-dispatch predicates and completion remain separate proofs.
For executable operations, use receiver::detect(transport, &mode). It binds
encoding, the complete entry prelude/body, mode-six guard, compiler helper and
WRITE BUFFER registration to bounded live reads. Unknown entry code fails
closed. Encrypted applications with a proven shared 0x67 entry select that
dialect; the other recognized receivers select the proven mode-one path.
Both Flash and explicit Recovery store the resulting command in the same
immutable TransferPlan, revalidate its code/table snapshots before writing,
and execute through the shared transfer/readback engine. The snapshot re-read
is bracketed by INQUIRY identity checks: READ BUFFER returns the same flash
bytes from BOOT and from the application, so only INQUIRY binds the running
receiver, and the last revalidation command before the update session is an
INQUIRY. A Current-reference receiver likewise re-reads INQUIRY after its
banner and descriptor. detect_encoding
remains a read-only encoding probe and cannot supply an executable plan.
The private website corpus test replays both receiver modes for every package:
MTK_RECEIVER_AUDIT=/path/to/website-transfer-audit.json \
MTK_HOARD_ROOT=/path/to/firmware-extractor \
cargo test --all-features --test receiver_corpus -- --ignored --nocaptureTyped errors implement mediatek_optical::CodedError. error.code() returns a
stable identifier such as mediatek.receiver.changed, independent of runtime
values. Match typed variants for parameters and use codes for translation keys;
English Display messages are diagnostic prose. Wrapper errors preserve their
own stage code and expose typed causes through std::error::Error::source when
std is enabled. The source chain ends at drive::Error: its transport
variant carries the host's own error type, which only needs Debug, so match
drive::Error::Transport(e) to inspect it. Handle unknown future identifiers with a generic message.