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,
561 pub ip: [c_char; MXR_IP_STRING_LEN],
563 pub cec_enabled: bool,
565 pub cec_auto_on: bool,
567 pub forward_rc: bool,
569 pub forward_ir: bool,
571 pub rc_status: u8,
574 pub status_name: [c_char; MXR_NAME_LEN],
576}
577
578fn put_ip(dst: &mut [c_char], ip: Option<Ipv4Addr>) {
581 put_str(dst, &ip.map(|ip| ip.to_string()).unwrap_or_default());
582}
583
584unsafe fn fill<T>(
595 r: &mxr_remote_t,
596 uid: mxr_uid_t,
597 out: *mut T,
598 what: &str,
599 value: Option<T>,
600) -> mxr_result_t {
601 if out.is_null() {
602 return null_out(what);
603 }
604 match value {
605 Some(value) => {
606 unsafe { *out = value };
608 mxr_result_t::MXR_OK
609 }
610 None => not_reported(r, uid, what),
611 }
612}
613
614fn not_reported(r: &mxr_remote_t, uid: mxr_uid_t, what: &str) -> mxr_result_t {
617 if r.remote.device(uid.into()).is_none() {
618 return not_heard_from(uid);
619 }
620 fail(
621 mxr_result_t::MXR_ERR_NOT_REPORTED,
622 &format!("the device has reported no {what}"),
623 )
624}
625
626#[no_mangle]
636pub unsafe extern "C" fn mxr_v2ip_stats(
637 remote: *const mxr_remote_t,
638 uid: mxr_uid_t,
639 out: *mut mxr_v2ip_stats_t,
640) -> mxr_result_t {
641 let handle = unsafe { remote.as_ref() };
643 with(handle, |r| {
644 let value = r
645 .remote
646 .v2ip_stats(uid.into())
647 .map(|s: V2ipDeviceStats| mxr_v2ip_stats_t {
648 tx: s.tx.into(),
649 tx_per_minute: s.tx_per_minute.into(),
650 rx: s.rx.into(),
651 rx_per_minute: s.rx_per_minute.into(),
652 });
653 unsafe { fill(r, uid, out, "V2IP statistics", value) }
655 })
656}
657
658#[no_mangle]
665pub unsafe extern "C" fn mxr_v2ip_details(
666 remote: *const mxr_remote_t,
667 uid: mxr_uid_t,
668 out: *mut mxr_v2ip_details_t,
669) -> mxr_result_t {
670 let handle = unsafe { remote.as_ref() };
672 with(handle, |r| {
673 let value = r
674 .remote
675 .v2ip_details(uid.into())
676 .map(|d: DeviceV2ipDetails| mxr_v2ip_details_t {
677 video: d.video.into(),
678 audio: d.audio.into(),
679 anc: d.anc.into(),
680 arc: d.arc.into(),
681 tx_rate: d.tx_rate.map_or(-1, i16::from),
684 dscp_video: d.dscp.video.map_or(-1, i16::from),
685 dscp_audio: d.dscp.audio.map_or(-1, i16::from),
686 dscp_anc: d.dscp.anc.map_or(-1, i16::from),
687 scaling_mode: d.scaling.mode.to_wire(),
688 scaling_refresh: d.scaling.refresh,
689 scaling_flags: d.scaling.flags,
690 });
691 unsafe { fill(r, uid, out, "V2IP encoder configuration", value) }
693 })
694}
695
696#[no_mangle]
703pub unsafe extern "C" fn mxr_v2ip_sink(
704 remote: *const mxr_remote_t,
705 uid: mxr_uid_t,
706 out: *mut mxr_v2ip_sink_t,
707) -> mxr_result_t {
708 let handle = unsafe { remote.as_ref() };
710 with(handle, |r| {
711 let value = r
712 .remote
713 .v2ip_sink(uid.into())
714 .map(|s: DeviceV2ipSink| mxr_v2ip_sink_t {
715 addresses: s.addresses.into(),
716 has_audio_format: s.audio_fmt.is_some(),
717 audio_format: {
718 let f = s.audio_fmt.unwrap_or_default();
719 mxr_audio_format_t {
720 sample_rate: f.sample_rate,
721 channels: f.channels,
722 }
723 },
724 });
725 unsafe { fill(r, uid, out, "V2IP sink route", value) }
727 })
728}
729
730#[no_mangle]
737pub unsafe extern "C" fn mxr_v2ip_tiling(
738 remote: *const mxr_remote_t,
739 uid: mxr_uid_t,
740 out: *mut mxr_tiling_config_t,
741) -> mxr_result_t {
742 let handle = unsafe { remote.as_ref() };
744 with(handle, |r| {
745 let value =
746 r.remote
747 .v2ip_tiling(uid.into())
748 .map(|t: V2ipTilingConfig| mxr_tiling_config_t {
749 target: t.target.into(),
750 pos_x: t.pos_x,
751 pos_y: t.pos_y,
752 width: t.width,
753 height: t.height,
754 });
755 unsafe { fill(r, uid, out, "window", value) }
757 })
758}
759
760#[no_mangle]
767pub unsafe extern "C" fn mxr_multiviewer_status(
768 remote: *const mxr_remote_t,
769 uid: mxr_uid_t,
770 out: *mut mxr_multiviewer_status_t,
771) -> mxr_result_t {
772 let handle = unsafe { remote.as_ref() };
774 with(handle, |r| {
775 let value = r.remote.multiviewer_status(uid.into()).map(multiviewer_of);
776 unsafe { fill(r, uid, out, "multiviewer status", value) }
778 })
779}
780
781fn multiviewer_of(s: MultiviewerStatus) -> mxr_multiviewer_status_t {
783 let mut out = mxr_multiviewer_status_t {
784 uid: s.uid.into(),
785 mappings: [mxr_uid_t::default(); MXR_MULTIVIEWER_INPUTS],
786 mcu_version: [0; MXR_NAME_LEN],
787 scaler_version: [0; MXR_NAME_LEN],
788 hw_view_mode: s.hw_view_mode,
789 view_mode: s.view_mode.to_wire(),
790 pip_position: s.pip_position.to_wire(),
791 pip_size: s.pip_size.to_wire(),
792 output_mode: s.output_mode.to_wire(),
793 hdcp_mode: s.hdcp_mode.to_wire(),
794 output_itc: s.output_itc.to_wire(),
795 edid_template: s.edid_template.to_wire(),
796 aspect_ratio: s.aspect_ratio.to_wire(),
797 auto_switch: s.auto_switch.to_wire(),
798 audio_source: s.audio_source.to_wire(),
799 has_audio_volume: s.audio_volume.is_some(),
800 audio_volume: s.audio_volume.unwrap_or(0),
801 audio_muted: s.audio_muted.to_wire(),
802 video_sources: [0; MXR_MULTIVIEWER_INPUTS],
803 remote_control: s.remote_control.to_wire(),
804 };
805 for (slot, uid) in out.mappings.iter_mut().zip(s.mappings) {
806 *slot = uid.into();
807 }
808 for (slot, source) in out.video_sources.iter_mut().zip(s.video_sources) {
809 *slot = source.to_wire();
810 }
811 put_str(&mut out.mcu_version, &s.mcu_version);
812 put_str(&mut out.scaler_version, &s.scaler_version);
813 out
814}
815
816#[no_mangle]
823pub unsafe extern "C" fn mxr_dolby_settings(
824 remote: *const mxr_remote_t,
825 uid: mxr_uid_t,
826 out: *mut mxr_dolby_settings_t,
827) -> mxr_result_t {
828 let handle = unsafe { remote.as_ref() };
830 with(handle, |r| {
831 let value = r
832 .remote
833 .dolby_settings(uid.into())
834 .map(mxr_dolby_settings_t::from);
835 unsafe { fill(r, uid, out, "Dolby settings", value) }
837 })
838}
839
840#[no_mangle]
847pub unsafe extern "C" fn mxr_pdu_state(
848 remote: *const mxr_remote_t,
849 uid: mxr_uid_t,
850 out: *mut mxr_pdu_state_t,
851) -> mxr_result_t {
852 let handle = unsafe { remote.as_ref() };
854 with(handle, |r| {
855 let value = r.remote.pdu_state(uid.into()).map(mxr_pdu_state_t::from);
856 unsafe { fill(r, uid, out, "PDU state", value) }
858 })
859}
860
861#[no_mangle]
868pub unsafe extern "C" fn mxr_rc_settings(
869 remote: *const mxr_remote_t,
870 uid: mxr_uid_t,
871 out: *mut mxr_rc_settings_t,
872) -> mxr_result_t {
873 let handle = unsafe { remote.as_ref() };
875 with(handle, |r| {
876 let value = r.remote.rc_settings(uid.into()).map(rc_settings_of);
877 unsafe { fill(r, uid, out, "remote-control configuration", value) }
879 })
880}
881
882fn rc_settings_of(s: RcSettings) -> mxr_rc_settings_t {
884 let mut out = mxr_rc_settings_t {
885 target: s.target.into(),
886 rc_target: s.rc_target,
887 ip: [0; MXR_IP_STRING_LEN],
888 cec_enabled: s.cec_enabled,
889 cec_auto_on: s.cec_auto_on,
890 forward_rc: s.forward_rc,
891 forward_ir: s.forward_ir,
892 rc_status: s.rc_status,
893 status_name: [0; MXR_NAME_LEN],
894 };
895 put_ip(&mut out.ip, s.ip);
896 put_str(&mut out.status_name, &s.status_name);
897 out
898}
899
900#[no_mangle]
911pub unsafe extern "C" fn mxr_v2ip_sources(
912 remote: *const mxr_remote_t,
913 uid: mxr_uid_t,
914 out: *mut mxr_stream_sources_t,
915 cap: usize,
916) -> usize {
917 guard(0, || {
918 let Some(r) = (unsafe { remote.as_ref() }) else {
920 return no_handle();
921 };
922 let Some(sources) = r.remote.v2ip_sources(uid.into()) else {
923 not_reported(r, uid, "V2IP stream sources");
924 return 0;
925 };
926 unsafe { copy_into(&sources, out, cap) }
928 })
929}
930
931#[no_mangle]
938pub unsafe extern "C" fn mxr_network_status(
939 remote: *const mxr_remote_t,
940 uid: mxr_uid_t,
941 out: *mut mxr_network_port_t,
942 cap: usize,
943) -> usize {
944 guard(0, || {
945 let Some(r) = (unsafe { remote.as_ref() }) else {
947 return no_handle();
948 };
949 let ports: Vec<mxr_network_port_t> = r
950 .remote
951 .network_status(uid.into())
952 .iter()
953 .map(port_of)
954 .collect();
955 unsafe { copy_into(&ports, out, cap) }
957 })
958}
959
960fn port_of(p: &NetworkPortStatus) -> mxr_network_port_t {
962 let errors = p.errors.unwrap_or_default();
963 let mut out = mxr_network_port_t {
964 port: p.port,
965 name: [0; MXR_NAME_LEN],
966 link_speed: p.link_speed.to_wire(),
967 link_full_duplex: p.link_full_duplex,
968 ip: [0; MXR_IP_STRING_LEN],
969 querier: [0; MXR_IP_STRING_LEN],
970 has_mac_address: p.mac_address.is_some(),
971 mac_address: p.mac_address.unwrap_or_default().0,
972 has_errors: p.errors.is_some(),
973 in_error: errors.in_error,
974 in_fcs_error: errors.in_fcs_error,
975 in_collision: errors.in_collision,
976 out_deferred: errors.out_deferred,
977 out_excessive: errors.out_excessive,
978 polarity_error: errors.polarity_error,
979 skew_warning: errors.skew_warning,
980 length_warning: errors.length_warning,
981 has_vct_status: p.vct_status.is_some(),
982 vct_warning: [false; MXR_UTP_PAIRS],
983 cable_status_count: p.cable_status.len().min(MXR_UTP_PAIRS),
984 cable_status: [mxr_cable_status_t {
985 polarity: false,
986 pair: 0,
987 skew: 0,
988 length: 0,
989 }; MXR_UTP_PAIRS],
990 };
991 put_str(&mut out.name, &p.name);
992 put_ip(&mut out.ip, p.ip);
993 put_ip(&mut out.querier, p.querier);
994 if let Some(vct) = p.vct_status {
995 for (slot, status) in out.vct_warning.iter_mut().zip(vct) {
996 *slot = status == VctStatus::Warning;
997 }
998 }
999 for (slot, cable) in out.cable_status.iter_mut().zip(&p.cable_status) {
1000 *slot = (*cable).into();
1001 }
1002 out
1003}
1004
1005#[no_mangle]
1013pub unsafe extern "C" fn mxr_topology(
1014 remote: *const mxr_remote_t,
1015 uid: mxr_uid_t,
1016 out: *mut mxr_topology_entry_t,
1017 cap: usize,
1018) -> usize {
1019 guard(0, || {
1020 let Some(r) = (unsafe { remote.as_ref() }) else {
1022 return no_handle();
1023 };
1024 let topology = r.remote.topology(uid.into());
1025 unsafe { copy_into(&topology, out, cap) }
1027 })
1028}
1029
1030#[no_mangle]
1038pub unsafe extern "C" fn mxr_device_firmware(
1039 remote: *const mxr_remote_t,
1040 uid: mxr_uid_t,
1041 out: *mut mxr_firmware_version_t,
1042 cap: usize,
1043) -> usize {
1044 guard(0, || {
1045 let Some(r) = (unsafe { remote.as_ref() }) else {
1047 return no_handle();
1048 };
1049 let versions: Vec<mxr_firmware_version_t> = r
1050 .remote
1051 .firmware(uid.into())
1052 .iter()
1053 .map(|(_, v)| firmware_of(v))
1054 .collect();
1055 unsafe { copy_into(&versions, out, cap) }
1057 })
1058}
1059
1060fn firmware_of(v: &FirmwareVersion) -> mxr_firmware_version_t {
1062 let mut out = mxr_firmware_version_t {
1063 firmware_type: v.firmware_type.to_wire(),
1064 timestamp: v.timestamp,
1065 hash: v.hash,
1066 version: [0; MXR_VERSION_LEN],
1067 };
1068 put_str(&mut out.version, &v.version);
1069 out
1070}
1071
1072#[no_mangle]
1080pub unsafe extern "C" fn mxr_audio_endpoints(
1081 remote: *const mxr_remote_t,
1082 uid: mxr_uid_t,
1083 out: *mut mxr_audio_endpoint_t,
1084 cap: usize,
1085) -> usize {
1086 guard(0, || {
1087 let Some(r) = (unsafe { remote.as_ref() }) else {
1089 return no_handle();
1090 };
1091 let Some(endpoints) = r.remote.audio_endpoints(uid.into()) else {
1092 not_reported(r, uid, "audio endpoints");
1093 return 0;
1094 };
1095 let list: Vec<mxr_audio_endpoint_t> = endpoints.list().map(Into::into).collect();
1096 unsafe { copy_into(&list, out, cap) }
1098 })
1099}
1100
1101#[no_mangle]
1109pub unsafe extern "C" fn mxr_audio_endpoint_children(
1110 remote: *const mxr_remote_t,
1111 uid: mxr_uid_t,
1112 endpoint: u8,
1113 out: *mut u8,
1114 cap: usize,
1115) -> usize {
1116 guard(0, || {
1117 let Some(r) = (unsafe { remote.as_ref() }) else {
1119 return no_handle();
1120 };
1121 let children = match r.remote.audio_endpoints(uid.into()) {
1122 Some(endpoints) => match endpoints.get(endpoint) {
1123 Some(e) => e.children.clone(),
1124 None => {
1125 fail(
1126 mxr_result_t::MXR_ERR_NOT_FOUND,
1127 &format!("the device has no audio endpoint {endpoint}"),
1128 );
1129 return 0;
1130 }
1131 },
1132 None => {
1133 not_reported(r, uid, "audio endpoints");
1134 return 0;
1135 }
1136 };
1137 unsafe { copy_into(&children, out, cap) }
1139 })
1140}
1141
1142fn no_handle() -> usize {
1144 fail(
1145 mxr_result_t::MXR_ERR_INVALID_ARGUMENT,
1146 "the client handle is null",
1147 );
1148 0
1149}