The NetMD command set, ported from netmd-js.
Every command is a query built with NetMD.Query, sent through
NetMD.Device with a status byte prepended, and a reply parsed the same
way. Replies carry an AV/C response status: accepted, implemented
and late interim responses succeed; not_implemented and rejected
become error tuples; interim is retried with backoff.
Track numbers are zero-based, as in the reference implementations.
Summary
Functions
Take exclusive control of the device.
Whether the device supports ejecting by command.
Open or close one of the device's descriptors. Errors are ignored, as in the reference.
Commit a downloaded track (zero-based), proving session knowledge.
Enable or disable protection on newly recorded tracks.
Disc capacity as three {hours, minutes, seconds, frames} tuples:
recorded, total and available.
Disc flags byte: 0x10 writable, 0x40 write-protected.
Whether a disc is loaded.
The disc title with group markup stripped.
Eject the disc.
Switch a Hi-MD capable device to Hi-MD mode.
Enter the secure session used for track download.
Erase the whole disc.
Erase a track (zero-based).
Fast-forward.
Status mode and operating status number. Does not work on all devices.
Seek to a position within a track.
Seek to the beginning of a track (zero-based).
The device's DRM leaf ID.
Leave the secure session.
Move a track to another position (both zero-based).
The device's NetMD level from its subunit identifier: 0x20 (network),
0x50 (program play) or 0x70 (editing).
Skip to the next track.
Operating status number, see full_operating_status/1.
Pause playback.
Start playback.
First playback status block.
Second playback status block.
Current playback position: {:ok, %{track: n, time: {hours, minutes, seconds, frames}}}, or {:ok, nil} when the device rejects the query
(no disc, for instance).
Skip to the previous track.
The raw disc title string, including group markup like 0;Title//.
Read and check a reply, retrying interim responses with backoff.
Recording parameters: {encoding, channel} bytes for the current mode.
Release exclusive control of the device.
Restart the current track.
Rewind.
Read a track's raw audio data off the disc. Only the MZ-RH1 (and M200) supports this. Returns the disc format byte, frame count and data.
Send a query without reading the reply.
Send an enabling key block to the device.
Send a query and read its parsed reply payload (status byte stripped).
Stream encrypted track packets to the device.
Exchange nonces with the device; returns the device nonce.
Make the device forget the negotiated session key.
Set the raw disc title. The title is sanitized; group markup must
already be in place (see NetMD.Commands.rename_disc/3 for the
friendly version).
Set the title of a track (zero-based). The title is sanitized.
Announce a track download, passing the content ID and key encryption key encrypted with the 8-byte session key.
Raw status block.
Stop playback. Errors are ignored, as in the reference (LAM-1 fix).
Terminate the secure session state machine.
Number of tracks on the disc.
Track codec and channel bytes: codec 0x90 SP, 0x92 LP2, 0x93 LP4;
channel 0x00 stereo, 0x01 mono.
Track flags byte: 0x03 protected, 0x00 unprotected (zero-based).
Groups on the disc as {name, full_width_name, tracks} tuples, with
nil names for the ungrouped tracks entry. Track numbers zero-based.
Track length as {hours, minutes, seconds, frames} (zero-based track).
Title of a track (zero-based).
UUID of a track (zero-based) as raw bytes.
Types
@type error() :: {:error, term()}
Functions
@spec acquire(NetMD.Device.t()) :: :ok | error()
Take exclusive control of the device.
@spec can_eject_disc?(NetMD.Device.t()) :: boolean()
Whether the device supports ejecting by command.
@spec change_descriptor_state( NetMD.Device.t(), descriptor :: atom(), action :: :open_read | :open_write | :close ) :: :ok
Open or close one of the device's descriptors. Errors are ignored, as in the reference.
@spec commit_track(NetMD.Device.t(), non_neg_integer(), session_key :: <<_::64>>) :: :ok | error()
Commit a downloaded track (zero-based), proving session knowledge.
@spec disable_new_track_protection(NetMD.Device.t(), 0..65535) :: :ok | error()
Enable or disable protection on newly recorded tracks.
@spec disc_capacity(NetMD.Device.t()) :: {:ok, [{non_neg_integer(), byte(), byte(), byte()}]} | error()
Disc capacity as three {hours, minutes, seconds, frames} tuples:
recorded, total and available.
@spec disc_flags(NetMD.Device.t()) :: {:ok, byte()} | error()
Disc flags byte: 0x10 writable, 0x40 write-protected.
@spec disc_present?(NetMD.Device.t()) :: {:ok, boolean()} | error()
Whether a disc is loaded.
@spec disc_title( NetMD.Device.t(), keyword() ) :: {:ok, String.t()} | error()
The disc title with group markup stripped.
@spec eject_disc(NetMD.Device.t()) :: :ok | error()
Eject the disc.
@spec enter_himd_mode(NetMD.Device.t()) :: :ok | error()
Switch a Hi-MD capable device to Hi-MD mode.
@spec enter_secure_session(NetMD.Device.t()) :: :ok | error()
Enter the secure session used for track download.
@spec erase_disc(NetMD.Device.t()) :: :ok | error()
Erase the whole disc.
@spec erase_track(NetMD.Device.t(), non_neg_integer()) :: :ok | error()
Erase a track (zero-based).
@spec fast_forward(NetMD.Device.t()) :: :ok | error()
Fast-forward.
@spec full_operating_status(NetMD.Device.t()) :: {:ok, %{status_mode: byte(), operating_status: 0..65535}} | error()
Status mode and operating status number. Does not work on all devices.
@spec goto_time(NetMD.Device.t(), non_neg_integer(), keyword()) :: :ok | error()
Seek to a position within a track.
@spec goto_track(NetMD.Device.t(), non_neg_integer()) :: {:ok, non_neg_integer()} | error()
Seek to the beginning of a track (zero-based).
@spec leaf_id(NetMD.Device.t()) :: {:ok, binary()} | error()
The device's DRM leaf ID.
@spec leave_secure_session(NetMD.Device.t()) :: :ok | error()
Leave the secure session.
@spec move_track(NetMD.Device.t(), non_neg_integer(), non_neg_integer()) :: :ok | error()
Move a track to another position (both zero-based).
@spec netmd_level(NetMD.Device.t()) :: {:ok, byte()} | error()
The device's NetMD level from its subunit identifier: 0x20 (network),
0x50 (program play) or 0x70 (editing).
@spec next_track(NetMD.Device.t()) :: :ok | error()
Skip to the next track.
@spec operating_status(NetMD.Device.t()) :: {:ok, 0..65535} | error()
Operating status number, see full_operating_status/1.
@spec pause(NetMD.Device.t()) :: :ok | error()
Pause playback.
@spec play(NetMD.Device.t()) :: :ok | error()
Start playback.
@spec playback_status1(NetMD.Device.t()) :: {:ok, binary()} | error()
First playback status block.
@spec playback_status2(NetMD.Device.t()) :: {:ok, binary()} | error()
Second playback status block.
@spec position(NetMD.Device.t()) :: {:ok, %{track: non_neg_integer(), time: {byte(), byte(), byte(), byte()}} | nil} | error()
Current playback position: {:ok, %{track: n, time: {hours, minutes, seconds, frames}}}, or {:ok, nil} when the device rejects the query
(no disc, for instance).
@spec previous_track(NetMD.Device.t()) :: :ok | error()
Skip to the previous track.
@spec raw_disc_title( NetMD.Device.t(), keyword() ) :: {:ok, String.t()} | error()
The raw disc title string, including group markup like 0;Title//.
@spec read_reply( NetMD.Device.t(), keyword() ) :: {:ok, binary()} | error()
Read and check a reply, retrying interim responses with backoff.
@spec recording_parameters(NetMD.Device.t()) :: {:ok, {byte(), byte()}} | error()
Recording parameters: {encoding, channel} bytes for the current mode.
@spec release(NetMD.Device.t()) :: :ok | error()
Release exclusive control of the device.
@spec restart_track(NetMD.Device.t()) :: :ok | error()
Restart the current track.
@spec rewind(NetMD.Device.t()) :: :ok | error()
Rewind.
@spec save_track_to_binary(NetMD.Device.t(), non_neg_integer(), keyword()) :: {:ok, %{format: byte(), frames: non_neg_integer(), data: binary()}} | error()
Read a track's raw audio data off the disc. Only the MZ-RH1 (and M200) supports this. Returns the disc format byte, frame count and data.
Options are passed through to NetMD.Device.read_bulk/3 (:chunk_size,
:progress, :timeout).
@spec send_command(NetMD.Device.t(), binary(), keyword()) :: :ok | error()
Send a query without reading the reply.
@spec send_key_data(NetMD.Device.t(), NetMD.EKB.t()) :: :ok | error()
Send an enabling key block to the device.
@spec send_query(NetMD.Device.t(), binary(), keyword()) :: {:ok, binary()} | error()
Send a query and read its parsed reply payload (status byte stripped).
Options:
test?: true- send as a specific inquiry instead of a controlaccept_interim?: true- return an interim response instead of retryingfactory?: true- use the factory command set (request0xff)
@spec send_track( NetMD.Device.t(), wireformat :: byte(), discformat :: byte(), frames :: pos_integer(), packet_size :: pos_integer(), packets :: Enumerable.t(), session_key :: <<_::64>>, keyword() ) :: {:ok, %{track: non_neg_integer(), uuid: binary(), ccid: binary()}} | error()
Stream encrypted track packets to the device.
packets is an enumerable of {key, iv, data} tuples as produced by
NetMD.Track.packets/1. Returns the new track number and the
decrypted UUID and content ID the device reports.
Options:
:progress- function called with(total_bytes, written_bytes):settle_ms- wait before and after announcing (default 200, the reference's allowance for slow Sharp devices)
@spec session_key_exchange(NetMD.Device.t(), host_nonce :: <<_::64>>) :: {:ok, binary()} | error()
Exchange nonces with the device; returns the device nonce.
@spec session_key_forget(NetMD.Device.t()) :: :ok | error()
Make the device forget the negotiated session key.
@spec set_disc_title(NetMD.Device.t(), String.t(), keyword()) :: :ok | error()
Set the raw disc title. The title is sanitized; group markup must
already be in place (see NetMD.Commands.rename_disc/3 for the
friendly version).
@spec set_track_title(NetMD.Device.t(), non_neg_integer(), String.t(), keyword()) :: :ok | error()
Set the title of a track (zero-based). The title is sanitized.
@spec setup_download( NetMD.Device.t(), content_id :: <<_::160>>, key_encryption_key :: <<_::64>>, session_key :: <<_::64>> ) :: :ok | error()
Announce a track download, passing the content ID and key encryption key encrypted with the 8-byte session key.
@spec status(NetMD.Device.t()) :: {:ok, binary()} | error()
Raw status block.
@spec stop(NetMD.Device.t()) :: :ok
Stop playback. Errors are ignored, as in the reference (LAM-1 fix).
@spec terminate(NetMD.Device.t()) :: :ok | error()
Terminate the secure session state machine.
@spec track_count(NetMD.Device.t()) :: {:ok, non_neg_integer()} | error()
Number of tracks on the disc.
@spec track_encoding(NetMD.Device.t(), non_neg_integer()) :: {:ok, {codec :: byte(), channel :: byte()}} | error()
Track codec and channel bytes: codec 0x90 SP, 0x92 LP2, 0x93 LP4;
channel 0x00 stereo, 0x01 mono.
@spec track_flags(NetMD.Device.t(), non_neg_integer()) :: {:ok, byte()} | error()
Track flags byte: 0x03 protected, 0x00 unprotected (zero-based).
@spec track_group_list(NetMD.Device.t()) :: {:ok, [{String.t() | nil, String.t() | nil, [non_neg_integer()]}]} | error()
Groups on the disc as {name, full_width_name, tracks} tuples, with
nil names for the ungrouped tracks entry. Track numbers zero-based.
@spec track_length(NetMD.Device.t(), non_neg_integer()) :: {:ok, {byte(), byte(), byte(), byte()}} | error()
Track length as {hours, minutes, seconds, frames} (zero-based track).
@spec track_title(NetMD.Device.t(), non_neg_integer(), keyword()) :: {:ok, String.t()} | error()
Title of a track (zero-based).
@spec track_uuid(NetMD.Device.t(), non_neg_integer()) :: {:ok, binary()} | error()
UUID of a track (zero-based) as raw bytes.