1use std::future::{Future, IntoFuture};
4use std::pin::Pin;
5use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
6use std::task::{Context, Poll};
7use std::time::Duration;
8
9use nusb::MaybeFuture;
10
11use crate::Complex32;
12use crate::config::{
13 Config, ConfigBuilder, validate_frequency, validate_lna_gain, validate_sample_rate,
14 validate_vga_gain,
15};
16use crate::device::{HackRf, shutdown_hardware};
17use crate::errors::{Error, Result};
18use crate::maybe_future::{MaybeFutureExt, ready};
19use crate::streaming::{AsyncDirectRxStream, AsyncStreamingBackend, StreamingStats};
20#[cfg(not(target_arch = "wasm32"))]
21use crate::streaming::{DirectRxStream, StreamingBackend};
22use crate::types::DeviceInfo;
23use crate::usb::{ControlBackend, NusbControl};
24
25#[cfg(not(target_arch = "wasm32"))]
26type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
27#[cfg(target_arch = "wasm32")]
28type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
29
30#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31enum DeviceLifecycle {
32 Open,
33 Closing,
34 DropCleanupPending,
35 Closed,
36}
37
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39enum ReceiverSlot {
40 Idle,
41 Held,
42 Orphaned,
43}
44
45#[derive(Debug)]
46struct SharedDeviceState {
47 device: DeviceLifecycle,
48 receiver: ReceiverSlot,
49 stream_claimed: bool,
50 bias_tee_enabled: bool,
51}
52
53impl SharedDeviceState {
54 fn new(bias_tee_enabled: bool) -> Self {
55 Self {
56 device: DeviceLifecycle::Open,
57 receiver: ReceiverSlot::Idle,
58 stream_claimed: false,
59 bias_tee_enabled,
60 }
61 }
62}
63
64type SharedState = Arc<Mutex<SharedDeviceState>>;
65
66fn lock_shared(state: &SharedState) -> MutexGuard<'_, SharedDeviceState> {
67 state.lock().unwrap_or_else(PoisonError::into_inner)
68}
69
70fn ensure_device_open(state: &SharedState) -> Result<()> {
71 if lock_shared(state).device == DeviceLifecycle::Open {
72 Ok(())
73 } else {
74 Err(Error::DeviceClosed)
75 }
76}
77
78fn desired_bias_tee(state: &SharedState) -> bool {
79 lock_shared(state).bias_tee_enabled
80}
81
82fn begin_shutdown(state: &SharedState) -> Result<bool> {
83 let mut shared = lock_shared(state);
84 match shared.device {
85 DeviceLifecycle::Closed | DeviceLifecycle::DropCleanupPending => Ok(false),
86 DeviceLifecycle::Closing => Ok(true),
87 DeviceLifecycle::Open => {
88 if shared.receiver == ReceiverSlot::Held {
89 return Err(Error::Busy);
90 }
91 shared.device = DeviceLifecycle::Closing;
92 Ok(true)
93 }
94 }
95}
96
97fn complete_shutdown(state: &SharedState, result: Result<()>) -> Result<()> {
98 if result.is_ok() {
99 let mut shared = lock_shared(state);
100 shared.device = DeviceLifecycle::Closed;
101 shared.receiver = ReceiverSlot::Idle;
102 }
103 result
104}
105
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
107enum DeviceDropAction {
108 None,
109 Immediate,
110 Deferred,
111}
112
113fn begin_device_drop(state: &SharedState) -> DeviceDropAction {
114 let mut shared = lock_shared(state);
115 match shared.device {
116 DeviceLifecycle::Closed => DeviceDropAction::None,
117 DeviceLifecycle::DropCleanupPending => DeviceDropAction::Deferred,
118 DeviceLifecycle::Open | DeviceLifecycle::Closing => {
119 if shared.receiver == ReceiverSlot::Held {
120 shared.device = DeviceLifecycle::DropCleanupPending;
121 DeviceDropAction::Deferred
122 } else {
123 shared.device = DeviceLifecycle::Closed;
124 shared.receiver = ReceiverSlot::Idle;
125 DeviceDropAction::Immediate
126 }
127 }
128 }
129}
130
131#[derive(Debug)]
132struct DeviceInner<C: ControlBackend = NusbControl> {
133 direct: HackRf<C>,
134 info: DeviceInfo,
135 #[cfg(not(target_arch = "wasm32"))]
136 shutdown_on_drop: bool,
137}
138
139impl<C: ControlBackend> DeviceInner<C> {
140 fn stream_handle(&self) -> Self {
141 Self {
142 direct: self.direct.stream_handle(),
143 info: self.info.clone(),
144 #[cfg(not(target_arch = "wasm32"))]
145 shutdown_on_drop: false,
146 }
147 }
148}
149
150#[cfg(not(target_arch = "wasm32"))]
151impl<C: ControlBackend> Drop for DeviceInner<C> {
152 fn drop(&mut self) {
153 if self.shutdown_on_drop {
154 let _ = shutdown_hardware(&self.direct).wait();
155 }
156 }
157}
158
159#[derive(Debug)]
161pub struct Device {
162 inner: DeviceInner<NusbControl>,
163 config: Config,
164 shared: SharedState,
165}
166
167impl Device {
168 pub fn list() -> impl MaybeFuture<Output = Result<Vec<crate::DeviceDescriptor>>> {
170 crate::discovery::list_devices()
171 }
172
173 pub fn builder() -> DeviceBuilder {
175 DeviceBuilder::default()
176 }
177
178 pub fn open() -> impl MaybeFuture<Output = Result<Self>> {
180 Self::builder().open()
181 }
182
183 pub fn open_serial(serial: u128) -> impl MaybeFuture<Output = Result<Self>> {
185 Self::builder().serial(serial).open()
186 }
187
188 #[cfg(target_arch = "wasm32")]
192 pub async fn request_permission() -> Result<()> {
193 Self::builder().request_permission().await
194 }
195
196 pub fn info(&self) -> &DeviceInfo {
198 &self.inner.info
199 }
200
201 pub fn config(&self) -> &Config {
203 &self.config
204 }
205
206 pub fn configure<'a>(
208 &'a mut self,
209 config: &'a Config,
210 ) -> impl MaybeFuture<Output = Result<()>> + 'a {
211 let lifecycle = ensure_device_open(&self.shared);
212 let applied = config.clone();
213 let active = &mut self.config;
214 let shared = Arc::clone(&self.shared);
215 let operation = self.inner.direct.configure(config);
216 ready(lifecycle)
217 .and_then(move |()| operation)
218 .map(move |result| {
219 if result.is_ok() {
220 *active = applied;
221 lock_shared(&shared).bias_tee_enabled = active.bias_tee_enabled();
222 }
223 result
224 })
225 }
226
227 pub fn set_frequency_hz(
229 &mut self,
230 frequency_hz: u64,
231 ) -> impl MaybeFuture<Output = Result<()>> + '_ {
232 let validation =
233 ensure_device_open(&self.shared).and_then(|()| validate_frequency(frequency_hz));
234 let config = &mut self.config;
235 let operation = self.inner.direct.set_frequency(frequency_hz);
236 ready(validation)
237 .and_then(move |()| operation)
238 .map(move |result| {
239 if result.is_ok() {
240 config.set_frequency_hz_internal(frequency_hz);
241 }
242 result
243 })
244 }
245
246 pub fn set_sample_rate_hz(
250 &mut self,
251 sample_rate_hz: u32,
252 ) -> impl MaybeFuture<Output = Result<()>> + '_ {
253 let validation =
254 ensure_device_open(&self.shared).and_then(|()| validate_sample_rate(sample_rate_hz));
255 let config = &mut self.config;
256 let operation = self.inner.direct.set_sample_rate(sample_rate_hz);
257 ready(validation)
258 .and_then(move |()| operation)
259 .map(move |result| {
260 if result.is_ok() {
261 config.set_sample_rate_hz_internal(sample_rate_hz);
262 }
263 result
264 })
265 }
266
267 pub fn set_lna_gain_db(&mut self, gain_db: u8) -> impl MaybeFuture<Output = Result<()>> + '_ {
269 let validation = ensure_device_open(&self.shared).and_then(|()| validate_lna_gain(gain_db));
270 let config = &mut self.config;
271 let operation = self.inner.direct.set_lna_gain(gain_db);
272 ready(validation)
273 .and_then(move |()| operation)
274 .map(move |result| {
275 if result.is_ok() {
276 config.set_lna_gain_db_internal(gain_db);
277 }
278 result
279 })
280 }
281
282 pub fn set_vga_gain_db(&mut self, gain_db: u8) -> impl MaybeFuture<Output = Result<()>> + '_ {
284 let validation = ensure_device_open(&self.shared).and_then(|()| validate_vga_gain(gain_db));
285 let config = &mut self.config;
286 let operation = self.inner.direct.set_vga_gain(gain_db);
287 ready(validation)
288 .and_then(move |()| operation)
289 .map(move |result| {
290 if result.is_ok() {
291 config.set_vga_gain_db_internal(gain_db);
292 }
293 result
294 })
295 }
296
297 pub fn set_amp_enable(&mut self, enabled: bool) -> impl MaybeFuture<Output = Result<()>> + '_ {
299 let lifecycle = ensure_device_open(&self.shared);
300 let config = &mut self.config;
301 let operation = self.inner.direct.set_amp(enabled);
302 ready(lifecycle)
303 .and_then(move |()| operation)
304 .map(move |result| {
305 if result.is_ok() {
306 config.set_amp_enabled_internal(enabled);
307 }
308 result
309 })
310 }
311
312 pub fn set_bias_tee(&mut self, enabled: bool) -> impl MaybeFuture<Output = Result<()>> + '_ {
317 let lifecycle = ensure_device_open(&self.shared);
318 let config = &mut self.config;
319 let shared = Arc::clone(&self.shared);
320 let operation = self.inner.direct.set_bias_tee(enabled);
321 ready(lifecycle)
322 .and_then(move |()| operation)
323 .map(move |result| {
324 if result.is_ok() {
325 config.set_bias_tee_enabled_internal(enabled);
326 lock_shared(&shared).bias_tee_enabled = enabled;
327 }
328 result
329 })
330 }
331
332 pub fn rx_stream(&self) -> Result<RxStream> {
337 let claim = RxStreamClaim::acquire(&self.shared)?;
338 Ok(RxStream::new(
339 self.inner.stream_handle(),
340 Arc::clone(&self.shared),
341 claim,
342 ))
343 }
344
345 #[must_use = "shutdown must be waited or awaited to observe hardware cleanup"]
350 pub fn shutdown(&mut self) -> impl MaybeFuture<Output = Result<()>> + '_ {
351 let decision = begin_shutdown(&self.shared);
352 let shared = Arc::clone(&self.shared);
353 let operation = shutdown_hardware(&self.inner.direct);
354 ready(decision).and_then(move |run| {
355 if run {
356 crate::maybe_future::Either::left(
357 operation.map(move |result| complete_shutdown(&shared, result)),
358 )
359 } else {
360 crate::maybe_future::Either::right(ready(Ok(())))
361 }
362 })
363 }
364}
365
366impl Drop for Device {
367 fn drop(&mut self) {
368 let action = begin_device_drop(&self.shared);
369 #[cfg(not(target_arch = "wasm32"))]
370 if action != DeviceDropAction::Immediate {
371 self.inner.shutdown_on_drop = false;
372 }
373 #[cfg(target_arch = "wasm32")]
374 if action == DeviceDropAction::Immediate {
375 let direct = self.inner.direct.stream_handle();
376 wasm_bindgen_futures::spawn_local(async move {
377 let _ = shutdown_hardware(&direct).await;
378 });
379 }
380 }
381}
382
383#[derive(Clone, Debug, Default)]
385pub struct DeviceBuilder {
386 serial: Option<u128>,
387 config: ConfigBuilder,
388}
389
390impl DeviceBuilder {
391 pub fn serial(mut self, serial: u128) -> Self {
393 self.serial = Some(serial);
394 self
395 }
396
397 pub fn frequency_hz(mut self, value: u64) -> Self {
399 self.config = self.config.frequency_hz(value);
400 self
401 }
402
403 pub fn sample_rate_hz(mut self, value: u32) -> Self {
405 self.config = self.config.sample_rate_hz(value);
406 self
407 }
408
409 pub fn lna_gain_db(mut self, value: u8) -> Self {
411 self.config = self.config.lna_gain_db(value);
412 self
413 }
414
415 pub fn vga_gain_db(mut self, value: u8) -> Self {
417 self.config = self.config.vga_gain_db(value);
418 self
419 }
420
421 pub fn amp_enable(mut self, enabled: bool) -> Self {
423 self.config = self.config.amp_enable(enabled);
424 self
425 }
426
427 pub fn bias_tee(mut self, enabled: bool) -> Self {
429 self.config = self.config.bias_tee(enabled);
430 self
431 }
432
433 pub fn open(self) -> impl MaybeFuture<Output = Result<Device>> {
435 let config = self.config.build();
436 let serial = self.serial;
437 ready(config).and_then(move |config| {
438 HackRf::open(serial).and_then(move |(direct, usb_api_version)| {
439 let info = direct.fetch_device_info(usb_api_version);
440 info.and_then(move |info| {
441 let applied = config.clone();
442 direct.configure(&config).map(move |result| {
443 result?;
444 Ok(Device {
445 inner: DeviceInner {
446 direct,
447 info,
448 #[cfg(not(target_arch = "wasm32"))]
449 shutdown_on_drop: true,
450 },
451 shared: Arc::new(Mutex::new(SharedDeviceState::new(
452 applied.bias_tee_enabled(),
453 ))),
454 config: applied,
455 })
456 })
457 })
458 })
459 })
460 }
461
462 #[cfg(target_arch = "wasm32")]
464 pub async fn request_permission(&self) -> Result<()> {
465 crate::discovery::request_device_permission(self.serial).await
466 }
467}
468
469#[derive(Clone, Copy, Debug, Eq, PartialEq)]
470enum ReceiverState {
471 Stopped,
472 CleanupRequired,
473 Running,
474}
475
476#[cfg(not(target_arch = "wasm32"))]
477struct BlockingRxInner<C: ControlBackend + StreamingBackend> {
478 device: DeviceInner<C>,
479 stream: Option<DirectRxStream<C::BulkIn>>,
480 stats: StreamingStats,
481 state: ReceiverState,
482}
483
484#[cfg(not(target_arch = "wasm32"))]
485impl<C> BlockingRxInner<C>
486where
487 C: ControlBackend + StreamingBackend,
488{
489 fn new(device: DeviceInner<C>) -> Self {
490 Self {
491 device,
492 stream: None,
493 stats: StreamingStats::default(),
494 state: ReceiverState::Stopped,
495 }
496 }
497
498 fn start(&mut self, bias_tee: bool) -> Result<()> {
499 match self.state {
500 ReceiverState::Running => return Ok(()),
501 ReceiverState::CleanupRequired => return Err(Error::Busy),
502 ReceiverState::Stopped => {}
503 }
504 self.state = ReceiverState::CleanupRequired;
505 self.stream = Some(self.device.direct.start_rx_blocking(bias_tee)?);
506 self.state = ReceiverState::Running;
507 Ok(())
508 }
509
510 fn read(&mut self, out: &mut [Complex32], timeout: Duration) -> Result<usize> {
511 if self.state != ReceiverState::Running {
512 return Err(Error::stream_closed("RX stream is stopped"));
513 }
514 let result = self
515 .stream
516 .as_mut()
517 .ok_or(Error::stream_closed("RX stream has no USB queue"))?
518 .read_complex(out, timeout);
519 if result.is_err() {
520 self.state = ReceiverState::CleanupRequired;
521 }
522 result
523 }
524
525 fn stop(&mut self) -> Result<StreamingStats> {
526 if self.state == ReceiverState::Stopped {
527 return Ok(self.stats);
528 }
529 let result = if let Some(stream) = self.stream.take() {
530 let (stats, result) = self.device.direct.stop_rx_blocking(stream);
531 self.stats.accumulate(stats);
532 result
533 } else {
534 shutdown_hardware(&self.device.direct).wait()
535 };
536 match result {
537 Ok(()) => {
538 self.state = ReceiverState::Stopped;
539 Ok(self.stats)
540 }
541 Err(error) => {
542 self.state = ReceiverState::CleanupRequired;
543 Err(error)
544 }
545 }
546 }
547}
548
549struct AsyncRxInner<C: ControlBackend + AsyncStreamingBackend> {
550 device: DeviceInner<C>,
551 stream: Option<AsyncDirectRxStream<C::BulkIn>>,
552 stats: StreamingStats,
553 state: ReceiverState,
554}
555
556impl<C> AsyncRxInner<C>
557where
558 C: ControlBackend + AsyncStreamingBackend,
559{
560 fn new(device: DeviceInner<C>) -> Self {
561 Self {
562 device,
563 stream: None,
564 stats: StreamingStats::default(),
565 state: ReceiverState::Stopped,
566 }
567 }
568
569 async fn start(&mut self, bias_tee: bool) -> Result<()> {
570 match self.state {
571 ReceiverState::Running => return Ok(()),
572 ReceiverState::CleanupRequired => return Err(Error::Busy),
573 ReceiverState::Stopped => {}
574 }
575 self.state = ReceiverState::CleanupRequired;
576 if self.stream.is_some() {
577 self.device.direct.restart_rx_async(bias_tee).await?;
578 } else {
579 self.stream = Some(self.device.direct.start_rx_async(bias_tee).await?);
580 }
581 self.state = ReceiverState::Running;
582 Ok(())
583 }
584
585 fn poll_read(&mut self, out: &mut [Complex32], cx: &mut Context<'_>) -> Poll<Result<usize>> {
586 if self.state != ReceiverState::Running {
587 return Poll::Ready(Err(Error::stream_closed("async RX stream is stopped")));
588 }
589 let result = match self.stream.as_mut() {
590 Some(stream) => stream.poll_read_complex(out, cx),
591 None => Poll::Ready(Err(Error::stream_closed(
592 "async RX stream has no USB queue",
593 ))),
594 };
595 if matches!(result, Poll::Ready(Err(_))) {
596 self.state = ReceiverState::CleanupRequired;
597 }
598 result
599 }
600
601 async fn stop(&mut self) -> Result<StreamingStats> {
602 if self.state == ReceiverState::Stopped {
603 return Ok(self.current_stats());
604 }
605 if let Some(stream) = self.stream.as_mut() {
606 self.device.direct.stop_rx_async(stream).await?;
607 } else {
608 shutdown_hardware(&self.device.direct).await?;
609 }
610 if self
611 .stream
612 .as_ref()
613 .is_some_and(AsyncDirectRxStream::is_closed)
614 {
615 let mut stream = self.stream.take().expect("closed stream checked");
616 self.stats.accumulate(stream.close());
617 }
618 self.state = ReceiverState::Stopped;
619 Ok(self.current_stats())
620 }
621
622 #[cfg(not(target_arch = "wasm32"))]
623 fn stop_on_drop(&mut self) -> Result<StreamingStats> {
624 if self.state == ReceiverState::Stopped {
625 return Ok(self.current_stats());
626 }
627 shutdown_hardware(&self.device.direct).wait()?;
628 if let Some(stream) = self.stream.as_mut() {
629 stream.pause()?;
630 }
631 self.state = ReceiverState::Stopped;
632 Ok(self.current_stats())
633 }
634
635 fn current_stats(&self) -> StreamingStats {
636 self.stream
637 .as_ref()
638 .map_or(self.stats, |stream| self.stats.combined(stream.stats()))
639 }
640}
641
642enum RxStreamState {
643 Dormant(DeviceInner<NusbControl>),
644 Poisoned,
645 #[cfg(not(target_arch = "wasm32"))]
646 Blocking(BlockingRxInner<NusbControl>),
647 Async(AsyncRxInner<NusbControl>),
648}
649
650#[derive(Debug)]
651struct RxStreamClaim {
652 shared: SharedState,
653}
654
655impl RxStreamClaim {
656 fn acquire(shared: &SharedState) -> Result<Self> {
657 let mut state = lock_shared(shared);
658 if state.device != DeviceLifecycle::Open {
659 return Err(Error::DeviceClosed);
660 }
661 if state.stream_claimed || state.receiver != ReceiverSlot::Idle {
662 return Err(Error::Busy);
663 }
664 state.stream_claimed = true;
665 Ok(Self {
666 shared: Arc::clone(shared),
667 })
668 }
669}
670
671impl Drop for RxStreamClaim {
672 fn drop(&mut self) {
673 lock_shared(&self.shared).stream_claimed = false;
674 }
675}
676
677#[derive(Debug)]
678struct ReceiverLease {
679 shared: SharedState,
680 armed: bool,
681}
682
683impl ReceiverLease {
684 fn acquire(shared: &SharedState) -> Result<Self> {
685 let mut state = lock_shared(shared);
686 if state.device != DeviceLifecycle::Open {
687 return Err(Error::DeviceClosed);
688 }
689 if state.receiver != ReceiverSlot::Idle {
690 return Err(Error::Busy);
691 }
692 state.receiver = ReceiverSlot::Held;
693 Ok(Self {
694 shared: Arc::clone(shared),
695 armed: true,
696 })
697 }
698
699 fn release(mut self) {
700 let mut state = lock_shared(&self.shared);
701 if state.receiver == ReceiverSlot::Held {
702 state.receiver = ReceiverSlot::Idle;
703 }
704 if state.device == DeviceLifecycle::DropCleanupPending {
705 state.device = DeviceLifecycle::Closed;
706 }
707 self.armed = false;
708 }
709}
710
711impl Drop for ReceiverLease {
712 fn drop(&mut self) {
713 if self.armed {
714 let mut state = lock_shared(&self.shared);
715 if state.receiver == ReceiverSlot::Held {
716 state.receiver = ReceiverSlot::Orphaned;
717 }
718 }
719 }
720}
721
722#[must_use = "RX streams retain the device's exclusive stream claim until dropped"]
729pub struct RxStream {
730 state: RxStreamState,
731 shared: SharedState,
732 receiver: Option<ReceiverLease>,
733 _claim: RxStreamClaim,
734}
735
736impl RxStream {
737 fn new(device: DeviceInner<NusbControl>, shared: SharedState, claim: RxStreamClaim) -> Self {
738 Self {
739 state: RxStreamState::Dormant(device),
740 shared,
741 receiver: None,
742 _claim: claim,
743 }
744 }
745
746 pub fn start(&mut self) -> impl MaybeFuture<Output = Result<()>> + '_ {
748 StartOperation { stream: self }
749 }
750
751 pub fn read<'a>(
757 &'a mut self,
758 out: &'a mut [Complex32],
759 timeout: Option<Duration>,
760 ) -> impl MaybeFuture<Output = Result<usize>> + 'a {
761 ReadOperation {
762 stream: self,
763 out,
764 timeout,
765 }
766 }
767
768 pub fn stop(&mut self) -> impl MaybeFuture<Output = Result<StreamingStats>> + '_ {
770 StopOperation { stream: self }
771 }
772
773 fn receiver_state(&self) -> ReceiverState {
774 match &self.state {
775 RxStreamState::Dormant(_) => ReceiverState::Stopped,
776 RxStreamState::Poisoned => ReceiverState::CleanupRequired,
777 #[cfg(not(target_arch = "wasm32"))]
778 RxStreamState::Blocking(stream) => stream.state,
779 RxStreamState::Async(stream) => stream.state,
780 }
781 }
782
783 fn begin_start(&mut self) -> Result<bool> {
784 ensure_device_open(&self.shared)?;
785 match (self.receiver.is_some(), self.receiver_state()) {
786 (true, ReceiverState::Running) => Ok(false),
787 (true, ReceiverState::Stopped | ReceiverState::CleanupRequired)
788 | (false, ReceiverState::Running | ReceiverState::CleanupRequired) => Err(Error::Busy),
789 (false, ReceiverState::Stopped) => {
790 self.receiver = Some(ReceiverLease::acquire(&self.shared)?);
791 Ok(true)
792 }
793 }
794 }
795
796 fn finish_stop(&mut self, result: Result<StreamingStats>) -> Result<StreamingStats> {
797 let stats = result?;
798 if let Some(receiver) = self.receiver.take() {
799 receiver.release();
800 }
801 Ok(stats)
802 }
803
804 #[cfg(not(target_arch = "wasm32"))]
805 fn initialize_blocking(&mut self) -> Result<()> {
806 if matches!(self.state, RxStreamState::Blocking(_)) {
807 return Ok(());
808 }
809 if matches!(self.state, RxStreamState::Async(_)) {
810 return Err(Error::Busy);
811 }
812 let state = core::mem::replace(&mut self.state, RxStreamState::Poisoned);
813 match state {
814 RxStreamState::Dormant(device) => {
815 self.state = RxStreamState::Blocking(BlockingRxInner::new(device));
816 Ok(())
817 }
818 other => {
819 self.state = other;
820 Err(Error::stream_closed("RX stream has no device"))
821 }
822 }
823 }
824
825 fn initialize_async(&mut self) -> Result<()> {
826 if matches!(self.state, RxStreamState::Async(_)) {
827 return Ok(());
828 }
829 #[cfg(not(target_arch = "wasm32"))]
830 if matches!(self.state, RxStreamState::Blocking(_)) {
831 return Err(Error::Busy);
832 }
833 let state = core::mem::replace(&mut self.state, RxStreamState::Poisoned);
834 match state {
835 RxStreamState::Dormant(device) => {
836 self.state = RxStreamState::Async(AsyncRxInner::new(device));
837 Ok(())
838 }
839 other => {
840 self.state = other;
841 Err(Error::stream_closed("RX stream has no device"))
842 }
843 }
844 }
845
846 #[cfg(not(target_arch = "wasm32"))]
847 fn start_blocking(&mut self) -> Result<()> {
848 self.initialize_blocking()?;
849 if !self.begin_start()? {
850 return Ok(());
851 }
852 let bias = desired_bias_tee(&self.shared);
853 match &mut self.state {
854 RxStreamState::Blocking(stream) => stream.start(bias),
855 _ => Err(Error::Busy),
856 }
857 }
858
859 async fn start_async(&mut self) -> Result<()> {
860 self.initialize_async()?;
861 if !self.begin_start()? {
862 return Ok(());
863 }
864 let bias = desired_bias_tee(&self.shared);
865 match &mut self.state {
866 RxStreamState::Async(stream) => stream.start(bias).await,
867 _ => Err(Error::Busy),
868 }
869 }
870
871 #[cfg(not(target_arch = "wasm32"))]
872 fn read_blocking(&mut self, out: &mut [Complex32], timeout: Duration) -> Result<usize> {
873 match &mut self.state {
874 RxStreamState::Blocking(stream) => stream.read(out, timeout),
875 _ => Err(Error::stream_closed(
876 "RX stream is not running synchronously",
877 )),
878 }
879 }
880
881 fn begin_read_async(&mut self) -> Result<()> {
882 match &self.state {
883 RxStreamState::Async(stream) if stream.state == ReceiverState::Running => Ok(()),
884 _ => Err(Error::stream_closed(
885 "RX stream is not running asynchronously",
886 )),
887 }
888 }
889
890 fn poll_read_async(
891 &mut self,
892 out: &mut [Complex32],
893 cx: &mut Context<'_>,
894 ) -> Poll<Result<usize>> {
895 match &mut self.state {
896 RxStreamState::Async(stream) => stream.poll_read(out, cx),
897 _ => Poll::Ready(Err(Error::stream_closed(
898 "RX stream is not running asynchronously",
899 ))),
900 }
901 }
902
903 #[cfg(not(target_arch = "wasm32"))]
904 fn stop_blocking(&mut self) -> Result<StreamingStats> {
905 let result = match &mut self.state {
906 RxStreamState::Dormant(_) => Ok(StreamingStats::default()),
907 RxStreamState::Blocking(stream) => stream.stop(),
908 RxStreamState::Async(_) => Err(Error::Busy),
909 RxStreamState::Poisoned => Err(Error::stream_closed("RX stream has no device")),
910 };
911 self.finish_stop(result)
912 }
913
914 async fn stop_async(&mut self) -> Result<StreamingStats> {
915 let result = match &mut self.state {
916 RxStreamState::Dormant(_) => Ok(StreamingStats::default()),
917 RxStreamState::Async(stream) => stream.stop().await,
918 #[cfg(not(target_arch = "wasm32"))]
919 RxStreamState::Blocking(_) => Err(Error::Busy),
920 RxStreamState::Poisoned => Err(Error::stream_closed("RX stream has no device")),
921 };
922 self.finish_stop(result)
923 }
924
925 #[cfg(target_arch = "wasm32")]
926 fn control_handle(&self) -> Option<HackRf<NusbControl>> {
927 match &self.state {
928 RxStreamState::Dormant(device) => Some(device.direct.stream_handle()),
929 #[cfg(not(target_arch = "wasm32"))]
930 RxStreamState::Blocking(stream) => Some(stream.device.direct.stream_handle()),
931 RxStreamState::Async(stream) => Some(stream.device.direct.stream_handle()),
932 RxStreamState::Poisoned => None,
933 }
934 }
935
936 #[cfg(not(target_arch = "wasm32"))]
937 fn cleanup_on_drop(&mut self) -> Result<()> {
938 match &mut self.state {
939 RxStreamState::Dormant(_) => Ok(()),
940 RxStreamState::Blocking(stream) => stream.stop().map(|_| ()),
941 RxStreamState::Async(stream) => stream.stop_on_drop().map(|_| ()),
942 RxStreamState::Poisoned => Err(Error::stream_closed("RX stream has no device")),
943 }
944 }
945}
946
947impl Drop for RxStream {
948 fn drop(&mut self) {
949 #[cfg(not(target_arch = "wasm32"))]
950 if self.receiver.is_some()
951 && self.cleanup_on_drop().is_ok()
952 && let Some(receiver) = self.receiver.take()
953 {
954 receiver.release();
955 }
956 #[cfg(target_arch = "wasm32")]
957 if let Some(direct) = self.control_handle()
958 && let Some(receiver) = self.receiver.take()
959 {
960 wasm_bindgen_futures::spawn_local(async move {
961 if shutdown_hardware(&direct).await.is_ok() {
962 receiver.release();
963 }
964 });
965 }
966 }
967}
968
969struct StartOperation<'a> {
970 stream: &'a mut RxStream,
971}
972
973impl<'a> IntoFuture for StartOperation<'a> {
974 type Output = Result<()>;
975 type IntoFuture = BoxFuture<'a, Result<()>>;
976
977 fn into_future(self) -> Self::IntoFuture {
978 Box::pin(async move { self.stream.start_async().await })
979 }
980}
981
982impl MaybeFuture for StartOperation<'_> {
983 #[cfg(not(target_arch = "wasm32"))]
984 fn wait(self) -> Self::Output {
985 self.stream.start_blocking()
986 }
987}
988
989struct StopOperation<'a> {
990 stream: &'a mut RxStream,
991}
992
993impl<'a> IntoFuture for StopOperation<'a> {
994 type Output = Result<StreamingStats>;
995 type IntoFuture = BoxFuture<'a, Result<StreamingStats>>;
996
997 fn into_future(self) -> Self::IntoFuture {
998 Box::pin(async move { self.stream.stop_async().await })
999 }
1000}
1001
1002impl MaybeFuture for StopOperation<'_> {
1003 #[cfg(not(target_arch = "wasm32"))]
1004 fn wait(self) -> Self::Output {
1005 self.stream.stop_blocking()
1006 }
1007}
1008
1009struct ReadOperation<'a> {
1010 stream: &'a mut RxStream,
1011 out: &'a mut [Complex32],
1012 timeout: Option<Duration>,
1013}
1014
1015impl<'a> IntoFuture for ReadOperation<'a> {
1016 type Output = Result<usize>;
1017 type IntoFuture = ReadFuture<'a>;
1018
1019 fn into_future(self) -> Self::IntoFuture {
1020 let _ = self.timeout;
1021 ReadFuture {
1022 stream: self.stream,
1023 out: self.out,
1024 initialized: false,
1025 completed: false,
1026 }
1027 }
1028}
1029
1030impl MaybeFuture for ReadOperation<'_> {
1031 #[cfg(not(target_arch = "wasm32"))]
1032 fn wait(self) -> Self::Output {
1033 self.stream
1034 .read_blocking(self.out, self.timeout.unwrap_or(Duration::MAX))
1035 }
1036}
1037
1038struct ReadFuture<'a> {
1039 stream: &'a mut RxStream,
1040 out: &'a mut [Complex32],
1041 initialized: bool,
1042 completed: bool,
1043}
1044
1045impl Future for ReadFuture<'_> {
1046 type Output = Result<usize>;
1047
1048 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1049 let this = &mut *self;
1050 assert!(!this.completed, "read future polled after completion");
1051 if !this.initialized {
1052 if let Err(error) = this.stream.begin_read_async() {
1053 this.completed = true;
1054 return Poll::Ready(Err(error));
1055 }
1056 this.initialized = true;
1057 }
1058 match this.stream.poll_read_async(this.out, cx) {
1059 Poll::Pending => Poll::Pending,
1060 Poll::Ready(result) => {
1061 this.completed = true;
1062 Poll::Ready(result)
1063 }
1064 }
1065 }
1066}
1067
1068#[cfg(test)]
1069mod tests {
1070 use super::*;
1071
1072 fn shared_state() -> SharedState {
1073 Arc::new(Mutex::new(SharedDeviceState::new(false)))
1074 }
1075
1076 #[test]
1077 fn stream_claim_is_exclusive_and_released_on_drop() {
1078 let shared = shared_state();
1079 let claim = RxStreamClaim::acquire(&shared).unwrap();
1080 assert_eq!(
1081 RxStreamClaim::acquire(&shared).unwrap_err().kind(),
1082 crate::ErrorKind::Busy
1083 );
1084 drop(claim);
1085 assert!(RxStreamClaim::acquire(&shared).is_ok());
1086 }
1087
1088 #[test]
1089 fn shutdown_is_busy_only_while_receiver_lease_is_held() {
1090 let shared = shared_state();
1091 let receiver = ReceiverLease::acquire(&shared).unwrap();
1092 assert_eq!(
1093 begin_shutdown(&shared).unwrap_err().kind(),
1094 crate::ErrorKind::Busy
1095 );
1096 receiver.release();
1097 assert!(begin_shutdown(&shared).unwrap());
1098 }
1099
1100 #[test]
1101 fn device_drop_defers_closed_state_to_active_receiver() {
1102 let shared = shared_state();
1103 let receiver = ReceiverLease::acquire(&shared).unwrap();
1104 assert_eq!(begin_device_drop(&shared), DeviceDropAction::Deferred);
1105 assert_eq!(
1106 lock_shared(&shared).device,
1107 DeviceLifecycle::DropCleanupPending
1108 );
1109 receiver.release();
1110 assert_eq!(lock_shared(&shared).device, DeviceLifecycle::Closed);
1111 }
1112}