The fundamental NetMD USB exchange protocol, ported from netmd-js.
Commands go out as vendor control transfers; replies are fetched by
polling the device for a reply length and then reading that many bytes.
Audio data moves over the bulk endpoints. This layer carries raw
payloads only; NetMD.Interface gives them meaning.
Summary
Functions
Release the device.
Quirk flags from the known device table.
List the connected NetMD devices without opening any of them.
Display name from the known device table.
Open a NetMD device and drain any leftover reply from a previous session.
Read length bytes from the bulk endpoint in :chunk_size chunks
(default 0x10000). An optional :progress function is called with
(total, read_so_far) after each chunk.
Read the reply to the last command, polling until the device has one.
Poll the length of the pending reply. {:ok, 0} means no reply yet.
Send a raw command payload. Set factory?: true for the factory command
set (only do this if you know what you are doing).
Subscribe pid (default the caller) to {:netmd_status, status} events.
Stop pid (default the caller) receiving status events.
Run fun holding the device lock, if the transport has one.
Write data to the bulk OUT endpoint.
Types
@type listing() :: %{ vendor_id: 0..65535, product_id: 0..65535, name: String.t(), flags: NetMD.Devices.flags(), bus: pos_integer() | nil, address: pos_integer() | nil }
A connected NetMD device, as reported by list/1.
@type t() :: %NetMD.Device{ handle: NetMD.Transport.handle(), max_polls: pos_integer(), poll_interval_ms: non_neg_integer(), product_id: 0..65535, transport: module(), vendor_id: 0..65535 }
An open NetMD device.
Functions
@spec close(t()) :: :ok
Release the device.
@spec flags(t()) :: NetMD.Devices.flags()
Quirk flags from the known device table.
List the connected NetMD devices without opening any of them.
Each entry carries its USB ids, bus location and the display name and quirk
flags from the known device table. Multiple identical models are told apart
by their :bus and :address. Enumeration does not fail; an empty list means
nothing is connected.
Options:
:transport-NetMD.Transportimplementation, defaults toNetMD.Transport.Usb
Remaining options are passed to the transport. Raises ArgumentError for a
transport that cannot enumerate devices.
Display name from the known device table.
Open a NetMD device and drain any leftover reply from a previous session.
Options:
:reconnect- front the device withNetMD.Transport.Managedso it survives the USB re-enumeration that a disc insert/eject or session reset triggers, keeping the same handle across it (defaulttrue). Passing an explicit:transporttakes precedence and disables the wrapper. SeeNetMD.Transport.Managedfor:reconnect_waitand friends.:transport-NetMD.Transportimplementation, defaults toNetMD.Transport.Usb(wrapped per:reconnectabove):vendor_id,:product_id- open a specific device instead of the first known one:status_event_poll- with the managed transport (the default), ms between background status polls that drivesubscribe/2events, orfalseto disable (default1000)
Remaining options are passed to the transport.
@spec read_bulk(t(), non_neg_integer(), keyword()) :: {:ok, binary()} | {:error, term()}
Read length bytes from the bulk endpoint in :chunk_size chunks
(default 0x10000). An optional :progress function is called with
(total, read_so_far) after each chunk.
Read the reply to the last command, polling until the device has one.
Polling backs off exponentially from poll_interval_ms and gives up
after max_polls attempts with {:error, :no_reply}. Options:
factory?: true- read a factory command replylength: n- skip polling and read exactlynbytes
@spec reply_length(t()) :: {:ok, non_neg_integer()} | {:error, term()}
Poll the length of the pending reply. {:ok, 0} means no reply yet.
Send a raw command payload. Set factory?: true for the factory command
set (only do this if you know what you are doing).
Subscribe pid (default the caller) to {:netmd_status, status} events.
Only the managed transport polls status; other transports return
{:error, :status_events_unavailable}.
Stop pid (default the caller) receiving status events.
@spec with_lock(t(), (-> result)) :: result when result: var
Run fun holding the device lock, if the transport has one.
The managed transport serialises the whole exchange against the status poller
and any other holder; the lock is reentrant per process. Transports without a
lock (bare USB, simulator, tests) just run fun.
Write data to the bulk OUT endpoint.