docs: describe the generic Unlocker seam, drop in-tree firmware specifics
The docs still documented the old in-tree firmware unlocker: the MediaTek MT1959 variant table, the READ BUFFER unlock CDB bytes, the profiles.json schema (unlock_mode/unlock_buf_id/unlock_cdb), the platform/mt1959 driver listings, and the 'why unlock is needed' handshake mechanism. None of that lives in libfreemkv anymore — the core is firmware-clean and ships only the pluggable Unlocker trait + registry (src/unlock.rs). Rewrite drive-access, architecture, api-design, disc-to-rip, and the README to describe only the generic Unlocker seam: the trait, register_unlocker, the registry routing, and the host-cert fallback when no unlocker matches. Point readers to the freemkv-unlock repo for concrete unlockers. No source change.
This commit is contained in:
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
# libfreemkv
|
# libfreemkv
|
||||||
|
|
||||||
Rust library for 4K UHD / Blu-ray / DVD optical drives. Drive access, disc scanning, stream labels, AACS decryption, CSS decryption, KEYDB updates, and content reading in one crate. Bundled drive profiles — no external files needed.
|
Rust library for 4K UHD / Blu-ray / DVD optical drives. Drive access, disc scanning, stream labels, AACS decryption, CSS decryption, KEYDB updates, and content reading in one crate. Firmware-clean core: drive-unlock support is plugged in via the `Unlocker` trait, with concrete unlockers shipped as separate crates.
|
||||||
|
|
||||||
DVDs (CSS) decrypt out of the box. Blu-ray and UHD (AACS) require a `keydb.cfg` (default `~/.config/freemkv/keydb.cfg`) supplying disc-specific volume unique keys; no AACS key material is compiled in.
|
DVDs (CSS) decrypt out of the box. Blu-ray and UHD (AACS) require a `keydb.cfg` (default `~/.config/freemkv/keydb.cfg`) supplying disc-specific volume unique keys; no AACS key material is compiled in.
|
||||||
|
|
||||||
**12+ MB/s** sustained read speeds on BD. Full init: unlock, firmware upload, speed calibration — all from pure Rust.
|
**12+ MB/s** sustained read speeds on BD. Drive prep routes through the pluggable unlock seam — register an unlocker and `init()` drives it; with none registered the library rips via the host-certificate AACS handshake.
|
||||||
|
|
||||||
Multi-lingual by design — the library outputs structured data and numeric error codes, never English text. Build any UI or localization on top.
|
Multi-lingual by design — the library outputs structured data and numeric error codes, never English text. Build any UI or localization on top.
|
||||||
|
|
||||||
@@ -29,10 +29,10 @@ libfreemkv = "1.0.0-rc.1"
|
|||||||
use libfreemkv::{Drive, Disc, ScanOptions};
|
use libfreemkv::{Drive, Disc, ScanOptions};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
// Open drive — profiles are bundled, auto-identified
|
// Open drive — identified via INQUIRY
|
||||||
let mut drive = Drive::open(Path::new("/dev/sg4"))?;
|
let mut drive = Drive::open(Path::new("/dev/sg4"))?;
|
||||||
drive.wait_ready()?; // wait for disc
|
drive.wait_ready()?; // wait for disc
|
||||||
drive.init()?; // unlock + firmware upload
|
drive.init()?; // route through the unlock seam (if an unlocker is registered)
|
||||||
drive.probe_disc()?; // probe disc surface for optimal speeds
|
drive.probe_disc()?; // probe disc surface for optimal speeds
|
||||||
|
|
||||||
// Scan disc — UDF, playlists, streams, AACS (all automatic)
|
// Scan disc — UDF, playlists, streams, AACS (all automatic)
|
||||||
@@ -100,7 +100,7 @@ loop {
|
|||||||
|
|
||||||
## What It Does
|
## What It Does
|
||||||
|
|
||||||
- **Drive access** — open, identify, unlock, firmware upload, speed calibration, eject
|
- **Drive access** — open, identify, pluggable unlock seam, speed control, eject
|
||||||
- **12+ MB/s reads** — auto-detects kernel transfer limits, sustained full speed
|
- **12+ MB/s reads** — auto-detects kernel transfer limits, sustained full speed
|
||||||
- **Disc scanning** — UDF 2.50 filesystem, MPLS playlists, CLPI clip info
|
- **Disc scanning** — UDF 2.50 filesystem, MPLS playlists, CLPI clip info
|
||||||
- **Stream labels** — 5 BD-J format parsers (Paramount, Criterion, Pixelogic, CTRM, Deluxe)
|
- **Stream labels** — 5 BD-J format parsers (Paramount, Criterion, Pixelogic, CTRM, Deluxe)
|
||||||
@@ -132,10 +132,10 @@ Blu-rays and UHD (AACS) require a `keydb.cfg` at `~/.config/freemkv/keydb.cfg` (
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Drive — open, identify, init, unlock, single-shot read
|
Drive — open, identify, init, single-shot read
|
||||||
├── ScsiTransport — SG_IO (Linux), IOKit (macOS), SPTI (Windows)
|
├── ScsiTransport — SG_IO (Linux), IOKit (macOS), SPTI (Windows)
|
||||||
├── DriveProfile — per-drive unlock parameters (bundled)
|
└── Unlocker seam — pluggable trait + registry; concrete unlockers
|
||||||
└── PlatformDriver — MediaTek (supported), Renesas (planned)
|
live in the separate freemkv-unlock repo
|
||||||
|
|
||||||
Disc — scan titles, streams, AACS/CSS state
|
Disc — scan titles, streams, AACS/CSS state
|
||||||
├── UDF reader — Blu-ray UDF 2.50 with metadata partitions
|
├── UDF reader — Blu-ray UDF 2.50 with metadata partitions
|
||||||
@@ -186,7 +186,7 @@ All errors are structured with numeric codes. No user-facing English text — ap
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Run `freemkv info disc:// --share` with the [freemkv CLI](https://github.com/freemkv/freemkv) to contribute your drive's profile.
|
Run `freemkv info disc:// --share` with the [freemkv CLI](https://github.com/freemkv/freemkv) to capture your drive's identity for contribution. Drive-unlock profiles are maintained in the [freemkv-unlock](https://github.com/freemkv/freemkv-unlock) repository.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -171,7 +171,7 @@ libfreemkv/src/
|
|||||||
│ └── writeback.rs sync_file_range pipeline
|
│ └── writeback.rs sync_file_range pipeline
|
||||||
├── drive/ Drive (open, init, single-shot read)
|
├── drive/ Drive (open, init, single-shot read)
|
||||||
│ ├── mod.rs Drive struct, init, read (single-shot), reset, eject
|
│ ├── mod.rs Drive struct, init, read (single-shot), reset, eject
|
||||||
│ ├── capture.rs Drive profile capture for contribution
|
│ ├── capture.rs Raw drive SCSI capture (INQUIRY/GET_CONFIG) for contribution
|
||||||
│ ├── linux.rs Linux drive discovery
|
│ ├── linux.rs Linux drive discovery
|
||||||
│ ├── macos.rs macOS drive discovery
|
│ ├── macos.rs macOS drive discovery
|
||||||
│ └── windows.rs Windows drive discovery
|
│ └── windows.rs Windows drive discovery
|
||||||
@@ -182,7 +182,7 @@ libfreemkv/src/
|
|||||||
│ ├── mapfile.rs ddrescue-format mapfile
|
│ ├── mapfile.rs ddrescue-format mapfile
|
||||||
│ └── read_error.rs ReadCtx / ReadAction state machine
|
│ └── read_error.rs ReadCtx / ReadAction state machine
|
||||||
├── scsi/ SCSI transport (Linux SG_IO, macOS IOKit, Windows SPTI)
|
├── scsi/ SCSI transport (Linux SG_IO, macOS IOKit, Windows SPTI)
|
||||||
├── platform/ Drive unlock (MT1959 A/B)
|
├── unlock.rs Unlocker trait + registry (pluggable unlock seam)
|
||||||
├── aacs/ AACS decryption (handshake, keys, keydb, decrypt)
|
├── aacs/ AACS decryption (handshake, keys, keydb, decrypt)
|
||||||
├── css/ DVD CSS cipher
|
├── css/ DVD CSS cipher
|
||||||
├── decrypt.rs Unified decrypt dispatcher (AACS/CSS/None)
|
├── decrypt.rs Unified decrypt dispatcher (AACS/CSS/None)
|
||||||
@@ -198,7 +198,6 @@ libfreemkv/src/
|
|||||||
├── labels/ BD-J label extraction (5 format parsers)
|
├── labels/ BD-J label extraction (5 format parsers)
|
||||||
├── keydb.rs KEYDB download, parse, save
|
├── keydb.rs KEYDB download, parse, save
|
||||||
├── identity.rs DriveId from INQUIRY
|
├── identity.rs DriveId from INQUIRY
|
||||||
├── profile.rs Bundled drive profiles
|
|
||||||
├── speed.rs DriveSpeed enum
|
├── speed.rs DriveSpeed enum
|
||||||
├── mux/
|
├── mux/
|
||||||
│ ├── mod.rs Public mux exports
|
│ ├── mod.rs Public mux exports
|
||||||
|
|||||||
+22
-22
@@ -17,9 +17,10 @@ material is compiled in; DVD CSS player keys are the only compiled-in keys.
|
|||||||
format handling live in the library. CLI binaries are thin wrappers that call
|
format handling live in the library. CLI binaries are thin wrappers that call
|
||||||
`Drive::open()` and `Disc::scan()`.
|
`Drive::open()` and `Disc::scan()`.
|
||||||
|
|
||||||
2. **No external files.** Bundled drive profiles are compiled into the binary via
|
2. **Firmware-clean core.** libfreemkv ships no firmware, no unlock CDBs, and no
|
||||||
`include_str!`. No configuration directory, no runtime file lookups for drive
|
drive profiles. Drive-unlock logic is plugged in by an external crate through
|
||||||
support.
|
the `Unlocker` trait + registry (`register_unlocker`); without one the library
|
||||||
|
still rips via the host-certificate AACS handshake.
|
||||||
|
|
||||||
3. **Transparent AACS.** The `ContentReader` decrypts on the fly when keys are
|
3. **Transparent AACS.** The `ContentReader` decrypts on the fly when keys are
|
||||||
available. Callers read cleartext sectors without knowing whether the disc
|
available. Callers read cleartext sectors without knowing whether the disc
|
||||||
@@ -43,11 +44,9 @@ material is compiled in; DVD CSS player keys are the only compiled-in keys.
|
|||||||
libfreemkv (lib.rs)
|
libfreemkv (lib.rs)
|
||||||
│
|
│
|
||||||
├── Drive Access
|
├── Drive Access
|
||||||
│ ├── drive Drive — open, identify, init, unlock, single-shot read
|
│ ├── drive Drive — open, identify, init, single-shot read
|
||||||
│ ├── scsi ScsiTransport trait + platform backends (sg async, IOKit, SPTI)
|
│ ├── scsi ScsiTransport trait + platform backends (sg async, IOKit, SPTI)
|
||||||
│ ├── platform/ Platform trait — per-chipset command handlers
|
│ ├── unlock Unlocker trait + registry — the pluggable unlock seam
|
||||||
│ │ └── mt1959 MediaTek MT1959 driver (LG, ASUS, HP)
|
|
||||||
│ ├── profile DriveProfile loading, matching, bundled JSON
|
|
||||||
│ ├── identity DriveId from INQUIRY + GET_CONFIG 010C
|
│ ├── identity DriveId from INQUIRY + GET_CONFIG 010C
|
||||||
│ ├── speed DriveSpeed enum, SET CD SPEED CDB builder
|
│ ├── speed DriveSpeed enum, SET CD SPEED CDB builder
|
||||||
│ └── event Event system for drive status callbacks
|
│ └── event Event system for drive status callbacks
|
||||||
@@ -76,8 +75,7 @@ libfreemkv (lib.rs)
|
|||||||
│
|
│
|
||||||
├── Support
|
├── Support
|
||||||
│ ├── keydb KEYDB.cfg download, parse, verify, save
|
│ ├── keydb KEYDB.cfg download, parse, verify, save
|
||||||
│ ├── error Error enum with numeric codes E1000-E8000
|
│ └── error Error enum with numeric codes E1000-E8000
|
||||||
│ └── profile Bundled drive profiles
|
|
||||||
│
|
│
|
||||||
└── lib.rs Public API re-exports
|
└── lib.rs Public API re-exports
|
||||||
```
|
```
|
||||||
@@ -91,13 +89,12 @@ Drive::open(Path::new("/dev/sg4"))
|
|||||||
│
|
│
|
||||||
├─ scsi::open() Open /dev/sg4 (async write/poll/read)
|
├─ scsi::open() Open /dev/sg4 (async write/poll/read)
|
||||||
├─ DriveId::from_drive() INQUIRY + GET_CONFIG 010C
|
├─ DriveId::from_drive() INQUIRY + GET_CONFIG 010C
|
||||||
├─ profile::find_by_drive_id() Match against bundled profiles
|
└─ Drive ready for init/read
|
||||||
├─ Platform::new() Instantiate chipset driver (Mt1959)
|
|
||||||
└─ Drive ready for init/unlock/read
|
|
||||||
```
|
```
|
||||||
|
|
||||||
After open:
|
After open:
|
||||||
- `init()` -- unlock + firmware upload + speed calibration
|
- `init()` -- routes to the matching registered unlocker (if any); otherwise
|
||||||
|
a no-op and the cert handshake carries the disc
|
||||||
- `probe_disc()` -- probe disc surface for optimal speeds
|
- `probe_disc()` -- probe disc surface for optimal speeds
|
||||||
- `read(lba, count, buf, recovery)` -- single-shot read; `recovery` only selects the per-CDB timeout (1.5 s vs. 30 s)
|
- `read(lba, count, buf, recovery)` -- single-shot read; `recovery` only selects the per-CDB timeout (1.5 s vs. 30 s)
|
||||||
- `wait_ready()` -- wait for disc insertion
|
- `wait_ready()` -- wait for disc insertion
|
||||||
@@ -197,17 +194,20 @@ implementing `execute()` for that OS and wiring it into `scsi::open()`.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Chipset Support
|
## Drive Unlock
|
||||||
|
|
||||||
| Chipset | Drives | Status |
|
libfreemkv carries no drive-unlock mechanism. The `Unlocker` trait + registry
|
||||||
|---------|--------|--------|
|
(`src/unlock.rs`) is the seam: an external crate implements `Unlocker` and
|
||||||
| MediaTek MT1959 | LG, ASUS, HP | Supported (bundled profiles) |
|
registers it once via `register_unlocker(...)`. At drive-prep the registry is
|
||||||
| Renesas RS8xxx/RS9xxx | Pioneer, some HL-DT-ST | Planned |
|
walked in order and the first unlocker whose `matches()` is true is asked to
|
||||||
|
`unlock_drive()` over the raw `ScsiTransport`. If none match, the drive is left
|
||||||
|
untouched and the host-certificate AACS handshake carries the disc.
|
||||||
|
|
||||||
The `Platform` trait abstracts chipset-specific commands. Each chipset implements
|
The implementor owns everything firmware-specific — drive profiles, vendor CDBs,
|
||||||
handlers (unlock, config, register, calibrate, keepalive, status, probe,
|
variant logic. Concrete unlockers live in the separate
|
||||||
read_sectors, timing). All handlers are accessed via SCSI READ BUFFER with
|
**[freemkv-unlock](https://github.com/freemkv/freemkv-unlock)** repository, never
|
||||||
chipset-specific mode and buffer ID bytes.
|
in libfreemkv. See [`drive-access.md`](drive-access.md#drive-unlock-seam) for the
|
||||||
|
trait definition and routing.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -10,14 +10,14 @@ Insert disc
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
1. Open drive (drive/mod.rs)
|
1. Open drive (drive/mod.rs)
|
||||||
│ INQUIRY → identify drive
|
│ INQUIRY → identify drive (DriveId)
|
||||||
│ Match bundled profile → chipset, unlock parameters
|
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
2. Init drive (drive/mod.rs → platform/mt1959)
|
2. Init drive (drive/mod.rs → unlock seam)
|
||||||
│ Firmware upload (if needed, 10s recovery wait)
|
│ Walk the registered-unlocker registry; first match unlocks the drive
|
||||||
│ Unlock → vendor-specific command activates raw read mode
|
│ (firmware/vendor handshakes are the unlocker's own business)
|
||||||
│ Speed calibration → probe_disc()
|
│ No match → drive untouched; host-cert AACS handshake carries the disc
|
||||||
|
│ Speed control → probe_disc()
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
3. AACS handshake (aacs/handshake.rs) — optional
|
3. AACS handshake (aacs/handshake.rs) — optional
|
||||||
|
|||||||
+63
-89
@@ -7,8 +7,9 @@ optical drives.
|
|||||||
|
|
||||||
## Drive
|
## Drive
|
||||||
|
|
||||||
`Drive` is the primary API. It owns the SCSI transport, the matched
|
`Drive` is the primary API. It owns the SCSI transport and the drive
|
||||||
drive profile, and the chipset-specific platform driver.
|
identity (`DriveId`); any drive-specific unlock logic lives behind the
|
||||||
|
pluggable [unlock seam](#drive-unlock-seam), not in `Drive` itself.
|
||||||
|
|
||||||
### Opening a Drive
|
### Opening a Drive
|
||||||
|
|
||||||
@@ -16,15 +17,16 @@ drive profile, and the chipset-specific platform driver.
|
|||||||
let mut drive = Drive::open(Path::new("/dev/sg4"))?;
|
let mut drive = Drive::open(Path::new("/dev/sg4"))?;
|
||||||
```
|
```
|
||||||
|
|
||||||
`open()` performs: open device → send INQUIRY → match profile → instantiate
|
`open()` performs: open device → send INQUIRY → build `DriveId`. The drive
|
||||||
platform driver. The drive is ready for `wait_ready()` and `init()`.
|
is ready for `wait_ready()` and `init()` (which routes through the unlock
|
||||||
|
seam).
|
||||||
|
|
||||||
### Drive Operations
|
### Drive Operations
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `wait_ready()` | Wait for disc insertion (30s timeout, TUR polling) |
|
| `wait_ready()` | Wait for disc insertion (30s timeout, TUR polling) |
|
||||||
| `init()` | Firmware upload + unlock + speed calibration |
|
| `init()` | Route to the matching registered unlocker (if any), then prepare for reads |
|
||||||
| `probe_disc()` | Probe disc surface for optimal speeds |
|
| `probe_disc()` | Probe disc surface for optimal speeds |
|
||||||
| `read(lba, count, buf, recovery)` | Read sectors. Single-shot — no inline retries or reset. |
|
| `read(lba, count, buf, recovery)` | Read sectors. Single-shot — no inline retries or reset. |
|
||||||
| `reset()` | Eject-cycle escape hatch. Caller-invoked only; not on the read path. |
|
| `reset()` | Eject-cycle escape hatch. Caller-invoked only; not on the read path. |
|
||||||
@@ -32,17 +34,21 @@ platform driver. The drive is ready for `wait_ready()` and `init()`.
|
|||||||
| `unlock_tray()` | Allow tray ejection (also runs on Drop) |
|
| `unlock_tray()` | Allow tray ejection (also runs on Drop) |
|
||||||
| `eject()` | Eject disc tray |
|
| `eject()` | Eject disc tray |
|
||||||
| `drive_status()` | Query physical state (disc present, tray open, etc.) |
|
| `drive_status()` | Query physical state (disc present, tray open, etc.) |
|
||||||
| `has_profile()` | Whether a bundled profile matched |
|
| `has_profile()` | Whether a registered unlocker matches this drive |
|
||||||
| `close()` | Consume Drive, cleanup (also runs via Drop) |
|
| `close()` | Consume Drive, cleanup (also runs via Drop) |
|
||||||
|
|
||||||
### init() Sequence
|
### init() Sequence
|
||||||
|
|
||||||
`init()` orchestrates the full drive unlock:
|
`init()` routes drive preparation through the unlock seam:
|
||||||
|
|
||||||
1. Platform driver `run_init()` — sends vendor-specific SCSI commands
|
1. Walk the registered-unlocker registry; the first whose `matches()` is true
|
||||||
2. If firmware upload needed: upload, wait 10s for drive reset, retry
|
is asked to `unlock_drive()` over the raw transport.
|
||||||
3. Speed calibration after unlock
|
2. Whatever that unlocker needs (firmware upload, vendor handshakes, retries)
|
||||||
4. Max 3 attempts before giving up
|
is the unlocker's own business — libfreemkv only forwards the transport.
|
||||||
|
3. If no unlocker matches, the drive is left untouched and the library uses
|
||||||
|
the host-certificate AACS handshake.
|
||||||
|
|
||||||
|
See [Drive Unlock Seam](#drive-unlock-seam) for the trait and registry.
|
||||||
|
|
||||||
### read() — single-shot
|
### read() — single-shot
|
||||||
|
|
||||||
@@ -164,100 +170,68 @@ date for drives where Feature 010C is unavailable.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Drive Profiles
|
## Drive Unlock Seam
|
||||||
|
|
||||||
Profiles are JSON objects compiled into the binary (`profiles.json`).
|
libfreemkv ships **no firmware, no unlock CDBs, and no drive profiles.** It
|
||||||
Each profile contains:
|
knows only the *seam*, never the *mechanism*. The seam is the `Unlocker`
|
||||||
|
trait plus a small process-wide registry (`src/unlock.rs`):
|
||||||
| Field | Purpose |
|
|
||||||
|-------|---------|
|
|
||||||
| `vendor_id`, `product_revision`, `vendor_specific`, `firmware_date` | Matching fields |
|
|
||||||
| `chipset` | `"mediatek"` or `"renesas"` |
|
|
||||||
| `unlock_mode`, `unlock_buf_id` | READ BUFFER CDB parameters |
|
|
||||||
| `signature` | Expected 4-byte response signature |
|
|
||||||
| `unlock_cdb` | Pre-built unlock CDB (hex-encoded) |
|
|
||||||
| `register_offsets` | Offsets for hardware register reads |
|
|
||||||
| `capabilities` | Feature flags: `bd_raw_read`, `dvd_all_regions`, etc. |
|
|
||||||
|
|
||||||
Loading:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Bundled (compiled-in) -- no file I/O
|
pub trait Unlocker: Send + Sync {
|
||||||
let profiles = profile::load_bundled()?;
|
/// Stable, language-neutral identifier (logged).
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
// External file
|
/// True if this unlocker handles the given drive.
|
||||||
let profiles = profile::load_all(Path::new("/path/to/profiles.json"))?;
|
fn matches(&self, id: &DriveId) -> bool;
|
||||||
|
|
||||||
|
/// Put the drive into extended-access mode. The one required capability.
|
||||||
|
fn unlock_drive(&self, scsi: &mut dyn ScsiTransport, id: &DriveId) -> Result<()>;
|
||||||
|
|
||||||
|
/// Read the disc Volume ID via the drive's OEM path. Default: no-op.
|
||||||
|
fn read_volume_id(&self, _scsi: &mut dyn ScsiTransport, _id: &DriveId)
|
||||||
|
-> Result<Option<[u8; 16]>> { Ok(None) }
|
||||||
|
|
||||||
|
/// Raise the drive to its maximum read speed. Default: no-op.
|
||||||
|
fn set_max_read_speed(&self, _scsi: &mut dyn ScsiTransport, _id: &DriveId)
|
||||||
|
-> Result<()> { Ok(()) }
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
An unlocker is supplied by an **external crate** and registered once at
|
||||||
|
process start:
|
||||||
|
|
||||||
## Chipsets
|
```rust
|
||||||
|
libfreemkv::register_unlocker(Box::new(some_unlocker::Plugin::new()));
|
||||||
|
```
|
||||||
|
|
||||||
### MediaTek MT1959
|
The implementor owns everything about *how* a particular drive family is
|
||||||
|
driven — drive identification against its own profile database, firmware
|
||||||
|
upload, vendor CDBs, variant logic. libfreemkv only hands over the raw
|
||||||
|
`ScsiTransport` and the `DriveId`.
|
||||||
|
|
||||||
Covers all LG, ASUS, and HP optical drives. Two sub-variants share identical
|
### Routing
|
||||||
logic with different SCSI parameters:
|
|
||||||
|
|
||||||
| Variant | READ BUFFER mode | Buffer ID |
|
At drive-prep the registry is walked in registration order; the first
|
||||||
|---------|------------------|-----------|
|
unlocker whose `matches()` returns true is asked to `unlock_drive()` (and,
|
||||||
| MT1959-A | 0x01 | 0x44 |
|
when needed, `read_volume_id()` / `set_max_read_speed()`). If no unlocker
|
||||||
| MT1959-B | 0x02 | 0x77 |
|
matches, the drive is left untouched and the library falls back to the
|
||||||
|
standard host-certificate AACS handshake (the "OEM route"). The
|
||||||
|
`register_unlocker(...)` line is the entire plug: drop it (and the unlocker
|
||||||
|
crate) and libfreemkv still compiles and rips via the cert handshake.
|
||||||
|
|
||||||
The Platform trait maps to command handlers:
|
Concrete unlockers — including the firmware-unlock profile databases,
|
||||||
|
variant logic, and vendor CDBs that used to live in-tree — are maintained
|
||||||
| Handler | Function | Description |
|
in the separate **[freemkv-unlock](https://github.com/freemkv/freemkv-unlock)**
|
||||||
|---------|----------|-------------|
|
repository, never here.
|
||||||
| 0 | `unlock()` | Send READ BUFFER, verify signature + verification bytes |
|
|
||||||
| 1 | `read_config()` | Read 1888-byte configuration block + 4-byte status |
|
|
||||||
| 2-3 | `read_register()` | Read hardware registers at profile-specified offsets |
|
|
||||||
| 4 | `calibrate()` | Probe disc surface, build 64-entry speed table |
|
|
||||||
| 5 | `keepalive()` | Periodic session maintenance |
|
|
||||||
| 6 | `status()` | Query current mode and feature flags |
|
|
||||||
| 7 | `probe()` | Generic READ BUFFER with dynamic parameters |
|
|
||||||
| 8 | `read_sectors()` | Speed lookup + SET CD SPEED + READ(10) with flag 0x08 |
|
|
||||||
| 9 | `timing()` | Timing calibration |
|
|
||||||
|
|
||||||
### Renesas (Planned)
|
|
||||||
|
|
||||||
RS8xxx/RS9xxx chipsets used in Pioneer and some HL-DT-ST drives.
|
|
||||||
Currently returns `Error::UnsupportedDrive` when a Renesas profile is matched.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why Unlock Is Needed
|
|
||||||
|
|
||||||
Optical drive firmware restricts what applications can read from disc. Without
|
|
||||||
unlock:
|
|
||||||
|
|
||||||
- **READ(10) works for unencrypted filesystem data.** UDF structures, MPLS
|
|
||||||
playlists, and CLPI clip info are readable without unlock. Standard READ(10)
|
|
||||||
works on any drive.
|
|
||||||
|
|
||||||
- **READ(10) fails for encrypted content sectors.** The drive firmware returns
|
|
||||||
SCSI errors (sense key 0x05, illegal request) when an application attempts to
|
|
||||||
read sectors containing encrypted m2ts content without prior AACS
|
|
||||||
authentication via the bus key.
|
|
||||||
|
|
||||||
- **Raw mode bypasses firmware restrictions.** After unlock, the drive accepts
|
|
||||||
READ(10) with the raw read flag (CDB byte 1 = 0x08) for all sectors,
|
|
||||||
regardless of encryption status.
|
|
||||||
|
|
||||||
### AACS Before Unlock
|
|
||||||
|
|
||||||
AACS bus authentication uses standard MMC REPORT KEY / SEND KEY commands.
|
|
||||||
On some drives these must execute before unlock. The `Disc::scan()` handles
|
|
||||||
this internally — it manages the handshake/unlock ordering automatically.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Speed Control
|
## Speed Control
|
||||||
|
|
||||||
After `probe_disc()`, the platform driver maintains a speed lookup table
|
A matching unlocker may raise the drive to its maximum read speed via
|
||||||
built by probing the disc surface. On each `read()` call, the driver:
|
`set_max_read_speed()` (a no-op when no unlocker matches or the unlocker
|
||||||
|
declines). The library issues SET CD SPEED (0xBB) through the generic CDB
|
||||||
1. Looks up the optimal speed for the target LBA.
|
builder; the concrete speed policy lives in the unlocker.
|
||||||
2. Issues SET CD SPEED (0xBB) if the speed differs from current.
|
|
||||||
3. Performs the READ(10).
|
|
||||||
|
|
||||||
Available speeds:
|
Available speeds:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user