1use std::ffi::c_char;
17use std::net::Ipv4Addr;
18
19use mx_remote::{
20 AmpDolbySettings, AudioEndpoint, DeviceV2ipDetails, DeviceV2ipSink, FirmwareVersion,
21 MultiviewerStatus, NetworkPortStatus, PduState, RcSettings, StreamKind, TopologyEntry,
22 UtpCableStatus, V2ipDeviceStats, V2ipRxStats, V2ipStreamSource, V2ipStreamSources,
23 V2ipTilingConfig, 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
42pub const MXR_PDU_OUTLETS: usize = 8;
44
45#[repr(i32)]
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum mxr_stream_kind_t {
49 MXR_STREAM_VIDEO = 0,
51 MXR_STREAM_AUDIO = 1,
53 MXR_STREAM_ANC = 2,
55 MXR_STREAM_ARC = 3,
57}
58
59impl From<StreamKind> for mxr_stream_kind_t {
60 fn from(kind: StreamKind) -> Self {
61 match kind {
62 StreamKind::Video => Self::MXR_STREAM_VIDEO,
63 StreamKind::Audio => Self::MXR_STREAM_AUDIO,
64 StreamKind::Anc => Self::MXR_STREAM_ANC,
65 StreamKind::Arc => Self::MXR_STREAM_ARC,
66 }
67 }
68}
69
70#[repr(C)]
72#[derive(Clone, Copy)]
73pub struct mxr_stream_source_t {
74 pub kind: mxr_stream_kind_t,
76 pub ip: [c_char; MXR_IP_STRING_LEN],
78 pub port: u16,
80 pub valid: bool,
84}
85
86impl From<V2ipStreamSource> for mxr_stream_source_t {
87 fn from(s: V2ipStreamSource) -> Self {
88 let mut out = Self {
89 kind: s.kind.into(),
90 ip: [0; MXR_IP_STRING_LEN],
91 port: s.port,
92 valid: s.is_valid(),
93 };
94 put_str(&mut out.ip, &s.ip.to_string());
95 out
96 }
97}
98
99#[repr(C)]
101#[derive(Clone, Copy)]
102pub struct mxr_stream_sources_t {
103 pub uid: mxr_uid_t,
105 pub video: mxr_stream_source_t,
107 pub audio: mxr_stream_source_t,
109 pub anc: mxr_stream_source_t,
111 pub has_arc: bool,
113 pub arc: mxr_stream_source_t,
115}
116
117impl From<V2ipStreamSources> for mxr_stream_sources_t {
118 fn from(s: V2ipStreamSources) -> Self {
119 Self {
120 uid: s.uid.into(),
121 video: s.video.into(),
122 audio: s.audio.into(),
123 anc: s.anc.into(),
124 has_arc: s.arc.is_some(),
125 arc: s.arc.unwrap_or_default().into(),
126 }
127 }
128}
129
130#[repr(C)]
132#[derive(Clone, Copy)]
133pub struct mxr_v2ip_details_t {
134 pub video: mxr_stream_source_t,
136 pub audio: mxr_stream_source_t,
138 pub anc: mxr_stream_source_t,
140 pub arc: mxr_stream_source_t,
142 pub tx_rate: i16,
144 pub dscp_video: i16,
146 pub dscp_audio: i16,
148 pub dscp_anc: i16,
150 pub scaling_mode: u16,
152 pub scaling_refresh: u16,
154 pub scaling_flags: u8,
158}
159
160pub const MXR_SCALING_FLAG_MODE_VALID: u8 = 1 << 0;
162pub const MXR_SCALING_FLAG_OPTIONS_VALID: u8 = 1 << 1;
164pub const MXR_SCALING_FLAG_AUTO_SCALING: u8 = 1 << 7;
166
167#[repr(C)]
169#[derive(Clone, Copy)]
170pub struct mxr_v2ip_sink_t {
171 pub addresses: mxr_stream_sources_t,
173 pub has_audio_format: bool,
175 pub audio_format: mxr_audio_format_t,
177}
178
179#[repr(C)]
181#[derive(Clone, Copy)]
182pub struct mxr_v2ip_tx_stats_t {
183 pub video: u32,
185 pub audio: u32,
187 pub anc: u32,
189 pub stream_down: u32,
191 pub overflow: u32,
193}
194
195impl From<V2ipTxStats> for mxr_v2ip_tx_stats_t {
196 fn from(s: V2ipTxStats) -> Self {
197 Self {
198 video: s.video,
199 audio: s.audio,
200 anc: s.anc,
201 stream_down: s.stream_down,
202 overflow: s.overflow,
203 }
204 }
205}
206
207#[repr(C)]
209#[derive(Clone, Copy)]
210pub struct mxr_v2ip_rx_stats_t {
211 pub video_total: u32,
213 pub video_dropped: u32,
215 pub video_seq_errors: u32,
217 pub wdt_timeout: u32,
219 pub audio_total: u32,
221 pub audio_dropped: u32,
223 pub audio_seq_errors: u32,
225 pub anc_total: u32,
227 pub anc_dropped: u32,
229 pub anc_seq_errors: u32,
231 pub decoder_state: u8,
237}
238
239impl From<V2ipRxStats> for mxr_v2ip_rx_stats_t {
240 fn from(s: V2ipRxStats) -> Self {
241 Self {
242 video_total: s.video_total,
243 video_dropped: s.video_dropped,
244 video_seq_errors: s.video_seq_errors,
245 wdt_timeout: s.wdt_timeout,
246 audio_total: s.audio_total,
247 audio_dropped: s.audio_dropped,
248 audio_seq_errors: s.audio_seq_errors,
249 anc_total: s.anc_total,
250 anc_dropped: s.anc_dropped,
251 anc_seq_errors: s.anc_seq_errors,
252 decoder_state: s.decoder_state.to_wire(),
253 }
254 }
255}
256
257#[repr(C)]
259#[derive(Clone, Copy)]
260pub struct mxr_v2ip_stats_t {
261 pub tx: mxr_v2ip_tx_stats_t,
263 pub tx_per_minute: mxr_v2ip_tx_stats_t,
265 pub rx: mxr_v2ip_rx_stats_t,
267 pub rx_per_minute: mxr_v2ip_rx_stats_t,
269}
270
271#[repr(C)]
278#[derive(Clone, Copy)]
279pub struct mxr_tiling_config_t {
280 pub target: mxr_uid_t,
282 pub pos_x: u16,
284 pub pos_y: u16,
286 pub width: u16,
288 pub height: u16,
290}
291
292#[repr(C)]
294#[derive(Clone, Copy)]
295pub struct mxr_multiviewer_status_t {
296 pub uid: mxr_uid_t,
298 pub mappings: [mxr_uid_t; MXR_MULTIVIEWER_INPUTS],
300 pub mcu_version: [c_char; MXR_NAME_LEN],
302 pub scaler_version: [c_char; MXR_NAME_LEN],
304 pub hw_view_mode: u8,
307 pub view_mode: u8,
309 pub pip_position: u8,
311 pub pip_size: u8,
313 pub output_mode: u8,
315 pub hdcp_mode: u8,
317 pub output_itc: u8,
319 pub edid_template: u8,
321 pub aspect_ratio: u8,
323 pub auto_switch: u8,
325 pub audio_source: u8,
327 pub has_audio_volume: bool,
329 pub audio_volume: u8,
331 pub audio_muted: u8,
333 pub video_sources: [u8; MXR_MULTIVIEWER_INPUTS],
335 pub remote_control: u8,
337}
338
339#[repr(C)]
341#[derive(Clone, Copy)]
342pub struct mxr_audio_endpoint_t {
343 pub id: u8,
345 pub features: u32,
347 pub has_address: bool,
349 pub address: mxr_stream_source_t,
351 pub parent: i16,
353 pub child_count: usize,
356 pub has_inputs_available: bool,
358 pub inputs_available: u32,
360 pub has_inputs_routed: bool,
362 pub inputs_routed: u32,
364 pub linked_device: mxr_uid_t,
366 pub linked_endpoint: i16,
368}
369
370impl From<&AudioEndpoint> for mxr_audio_endpoint_t {
371 fn from(e: &AudioEndpoint) -> Self {
372 Self {
373 id: e.id,
374 features: e.features.bits(),
375 has_address: e.address.is_some(),
376 address: e.address.unwrap_or_default().into(),
377 parent: e.parent.map_or(-1, i16::from),
379 child_count: e.children.len(),
380 has_inputs_available: e.inputs_available.is_some(),
381 inputs_available: e.inputs_available.unwrap_or(0),
382 has_inputs_routed: e.inputs_routed.is_some(),
383 inputs_routed: e.inputs_routed.unwrap_or(0),
384 linked_device: e.linked_device.into(),
385 linked_endpoint: e.linked_endpoint.map_or(-1, i16::from),
386 }
387 }
388}
389
390#[repr(C)]
392#[derive(Clone, Copy)]
393pub struct mxr_cable_status_t {
394 pub polarity: bool,
396 pub pair: u8,
398 pub skew: u32,
400 pub length: u32,
402}
403
404impl From<UtpCableStatus> for mxr_cable_status_t {
405 fn from(c: UtpCableStatus) -> Self {
406 Self {
407 polarity: c.polarity,
408 pair: c.pair,
409 skew: c.skew,
410 length: c.length,
411 }
412 }
413}
414
415#[repr(C)]
417#[derive(Clone, Copy)]
418pub struct mxr_network_port_t {
419 pub port: u16,
421 pub name: [c_char; MXR_NAME_LEN],
423 pub link_speed: u8,
425 pub link_full_duplex: bool,
427 pub ip: [c_char; MXR_IP_STRING_LEN],
429 pub querier: [c_char; MXR_IP_STRING_LEN],
431 pub has_mac_address: bool,
433 pub mac_address: [u8; 6],
435 pub has_errors: bool,
437 pub in_error: bool,
439 pub in_fcs_error: bool,
441 pub in_collision: bool,
443 pub out_deferred: bool,
445 pub out_excessive: bool,
447 pub polarity_error: bool,
449 pub skew_warning: bool,
451 pub length_warning: bool,
453 pub has_vct_status: bool,
455 pub vct_warning: [bool; MXR_UTP_PAIRS],
458 pub cable_status_count: usize,
460 pub cable_status: [mxr_cable_status_t; MXR_UTP_PAIRS],
462}
463
464#[repr(C)]
466#[derive(Clone, Copy)]
467pub struct mxr_topology_entry_t {
468 pub uid: mxr_uid_t,
470 pub mask: u32,
472}
473
474impl From<TopologyEntry> for mxr_topology_entry_t {
475 fn from(e: TopologyEntry) -> Self {
476 Self {
477 uid: e.uid.into(),
478 mask: e.mask,
479 }
480 }
481}
482
483#[repr(C)]
485#[derive(Clone, Copy)]
486pub struct mxr_firmware_version_t {
487 pub firmware_type: u8,
489 pub timestamp: u32,
491 pub hash: u32,
493 pub version: [c_char; MXR_VERSION_LEN],
495}
496
497#[repr(C)]
499#[derive(Clone, Copy)]
500pub struct mxr_dolby_settings_t {
501 pub mode: u8,
503 pub pcm_upmix: bool,
505 pub dolby_detected: bool,
507 pub pcm_upmix_active: bool,
509}
510
511impl From<AmpDolbySettings> for mxr_dolby_settings_t {
512 fn from(s: AmpDolbySettings) -> Self {
513 Self {
514 mode: s.mode,
515 pcm_upmix: s.pcm_upmix,
516 dolby_detected: s.dolby_detected,
517 pcm_upmix_active: s.pcm_upmix_active,
518 }
519 }
520}
521
522#[repr(C)]
524#[derive(Clone, Copy)]
525pub struct mxr_pdu_state_t {
526 pub current: f64,
528 pub voltage: f64,
530 pub power: f64,
532 pub dissipation: f64,
534 pub frequency: f64,
536 pub outlets: [u8; MXR_PDU_OUTLETS],
538}
539
540impl From<PduState> for mxr_pdu_state_t {
541 fn from(s: PduState) -> Self {
542 Self {
543 current: s.current,
544 voltage: s.voltage,
545 power: s.power,
546 dissipation: s.dissipation,
547 frequency: s.frequency,
548 outlets: s.outlets,
549 }
550 }
551}
552
553#[repr(C)]
555#[derive(Clone, Copy)]
556pub struct mxr_rc_settings_t {
557 pub target: mxr_uid_t,
559 pub rc_target: u8,
568 pub ip: [c_char; MXR_IP_STRING_LEN],
570 pub cec_enabled: bool,
572 pub cec_auto_on: bool,
574 pub forward_rc: bool,
576 pub forward_ir: bool,
578 pub rc_status: u8,
581 pub status_name: [c_char; MXR_NAME_LEN],
583}
584
585fn put_ip(dst: &mut [c_char], ip: Option<Ipv4Addr>) {
588 put_str(dst, &ip.map(|ip| ip.to_string()).unwrap_or_default());
589}
590
591unsafe fn fill<T>(
602 r: &mxr_remote_t,
603 uid: mxr_uid_t,
604 out: *mut T,
605 what: &str,
606 value: Option<T>,
607) -> mxr_result_t {
608 if out.is_null() {
609 return null_out(what);
610 }
611 match value {
612 Some(value) => {
613 unsafe { *out = value };
615 mxr_result_t::MXR_OK
616 }
617 None => not_reported(r, uid, what),
618 }
619}
620
621fn not_reported(r: &mxr_remote_t, uid: mxr_uid_t, what: &str) -> mxr_result_t {
624 if r.remote.device(uid.into()).is_none() {
625 return not_heard_from(uid);
626 }
627 fail(
628 mxr_result_t::MXR_ERR_NOT_REPORTED,
629 &format!("the device has reported no {what}"),
630 )
631}
632
633#[no_mangle]
643pub unsafe extern "C" fn mxr_v2ip_stats(
644 remote: *const mxr_remote_t,
645 uid: mxr_uid_t,
646 out: *mut mxr_v2ip_stats_t,
647) -> mxr_result_t {
648 let handle = unsafe { remote.as_ref() };
650 with(handle, |r| {
651 let value = r
652 .remote
653 .v2ip_stats(uid.into())
654 .map(|s: V2ipDeviceStats| mxr_v2ip_stats_t {
655 tx: s.tx.into(),
656 tx_per_minute: s.tx_per_minute.into(),
657 rx: s.rx.into(),
658 rx_per_minute: s.rx_per_minute.into(),
659 });
660 unsafe { fill(r, uid, out, "V2IP statistics", value) }
662 })
663}
664
665#[no_mangle]
672pub unsafe extern "C" fn mxr_v2ip_details(
673 remote: *const mxr_remote_t,
674 uid: mxr_uid_t,
675 out: *mut mxr_v2ip_details_t,
676) -> mxr_result_t {
677 let handle = unsafe { remote.as_ref() };
679 with(handle, |r| {
680 let value = r
681 .remote
682 .v2ip_details(uid.into())
683 .map(|d: DeviceV2ipDetails| mxr_v2ip_details_t {
684 video: d.video.into(),
685 audio: d.audio.into(),
686 anc: d.anc.into(),
687 arc: d.arc.into(),
688 tx_rate: d.tx_rate.map_or(-1, i16::from),
691 dscp_video: d.dscp.video.map_or(-1, i16::from),
692 dscp_audio: d.dscp.audio.map_or(-1, i16::from),
693 dscp_anc: d.dscp.anc.map_or(-1, i16::from),
694 scaling_mode: d.scaling.mode.to_wire(),
695 scaling_refresh: d.scaling.refresh,
696 scaling_flags: d.scaling.flags,
697 });
698 unsafe { fill(r, uid, out, "V2IP encoder configuration", value) }
700 })
701}
702
703#[no_mangle]
710pub unsafe extern "C" fn mxr_v2ip_sink(
711 remote: *const mxr_remote_t,
712 uid: mxr_uid_t,
713 out: *mut mxr_v2ip_sink_t,
714) -> mxr_result_t {
715 let handle = unsafe { remote.as_ref() };
717 with(handle, |r| {
718 let value = r
719 .remote
720 .v2ip_sink(uid.into())
721 .map(|s: DeviceV2ipSink| mxr_v2ip_sink_t {
722 addresses: s.addresses.into(),
723 has_audio_format: s.audio_fmt.is_some(),
724 audio_format: {
725 let f = s.audio_fmt.unwrap_or_default();
726 mxr_audio_format_t {
727 sample_rate: f.sample_rate,
728 channels: f.channels,
729 }
730 },
731 });
732 unsafe { fill(r, uid, out, "V2IP sink route", value) }
734 })
735}
736
737#[no_mangle]
744pub unsafe extern "C" fn mxr_v2ip_tiling(
745 remote: *const mxr_remote_t,
746 uid: mxr_uid_t,
747 out: *mut mxr_tiling_config_t,
748) -> mxr_result_t {
749 let handle = unsafe { remote.as_ref() };
751 with(handle, |r| {
752 let value =
753 r.remote
754 .v2ip_tiling(uid.into())
755 .map(|t: V2ipTilingConfig| mxr_tiling_config_t {
756 target: t.target.into(),
757 pos_x: t.pos_x,
758 pos_y: t.pos_y,
759 width: t.width,
760 height: t.height,
761 });
762 unsafe { fill(r, uid, out, "window", value) }
764 })
765}
766
767#[no_mangle]
774pub unsafe extern "C" fn mxr_multiviewer_status(
775 remote: *const mxr_remote_t,
776 uid: mxr_uid_t,
777 out: *mut mxr_multiviewer_status_t,
778) -> mxr_result_t {
779 let handle = unsafe { remote.as_ref() };
781 with(handle, |r| {
782 let value = r.remote.multiviewer_status(uid.into()).map(multiviewer_of);
783 unsafe { fill(r, uid, out, "multiviewer status", value) }
785 })
786}
787
788fn multiviewer_of(s: MultiviewerStatus) -> mxr_multiviewer_status_t {
790 let mut out = mxr_multiviewer_status_t {
791 uid: s.uid.into(),
792 mappings: [mxr_uid_t::default(); MXR_MULTIVIEWER_INPUTS],
793 mcu_version: [0; MXR_NAME_LEN],
794 scaler_version: [0; MXR_NAME_LEN],
795 hw_view_mode: s.hw_view_mode,
796 view_mode: s.view_mode.to_wire(),
797 pip_position: s.pip_position.to_wire(),
798 pip_size: s.pip_size.to_wire(),
799 output_mode: s.output_mode.to_wire(),
800 hdcp_mode: s.hdcp_mode.to_wire(),
801 output_itc: s.output_itc.to_wire(),
802 edid_template: s.edid_template.to_wire(),
803 aspect_ratio: s.aspect_ratio.to_wire(),
804 auto_switch: s.auto_switch.to_wire(),
805 audio_source: s.audio_source.to_wire(),
806 has_audio_volume: s.audio_volume.is_some(),
807 audio_volume: s.audio_volume.unwrap_or(0),
808 audio_muted: s.audio_muted.to_wire(),
809 video_sources: [0; MXR_MULTIVIEWER_INPUTS],
810 remote_control: s.remote_control.to_wire(),
811 };
812 for (slot, uid) in out.mappings.iter_mut().zip(s.mappings) {
813 *slot = uid.into();
814 }
815 for (slot, source) in out.video_sources.iter_mut().zip(s.video_sources) {
816 *slot = source.to_wire();
817 }
818 put_str(&mut out.mcu_version, &s.mcu_version);
819 put_str(&mut out.scaler_version, &s.scaler_version);
820 out
821}
822
823#[no_mangle]
830pub unsafe extern "C" fn mxr_dolby_settings(
831 remote: *const mxr_remote_t,
832 uid: mxr_uid_t,
833 out: *mut mxr_dolby_settings_t,
834) -> mxr_result_t {
835 let handle = unsafe { remote.as_ref() };
837 with(handle, |r| {
838 let value = r
839 .remote
840 .dolby_settings(uid.into())
841 .map(mxr_dolby_settings_t::from);
842 unsafe { fill(r, uid, out, "Dolby settings", value) }
844 })
845}
846
847#[no_mangle]
854pub unsafe extern "C" fn mxr_pdu_state(
855 remote: *const mxr_remote_t,
856 uid: mxr_uid_t,
857 out: *mut mxr_pdu_state_t,
858) -> mxr_result_t {
859 let handle = unsafe { remote.as_ref() };
861 with(handle, |r| {
862 let value = r.remote.pdu_state(uid.into()).map(mxr_pdu_state_t::from);
863 unsafe { fill(r, uid, out, "PDU state", value) }
865 })
866}
867
868#[no_mangle]
875pub unsafe extern "C" fn mxr_rc_settings(
876 remote: *const mxr_remote_t,
877 uid: mxr_uid_t,
878 out: *mut mxr_rc_settings_t,
879) -> mxr_result_t {
880 let handle = unsafe { remote.as_ref() };
882 with(handle, |r| {
883 let value = r.remote.rc_settings(uid.into()).map(rc_settings_of);
884 unsafe { fill(r, uid, out, "remote-control configuration", value) }
886 })
887}
888
889fn rc_settings_of(s: RcSettings) -> mxr_rc_settings_t {
891 let mut out = mxr_rc_settings_t {
892 target: s.target.into(),
893 rc_target: s.rc_target,
894 ip: [0; MXR_IP_STRING_LEN],
895 cec_enabled: s.cec_enabled,
896 cec_auto_on: s.cec_auto_on,
897 forward_rc: s.forward_rc,
898 forward_ir: s.forward_ir,
899 rc_status: s.rc_status,
900 status_name: [0; MXR_NAME_LEN],
901 };
902 put_ip(&mut out.ip, s.ip);
903 put_str(&mut out.status_name, &s.status_name);
904 out
905}
906
907#[no_mangle]
918pub unsafe extern "C" fn mxr_v2ip_sources(
919 remote: *const mxr_remote_t,
920 uid: mxr_uid_t,
921 out: *mut mxr_stream_sources_t,
922 cap: usize,
923) -> usize {
924 guard(0, || {
925 let Some(r) = (unsafe { remote.as_ref() }) else {
927 return no_handle();
928 };
929 let Some(sources) = r.remote.v2ip_sources(uid.into()) else {
930 not_reported(r, uid, "V2IP stream sources");
931 return 0;
932 };
933 unsafe { copy_into(&sources, out, cap) }
935 })
936}
937
938#[no_mangle]
945pub unsafe extern "C" fn mxr_network_status(
946 remote: *const mxr_remote_t,
947 uid: mxr_uid_t,
948 out: *mut mxr_network_port_t,
949 cap: usize,
950) -> usize {
951 guard(0, || {
952 let Some(r) = (unsafe { remote.as_ref() }) else {
954 return no_handle();
955 };
956 let ports: Vec<mxr_network_port_t> = r
957 .remote
958 .network_status(uid.into())
959 .iter()
960 .map(port_of)
961 .collect();
962 unsafe { copy_into(&ports, out, cap) }
964 })
965}
966
967fn port_of(p: &NetworkPortStatus) -> mxr_network_port_t {
969 let errors = p.errors.unwrap_or_default();
970 let mut out = mxr_network_port_t {
971 port: p.port,
972 name: [0; MXR_NAME_LEN],
973 link_speed: p.link_speed.to_wire(),
974 link_full_duplex: p.link_full_duplex,
975 ip: [0; MXR_IP_STRING_LEN],
976 querier: [0; MXR_IP_STRING_LEN],
977 has_mac_address: p.mac_address.is_some(),
978 mac_address: p.mac_address.unwrap_or_default().0,
979 has_errors: p.errors.is_some(),
980 in_error: errors.in_error,
981 in_fcs_error: errors.in_fcs_error,
982 in_collision: errors.in_collision,
983 out_deferred: errors.out_deferred,
984 out_excessive: errors.out_excessive,
985 polarity_error: errors.polarity_error,
986 skew_warning: errors.skew_warning,
987 length_warning: errors.length_warning,
988 has_vct_status: p.vct_status.is_some(),
989 vct_warning: [false; MXR_UTP_PAIRS],
990 cable_status_count: p.cable_status.len().min(MXR_UTP_PAIRS),
991 cable_status: [mxr_cable_status_t {
992 polarity: false,
993 pair: 0,
994 skew: 0,
995 length: 0,
996 }; MXR_UTP_PAIRS],
997 };
998 put_str(&mut out.name, &p.name);
999 put_ip(&mut out.ip, p.ip);
1000 put_ip(&mut out.querier, p.querier);
1001 if let Some(vct) = p.vct_status {
1002 for (slot, status) in out.vct_warning.iter_mut().zip(vct) {
1003 *slot = status == VctStatus::Warning;
1004 }
1005 }
1006 for (slot, cable) in out.cable_status.iter_mut().zip(&p.cable_status) {
1007 *slot = (*cable).into();
1008 }
1009 out
1010}
1011
1012#[no_mangle]
1020pub unsafe extern "C" fn mxr_topology(
1021 remote: *const mxr_remote_t,
1022 uid: mxr_uid_t,
1023 out: *mut mxr_topology_entry_t,
1024 cap: usize,
1025) -> usize {
1026 guard(0, || {
1027 let Some(r) = (unsafe { remote.as_ref() }) else {
1029 return no_handle();
1030 };
1031 let topology = r.remote.topology(uid.into());
1032 unsafe { copy_into(&topology, out, cap) }
1034 })
1035}
1036
1037#[no_mangle]
1045pub unsafe extern "C" fn mxr_device_firmware(
1046 remote: *const mxr_remote_t,
1047 uid: mxr_uid_t,
1048 out: *mut mxr_firmware_version_t,
1049 cap: usize,
1050) -> usize {
1051 guard(0, || {
1052 let Some(r) = (unsafe { remote.as_ref() }) else {
1054 return no_handle();
1055 };
1056 let versions: Vec<mxr_firmware_version_t> = r
1057 .remote
1058 .firmware(uid.into())
1059 .iter()
1060 .map(|(_, v)| firmware_of(v))
1061 .collect();
1062 unsafe { copy_into(&versions, out, cap) }
1064 })
1065}
1066
1067fn firmware_of(v: &FirmwareVersion) -> mxr_firmware_version_t {
1069 let mut out = mxr_firmware_version_t {
1070 firmware_type: v.firmware_type.to_wire(),
1071 timestamp: v.timestamp,
1072 hash: v.hash,
1073 version: [0; MXR_VERSION_LEN],
1074 };
1075 put_str(&mut out.version, &v.version);
1076 out
1077}
1078
1079#[no_mangle]
1087pub unsafe extern "C" fn mxr_audio_endpoints(
1088 remote: *const mxr_remote_t,
1089 uid: mxr_uid_t,
1090 out: *mut mxr_audio_endpoint_t,
1091 cap: usize,
1092) -> usize {
1093 guard(0, || {
1094 let Some(r) = (unsafe { remote.as_ref() }) else {
1096 return no_handle();
1097 };
1098 let Some(endpoints) = r.remote.audio_endpoints(uid.into()) else {
1099 not_reported(r, uid, "audio endpoints");
1100 return 0;
1101 };
1102 let list: Vec<mxr_audio_endpoint_t> = endpoints.list().map(Into::into).collect();
1103 unsafe { copy_into(&list, out, cap) }
1105 })
1106}
1107
1108#[no_mangle]
1116pub unsafe extern "C" fn mxr_audio_endpoint_children(
1117 remote: *const mxr_remote_t,
1118 uid: mxr_uid_t,
1119 endpoint: u8,
1120 out: *mut u8,
1121 cap: usize,
1122) -> usize {
1123 guard(0, || {
1124 let Some(r) = (unsafe { remote.as_ref() }) else {
1126 return no_handle();
1127 };
1128 let children = match r.remote.audio_endpoints(uid.into()) {
1129 Some(endpoints) => match endpoints.get(endpoint) {
1130 Some(e) => e.children.clone(),
1131 None => {
1132 fail(
1133 mxr_result_t::MXR_ERR_NOT_FOUND,
1134 &format!("the device has no audio endpoint {endpoint}"),
1135 );
1136 return 0;
1137 }
1138 },
1139 None => {
1140 not_reported(r, uid, "audio endpoints");
1141 return 0;
1142 }
1143 };
1144 unsafe { copy_into(&children, out, cap) }
1146 })
1147}
1148
1149fn no_handle() -> usize {
1151 fail(
1152 mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
1153 "the client handle is null",
1154 );
1155 0
1156}