1use std::ffi::c_char;
17use std::net::Ipv4Addr;
18
19use mx_remote::{
20 AmpDolbySettings, AudioEndpoint, DeviceV2ipDetails, DeviceV2ipSink, FirmwareVersion,
21 MultiviewerStatus, NetworkPortStatus, RcSettings, StreamKind, TopologyEntry, UtpCableStatus,
22 V2ipDeviceStats, V2ipRxStats, V2ipStreamSource, V2ipStreamSources, V2ipTilingConfig,
23 V2ipTxStats, VctStatus, MULTIVIEWER_INPUTS,
24};
25
26use crate::abi::{fail, guard, mxr_result_t, mxr_uid_t, put_str};
27use crate::control::mxr_audio_format_t;
28use crate::info::{copy_into, not_heard_from, null_out, MXR_NAME_LEN, MXR_VERSION_LEN};
29use crate::remote::{mxr_remote_t, with, MXR_IP_STRING_LEN};
30
31pub const MXR_MULTIVIEWER_INPUTS: usize = 4;
36
37const _: () = assert!(MXR_MULTIVIEWER_INPUTS == MULTIVIEWER_INPUTS);
38
39pub const MXR_UTP_PAIRS: usize = 4;
41
42#[repr(i32)]
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum mxr_stream_kind_t {
46 MXR_STREAM_VIDEO = 0,
48 MXR_STREAM_AUDIO = 1,
50 MXR_STREAM_ANC = 2,
52 MXR_STREAM_ARC = 3,
54}
55
56impl From<StreamKind> for mxr_stream_kind_t {
57 fn from(kind: StreamKind) -> Self {
58 match kind {
59 StreamKind::Video => Self::MXR_STREAM_VIDEO,
60 StreamKind::Audio => Self::MXR_STREAM_AUDIO,
61 StreamKind::Anc => Self::MXR_STREAM_ANC,
62 StreamKind::Arc => Self::MXR_STREAM_ARC,
63 }
64 }
65}
66
67#[repr(C)]
69#[derive(Clone, Copy)]
70pub struct mxr_stream_source_t {
71 pub kind: mxr_stream_kind_t,
73 pub ip: [c_char; MXR_IP_STRING_LEN],
75 pub port: u16,
77 pub valid: bool,
81}
82
83impl From<V2ipStreamSource> for mxr_stream_source_t {
84 fn from(s: V2ipStreamSource) -> Self {
85 let mut out = Self {
86 kind: s.kind.into(),
87 ip: [0; MXR_IP_STRING_LEN],
88 port: s.port,
89 valid: s.is_valid(),
90 };
91 put_str(&mut out.ip, &s.ip.to_string());
92 out
93 }
94}
95
96#[repr(C)]
98#[derive(Clone, Copy)]
99pub struct mxr_stream_sources_t {
100 pub uid: mxr_uid_t,
102 pub video: mxr_stream_source_t,
104 pub audio: mxr_stream_source_t,
106 pub anc: mxr_stream_source_t,
108 pub has_arc: bool,
110 pub arc: mxr_stream_source_t,
112}
113
114impl From<V2ipStreamSources> for mxr_stream_sources_t {
115 fn from(s: V2ipStreamSources) -> Self {
116 Self {
117 uid: s.uid.into(),
118 video: s.video.into(),
119 audio: s.audio.into(),
120 anc: s.anc.into(),
121 has_arc: s.arc.is_some(),
122 arc: s.arc.unwrap_or_default().into(),
123 }
124 }
125}
126
127#[repr(C)]
129#[derive(Clone, Copy)]
130pub struct mxr_v2ip_details_t {
131 pub video: mxr_stream_source_t,
133 pub audio: mxr_stream_source_t,
135 pub anc: mxr_stream_source_t,
137 pub arc: mxr_stream_source_t,
139 pub tx_rate: i16,
141 pub dscp_video: i16,
143 pub dscp_audio: i16,
145 pub dscp_anc: i16,
147 pub scaling_mode: u16,
149 pub scaling_refresh: u16,
151 pub scaling_flags: u8,
155}
156
157pub const MXR_SCALING_FLAG_MODE_VALID: u8 = 1 << 0;
159pub const MXR_SCALING_FLAG_OPTIONS_VALID: u8 = 1 << 1;
161pub const MXR_SCALING_FLAG_AUTO_SCALING: u8 = 1 << 7;
163
164#[repr(C)]
166#[derive(Clone, Copy)]
167pub struct mxr_v2ip_sink_t {
168 pub addresses: mxr_stream_sources_t,
170 pub has_audio_format: bool,
172 pub audio_format: mxr_audio_format_t,
174}
175
176#[repr(C)]
178#[derive(Clone, Copy)]
179pub struct mxr_v2ip_tx_stats_t {
180 pub video: u32,
182 pub audio: u32,
184 pub anc: u32,
186 pub stream_down: u32,
188 pub overflow: u32,
190}
191
192impl From<V2ipTxStats> for mxr_v2ip_tx_stats_t {
193 fn from(s: V2ipTxStats) -> Self {
194 Self {
195 video: s.video,
196 audio: s.audio,
197 anc: s.anc,
198 stream_down: s.stream_down,
199 overflow: s.overflow,
200 }
201 }
202}
203
204#[repr(C)]
206#[derive(Clone, Copy)]
207pub struct mxr_v2ip_rx_stats_t {
208 pub video_total: u32,
210 pub video_dropped: u32,
212 pub video_seq_errors: u32,
214 pub wdt_timeout: u32,
216 pub audio_total: u32,
218 pub audio_dropped: u32,
220 pub audio_seq_errors: u32,
222 pub anc_total: u32,
224 pub anc_dropped: u32,
226 pub anc_seq_errors: u32,
228 pub decoder_state: u8,
234}
235
236impl From<V2ipRxStats> for mxr_v2ip_rx_stats_t {
237 fn from(s: V2ipRxStats) -> Self {
238 Self {
239 video_total: s.video_total,
240 video_dropped: s.video_dropped,
241 video_seq_errors: s.video_seq_errors,
242 wdt_timeout: s.wdt_timeout,
243 audio_total: s.audio_total,
244 audio_dropped: s.audio_dropped,
245 audio_seq_errors: s.audio_seq_errors,
246 anc_total: s.anc_total,
247 anc_dropped: s.anc_dropped,
248 anc_seq_errors: s.anc_seq_errors,
249 decoder_state: s.decoder_state.to_wire(),
250 }
251 }
252}
253
254#[repr(C)]
256#[derive(Clone, Copy)]
257pub struct mxr_v2ip_stats_t {
258 pub tx: mxr_v2ip_tx_stats_t,
260 pub tx_per_minute: mxr_v2ip_tx_stats_t,
262 pub rx: mxr_v2ip_rx_stats_t,
264 pub rx_per_minute: mxr_v2ip_rx_stats_t,
266}
267
268#[repr(C)]
275#[derive(Clone, Copy)]
276pub struct mxr_tiling_config_t {
277 pub target: mxr_uid_t,
279 pub pos_x: u16,
281 pub pos_y: u16,
283 pub width: u16,
285 pub height: u16,
287}
288
289#[repr(C)]
291#[derive(Clone, Copy)]
292pub struct mxr_multiviewer_status_t {
293 pub uid: mxr_uid_t,
295 pub mappings: [mxr_uid_t; MXR_MULTIVIEWER_INPUTS],
297 pub mcu_version: [c_char; MXR_NAME_LEN],
299 pub scaler_version: [c_char; MXR_NAME_LEN],
301 pub hw_view_mode: u8,
304 pub view_mode: u8,
306 pub pip_position: u8,
308 pub pip_size: u8,
310 pub output_mode: u8,
312 pub hdcp_mode: u8,
314 pub output_itc: u8,
316 pub edid_template: u8,
318 pub aspect_ratio: u8,
320 pub auto_switch: u8,
322 pub audio_source: u8,
324 pub has_audio_volume: bool,
326 pub audio_volume: u8,
328 pub audio_muted: u8,
330 pub video_sources: [u8; MXR_MULTIVIEWER_INPUTS],
332 pub remote_control: u8,
334}
335
336#[repr(C)]
338#[derive(Clone, Copy)]
339pub struct mxr_audio_endpoint_t {
340 pub id: u8,
342 pub features: u32,
344 pub has_address: bool,
346 pub address: mxr_stream_source_t,
348 pub parent: i16,
350 pub child_count: usize,
353 pub has_inputs_available: bool,
355 pub inputs_available: u32,
357 pub has_inputs_routed: bool,
359 pub inputs_routed: u32,
361 pub linked_device: mxr_uid_t,
363 pub linked_endpoint: i16,
365}
366
367impl From<&AudioEndpoint> for mxr_audio_endpoint_t {
368 fn from(e: &AudioEndpoint) -> Self {
369 Self {
370 id: e.id,
371 features: e.features.bits(),
372 has_address: e.address.is_some(),
373 address: e.address.unwrap_or_default().into(),
374 parent: e.parent.map_or(-1, i16::from),
376 child_count: e.children.len(),
377 has_inputs_available: e.inputs_available.is_some(),
378 inputs_available: e.inputs_available.unwrap_or(0),
379 has_inputs_routed: e.inputs_routed.is_some(),
380 inputs_routed: e.inputs_routed.unwrap_or(0),
381 linked_device: e.linked_device.into(),
382 linked_endpoint: e.linked_endpoint.map_or(-1, i16::from),
383 }
384 }
385}
386
387#[repr(C)]
389#[derive(Clone, Copy)]
390pub struct mxr_cable_status_t {
391 pub polarity: bool,
393 pub pair: u8,
395 pub skew: u32,
397 pub length: u32,
399}
400
401impl From<UtpCableStatus> for mxr_cable_status_t {
402 fn from(c: UtpCableStatus) -> Self {
403 Self {
404 polarity: c.polarity,
405 pair: c.pair,
406 skew: c.skew,
407 length: c.length,
408 }
409 }
410}
411
412#[repr(C)]
414#[derive(Clone, Copy)]
415pub struct mxr_network_port_t {
416 pub port: u16,
418 pub name: [c_char; MXR_NAME_LEN],
420 pub link_speed: u8,
422 pub link_full_duplex: bool,
424 pub ip: [c_char; MXR_IP_STRING_LEN],
426 pub querier: [c_char; MXR_IP_STRING_LEN],
428 pub has_mac_address: bool,
430 pub mac_address: [u8; 6],
432 pub has_errors: bool,
434 pub in_error: bool,
436 pub in_fcs_error: bool,
438 pub in_collision: bool,
440 pub out_deferred: bool,
442 pub out_excessive: bool,
444 pub polarity_error: bool,
446 pub skew_warning: bool,
448 pub length_warning: bool,
450 pub has_vct_status: bool,
452 pub vct_warning: [bool; MXR_UTP_PAIRS],
455 pub cable_status_count: usize,
457 pub cable_status: [mxr_cable_status_t; MXR_UTP_PAIRS],
459}
460
461#[repr(C)]
463#[derive(Clone, Copy)]
464pub struct mxr_topology_entry_t {
465 pub uid: mxr_uid_t,
467 pub mask: u32,
469}
470
471impl From<TopologyEntry> for mxr_topology_entry_t {
472 fn from(e: TopologyEntry) -> Self {
473 Self {
474 uid: e.uid.into(),
475 mask: e.mask,
476 }
477 }
478}
479
480#[repr(C)]
482#[derive(Clone, Copy)]
483pub struct mxr_firmware_version_t {
484 pub firmware_type: u8,
486 pub timestamp: u32,
488 pub hash: u32,
490 pub version: [c_char; MXR_VERSION_LEN],
492}
493
494#[repr(C)]
496#[derive(Clone, Copy)]
497pub struct mxr_dolby_settings_t {
498 pub mode: u8,
500 pub pcm_upmix: bool,
502 pub dolby_detected: bool,
504 pub pcm_upmix_active: bool,
506}
507
508impl From<AmpDolbySettings> for mxr_dolby_settings_t {
509 fn from(s: AmpDolbySettings) -> Self {
510 Self {
511 mode: s.mode,
512 pcm_upmix: s.pcm_upmix,
513 dolby_detected: s.dolby_detected,
514 pcm_upmix_active: s.pcm_upmix_active,
515 }
516 }
517}
518
519#[repr(C)]
521#[derive(Clone, Copy)]
522pub struct mxr_rc_settings_t {
523 pub target: mxr_uid_t,
525 pub rc_target: u8,
534 pub ip: [c_char; MXR_IP_STRING_LEN],
536 pub cec_enabled: bool,
538 pub cec_auto_on: bool,
540 pub forward_rc: bool,
542 pub forward_ir: bool,
544 pub rc_status: u8,
547 pub status_name: [c_char; MXR_NAME_LEN],
549}
550
551fn put_ip(dst: &mut [c_char], ip: Option<Ipv4Addr>) {
554 put_str(dst, &ip.map(|ip| ip.to_string()).unwrap_or_default());
555}
556
557unsafe fn fill<T>(
568 r: &mxr_remote_t,
569 uid: mxr_uid_t,
570 out: *mut T,
571 what: &str,
572 value: Option<T>,
573) -> mxr_result_t {
574 if out.is_null() {
575 return null_out(what);
576 }
577 match value {
578 Some(value) => {
579 unsafe { *out = value };
581 mxr_result_t::MXR_OK
582 }
583 None => not_reported(r, uid, what),
584 }
585}
586
587fn not_reported(r: &mxr_remote_t, uid: mxr_uid_t, what: &str) -> mxr_result_t {
590 if r.remote.device(uid.into()).is_none() {
591 return not_heard_from(uid);
592 }
593 fail(
594 mxr_result_t::MXR_ERR_NOT_REPORTED,
595 &format!("the device has reported no {what}"),
596 )
597}
598
599#[no_mangle]
609pub unsafe extern "C" fn mxr_v2ip_stats(
610 remote: *const mxr_remote_t,
611 uid: mxr_uid_t,
612 out: *mut mxr_v2ip_stats_t,
613) -> mxr_result_t {
614 let handle = unsafe { remote.as_ref() };
616 with(handle, |r| {
617 let value = r
618 .remote
619 .v2ip_stats(uid.into())
620 .map(|s: V2ipDeviceStats| mxr_v2ip_stats_t {
621 tx: s.tx.into(),
622 tx_per_minute: s.tx_per_minute.into(),
623 rx: s.rx.into(),
624 rx_per_minute: s.rx_per_minute.into(),
625 });
626 unsafe { fill(r, uid, out, "V2IP statistics", value) }
628 })
629}
630
631#[no_mangle]
638pub unsafe extern "C" fn mxr_v2ip_details(
639 remote: *const mxr_remote_t,
640 uid: mxr_uid_t,
641 out: *mut mxr_v2ip_details_t,
642) -> mxr_result_t {
643 let handle = unsafe { remote.as_ref() };
645 with(handle, |r| {
646 let value = r
647 .remote
648 .v2ip_details(uid.into())
649 .map(|d: DeviceV2ipDetails| mxr_v2ip_details_t {
650 video: d.video.into(),
651 audio: d.audio.into(),
652 anc: d.anc.into(),
653 arc: d.arc.into(),
654 tx_rate: d.tx_rate.map_or(-1, i16::from),
657 dscp_video: d.dscp.video.map_or(-1, i16::from),
658 dscp_audio: d.dscp.audio.map_or(-1, i16::from),
659 dscp_anc: d.dscp.anc.map_or(-1, i16::from),
660 scaling_mode: d.scaling.mode.to_wire(),
661 scaling_refresh: d.scaling.refresh,
662 scaling_flags: d.scaling.flags,
663 });
664 unsafe { fill(r, uid, out, "V2IP encoder configuration", value) }
666 })
667}
668
669#[no_mangle]
676pub unsafe extern "C" fn mxr_v2ip_sink(
677 remote: *const mxr_remote_t,
678 uid: mxr_uid_t,
679 out: *mut mxr_v2ip_sink_t,
680) -> mxr_result_t {
681 let handle = unsafe { remote.as_ref() };
683 with(handle, |r| {
684 let value = r
685 .remote
686 .v2ip_sink(uid.into())
687 .map(|s: DeviceV2ipSink| mxr_v2ip_sink_t {
688 addresses: s.addresses.into(),
689 has_audio_format: s.audio_fmt.is_some(),
690 audio_format: {
691 let f = s.audio_fmt.unwrap_or_default();
692 mxr_audio_format_t {
693 sample_rate: f.sample_rate,
694 channels: f.channels,
695 }
696 },
697 });
698 unsafe { fill(r, uid, out, "V2IP sink route", value) }
700 })
701}
702
703#[no_mangle]
710pub unsafe extern "C" fn mxr_v2ip_tiling(
711 remote: *const mxr_remote_t,
712 uid: mxr_uid_t,
713 out: *mut mxr_tiling_config_t,
714) -> mxr_result_t {
715 let handle = unsafe { remote.as_ref() };
717 with(handle, |r| {
718 let value =
719 r.remote
720 .v2ip_tiling(uid.into())
721 .map(|t: V2ipTilingConfig| mxr_tiling_config_t {
722 target: t.target.into(),
723 pos_x: t.pos_x,
724 pos_y: t.pos_y,
725 width: t.width,
726 height: t.height,
727 });
728 unsafe { fill(r, uid, out, "window", value) }
730 })
731}
732
733#[no_mangle]
740pub unsafe extern "C" fn mxr_multiviewer_status(
741 remote: *const mxr_remote_t,
742 uid: mxr_uid_t,
743 out: *mut mxr_multiviewer_status_t,
744) -> mxr_result_t {
745 let handle = unsafe { remote.as_ref() };
747 with(handle, |r| {
748 let value = r.remote.multiviewer_status(uid.into()).map(multiviewer_of);
749 unsafe { fill(r, uid, out, "multiviewer status", value) }
751 })
752}
753
754fn multiviewer_of(s: MultiviewerStatus) -> mxr_multiviewer_status_t {
756 let mut out = mxr_multiviewer_status_t {
757 uid: s.uid.into(),
758 mappings: [mxr_uid_t::default(); MXR_MULTIVIEWER_INPUTS],
759 mcu_version: [0; MXR_NAME_LEN],
760 scaler_version: [0; MXR_NAME_LEN],
761 hw_view_mode: s.hw_view_mode,
762 view_mode: s.view_mode.to_wire(),
763 pip_position: s.pip_position.to_wire(),
764 pip_size: s.pip_size.to_wire(),
765 output_mode: s.output_mode.to_wire(),
766 hdcp_mode: s.hdcp_mode.to_wire(),
767 output_itc: s.output_itc.to_wire(),
768 edid_template: s.edid_template.to_wire(),
769 aspect_ratio: s.aspect_ratio.to_wire(),
770 auto_switch: s.auto_switch.to_wire(),
771 audio_source: s.audio_source.to_wire(),
772 has_audio_volume: s.audio_volume.is_some(),
773 audio_volume: s.audio_volume.unwrap_or(0),
774 audio_muted: s.audio_muted.to_wire(),
775 video_sources: [0; MXR_MULTIVIEWER_INPUTS],
776 remote_control: s.remote_control.to_wire(),
777 };
778 for (slot, uid) in out.mappings.iter_mut().zip(s.mappings) {
779 *slot = uid.into();
780 }
781 for (slot, source) in out.video_sources.iter_mut().zip(s.video_sources) {
782 *slot = source.to_wire();
783 }
784 put_str(&mut out.mcu_version, &s.mcu_version);
785 put_str(&mut out.scaler_version, &s.scaler_version);
786 out
787}
788
789#[no_mangle]
796pub unsafe extern "C" fn mxr_dolby_settings(
797 remote: *const mxr_remote_t,
798 uid: mxr_uid_t,
799 out: *mut mxr_dolby_settings_t,
800) -> mxr_result_t {
801 let handle = unsafe { remote.as_ref() };
803 with(handle, |r| {
804 let value = r
805 .remote
806 .dolby_settings(uid.into())
807 .map(mxr_dolby_settings_t::from);
808 unsafe { fill(r, uid, out, "Dolby settings", value) }
810 })
811}
812
813#[no_mangle]
820pub unsafe extern "C" fn mxr_rc_settings(
821 remote: *const mxr_remote_t,
822 uid: mxr_uid_t,
823 out: *mut mxr_rc_settings_t,
824) -> mxr_result_t {
825 let handle = unsafe { remote.as_ref() };
827 with(handle, |r| {
828 let value = r.remote.rc_settings(uid.into()).map(rc_settings_of);
829 unsafe { fill(r, uid, out, "remote-control configuration", value) }
831 })
832}
833
834fn rc_settings_of(s: RcSettings) -> mxr_rc_settings_t {
836 let mut out = mxr_rc_settings_t {
837 target: s.target.into(),
838 rc_target: s.rc_target,
839 ip: [0; MXR_IP_STRING_LEN],
840 cec_enabled: s.cec_enabled,
841 cec_auto_on: s.cec_auto_on,
842 forward_rc: s.forward_rc,
843 forward_ir: s.forward_ir,
844 rc_status: s.rc_status,
845 status_name: [0; MXR_NAME_LEN],
846 };
847 put_ip(&mut out.ip, s.ip);
848 put_str(&mut out.status_name, &s.status_name);
849 out
850}
851
852#[no_mangle]
863pub unsafe extern "C" fn mxr_v2ip_sources(
864 remote: *const mxr_remote_t,
865 uid: mxr_uid_t,
866 out: *mut mxr_stream_sources_t,
867 cap: usize,
868) -> usize {
869 guard(0, || {
870 let Some(r) = (unsafe { remote.as_ref() }) else {
872 return no_handle();
873 };
874 let Some(sources) = r.remote.v2ip_sources(uid.into()) else {
875 not_reported(r, uid, "V2IP stream sources");
876 return 0;
877 };
878 unsafe { copy_into(&sources, out, cap) }
880 })
881}
882
883#[no_mangle]
890pub unsafe extern "C" fn mxr_network_status(
891 remote: *const mxr_remote_t,
892 uid: mxr_uid_t,
893 out: *mut mxr_network_port_t,
894 cap: usize,
895) -> usize {
896 guard(0, || {
897 let Some(r) = (unsafe { remote.as_ref() }) else {
899 return no_handle();
900 };
901 let ports: Vec<mxr_network_port_t> = r
902 .remote
903 .network_status(uid.into())
904 .iter()
905 .map(port_of)
906 .collect();
907 unsafe { copy_into(&ports, out, cap) }
909 })
910}
911
912fn port_of(p: &NetworkPortStatus) -> mxr_network_port_t {
914 let errors = p.errors.unwrap_or_default();
915 let mut out = mxr_network_port_t {
916 port: p.port,
917 name: [0; MXR_NAME_LEN],
918 link_speed: p.link_speed.to_wire(),
919 link_full_duplex: p.link_full_duplex,
920 ip: [0; MXR_IP_STRING_LEN],
921 querier: [0; MXR_IP_STRING_LEN],
922 has_mac_address: p.mac_address.is_some(),
923 mac_address: p.mac_address.unwrap_or_default().0,
924 has_errors: p.errors.is_some(),
925 in_error: errors.in_error,
926 in_fcs_error: errors.in_fcs_error,
927 in_collision: errors.in_collision,
928 out_deferred: errors.out_deferred,
929 out_excessive: errors.out_excessive,
930 polarity_error: errors.polarity_error,
931 skew_warning: errors.skew_warning,
932 length_warning: errors.length_warning,
933 has_vct_status: p.vct_status.is_some(),
934 vct_warning: [false; MXR_UTP_PAIRS],
935 cable_status_count: p.cable_status.len().min(MXR_UTP_PAIRS),
936 cable_status: [mxr_cable_status_t {
937 polarity: false,
938 pair: 0,
939 skew: 0,
940 length: 0,
941 }; MXR_UTP_PAIRS],
942 };
943 put_str(&mut out.name, &p.name);
944 put_ip(&mut out.ip, p.ip);
945 put_ip(&mut out.querier, p.querier);
946 if let Some(vct) = p.vct_status {
947 for (slot, status) in out.vct_warning.iter_mut().zip(vct) {
948 *slot = status == VctStatus::Warning;
949 }
950 }
951 for (slot, cable) in out.cable_status.iter_mut().zip(&p.cable_status) {
952 *slot = (*cable).into();
953 }
954 out
955}
956
957#[no_mangle]
965pub unsafe extern "C" fn mxr_topology(
966 remote: *const mxr_remote_t,
967 uid: mxr_uid_t,
968 out: *mut mxr_topology_entry_t,
969 cap: usize,
970) -> usize {
971 guard(0, || {
972 let Some(r) = (unsafe { remote.as_ref() }) else {
974 return no_handle();
975 };
976 let topology = r.remote.topology(uid.into());
977 unsafe { copy_into(&topology, out, cap) }
979 })
980}
981
982#[no_mangle]
990pub unsafe extern "C" fn mxr_device_firmware(
991 remote: *const mxr_remote_t,
992 uid: mxr_uid_t,
993 out: *mut mxr_firmware_version_t,
994 cap: usize,
995) -> usize {
996 guard(0, || {
997 let Some(r) = (unsafe { remote.as_ref() }) else {
999 return no_handle();
1000 };
1001 let versions: Vec<mxr_firmware_version_t> = r
1002 .remote
1003 .firmware(uid.into())
1004 .iter()
1005 .map(|(_, v)| firmware_of(v))
1006 .collect();
1007 unsafe { copy_into(&versions, out, cap) }
1009 })
1010}
1011
1012fn firmware_of(v: &FirmwareVersion) -> mxr_firmware_version_t {
1014 let mut out = mxr_firmware_version_t {
1015 firmware_type: v.firmware_type.to_wire(),
1016 timestamp: v.timestamp,
1017 hash: v.hash,
1018 version: [0; MXR_VERSION_LEN],
1019 };
1020 put_str(&mut out.version, &v.version);
1021 out
1022}
1023
1024#[no_mangle]
1032pub unsafe extern "C" fn mxr_audio_endpoints(
1033 remote: *const mxr_remote_t,
1034 uid: mxr_uid_t,
1035 out: *mut mxr_audio_endpoint_t,
1036 cap: usize,
1037) -> usize {
1038 guard(0, || {
1039 let Some(r) = (unsafe { remote.as_ref() }) else {
1041 return no_handle();
1042 };
1043 let Some(endpoints) = r.remote.audio_endpoints(uid.into()) else {
1044 not_reported(r, uid, "audio endpoints");
1045 return 0;
1046 };
1047 let list: Vec<mxr_audio_endpoint_t> = endpoints.list().map(Into::into).collect();
1048 unsafe { copy_into(&list, out, cap) }
1050 })
1051}
1052
1053#[no_mangle]
1061pub unsafe extern "C" fn mxr_audio_endpoint_children(
1062 remote: *const mxr_remote_t,
1063 uid: mxr_uid_t,
1064 endpoint: u8,
1065 out: *mut u8,
1066 cap: usize,
1067) -> usize {
1068 guard(0, || {
1069 let Some(r) = (unsafe { remote.as_ref() }) else {
1071 return no_handle();
1072 };
1073 let children = match r.remote.audio_endpoints(uid.into()) {
1074 Some(endpoints) => match endpoints.get(endpoint) {
1075 Some(e) => e.children.clone(),
1076 None => {
1077 fail(
1078 mxr_result_t::MXR_ERR_NOT_FOUND,
1079 &format!("the device has no audio endpoint {endpoint}"),
1080 );
1081 return 0;
1082 }
1083 },
1084 None => {
1085 not_reported(r, uid, "audio endpoints");
1086 return 0;
1087 }
1088 };
1089 unsafe { copy_into(&children, out, cap) }
1091 })
1092}
1093
1094fn no_handle() -> usize {
1096 fail(
1097 mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
1098 "the client handle is null",
1099 );
1100 0
1101}