1#![deny(unsafe_code)]
2
3#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]
42#![warn(missing_docs)]
44
45mod types;
46pub use types::*;
47
48use std::sync::atomic::Ordering;
49
50use futures_lite::future::block_on;
51use nusb::{
52 transfer::{ControlIn, ControlOut, ControlType, Queue, Recipient, RequestBuffer},
53 DeviceInfo,
54};
55
56const HACKRF_USB_VID: u16 = 0x1D50;
58const HACKRF_ONE_USB_PID: u16 = 0x6089;
60
61pub struct HackRf {
63 interface: nusb::Interface,
64 version: UsbVersion,
65 mode: AtomicMode,
66}
67
68impl HackRf {
69 pub fn open(info: DeviceInfo) -> Result<Self> {
71 let device = info.open()?;
72
73 let interface = device
74 .detach_and_claim_interface(0)
75 .expect("claim interface");
76
77 Ok(HackRf {
78 interface,
79 version: UsbVersion::from_bcd(info.device_version()),
80 mode: AtomicMode::new(Mode::Off),
81 })
82 }
83
84 #[cfg(any(target_os = "android", target_os = "linux"))]
88 pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
89 use std::os::fd::AsRawFd;
90 log::info!("Wrapping hackrf fd={}", fd.as_raw_fd());
91 let device = nusb::Device::from_fd(fd)?;
92
93 let interface = device
94 .detach_and_claim_interface(0)
95 .expect("claim interface");
96
97 Ok(HackRf {
98 interface,
99 version: UsbVersion::from_bcd(0x0102),
101 mode: AtomicMode::new(Mode::Off),
102 })
103 }
104
105 pub fn open_first() -> Result<HackRf> {
107 for device in nusb::list_devices()? {
108 if device.vendor_id() == HACKRF_USB_VID && device.product_id() == HACKRF_ONE_USB_PID {
109 match Self::open(device) {
110 Ok(dev) => return Ok(dev),
111 Err(_) => continue,
112 }
113 }
114 }
115
116 Err(Error::NotFound)
117 }
118
119 #[cfg(any(target_os = "linux", target_os = "android"))]
123 pub fn scan() -> Result<Vec<(u8, u8)>> {
124 let mut res = vec![];
125 for device in nusb::list_devices()? {
126 if device.vendor_id() == HACKRF_USB_VID && device.product_id() == HACKRF_ONE_USB_PID {
127 res.push((device.bus_number(), device.device_address()));
128 }
129 }
130 Ok(res)
131 }
132
133 #[cfg(any(target_os = "linux", target_os = "android"))]
136 pub fn open_bus(bus_number: u8, address: u8) -> Result<HackRf> {
137 for device in nusb::list_devices()? {
138 match device.vendor_id() == HACKRF_USB_VID
139 && device.product_id() == HACKRF_ONE_USB_PID
140 && device.bus_number() == bus_number
141 && device.device_address() == address
142 {
143 true => return Self::open(device),
144 false => (),
145 }
146 }
147
148 Err(Error::NotFound)
149 }
150
151 pub fn reset(self) -> Result<()> {
153 self.check_api_version(UsbVersion::from_bcd(0x0102))?;
154 self.write_control(Request::Reset, 0, 0, &[])?;
155
156 Ok(())
157 }
158
159 pub fn device_version(&self) -> UsbVersion {
161 self.version
162 }
163
164 pub fn board_id(&self) -> Result<u8> {
166 let data: [u8; 1] = self.read_control(Request::BoardIdRead, 0, 0)?;
167 Ok(data[0])
168 }
169
170 pub fn version(&self) -> Result<String> {
172 let buf = block_on(self.interface.control_in(ControlIn {
173 control_type: ControlType::Vendor,
174 recipient: Recipient::Device,
175 request: Request::VersionStringRead as u8,
176 value: 0x0,
177 index: 0x0,
178 length: 64,
179 }))
180 .into_result()?;
181
182 Ok(String::from_utf8_lossy(&buf).into())
183 }
184
185 fn apply_config(&self, config: &Config) -> Result<()> {
186 self.set_lna_gain(config.lna_db)?;
187 self.set_vga_gain(config.vga_db)?;
188 self.set_txvga_gain(config.txvga_db)?;
189 self.set_freq(config.frequency_hz)?;
190 self.set_amp_enable(config.amp_enable)?;
191 self.set_antenna_enable(config.antenna_enable)?;
192 self.set_sample_rate(config.sample_rate_hz, config.sample_rate_div)?;
193
194 Ok(())
195 }
196
197 pub fn start_tx(&self, config: &Config) -> Result<()> {
206 if let Err(actual) = self.mode.compare_exchange(
209 Mode::Off,
210 Mode::Transmit,
211 Ordering::AcqRel,
212 Ordering::Relaxed,
213 ) {
214 return Err(Error::WrongMode {
215 required: Mode::Off,
216 actual,
217 });
218 }
219
220 self.apply_config(config)?;
221
222 self.write_control(Request::SetTransceiverMode, Mode::Transmit as u16, 0, &[])?;
223
224 Ok(())
225 }
226
227 pub fn start_rx(&self, config: &Config) -> Result<()> {
236 if let Err(actual) = self.mode.compare_exchange(
239 Mode::Off,
240 Mode::Receive,
241 Ordering::AcqRel,
242 Ordering::Relaxed,
243 ) {
244 return Err(Error::WrongMode {
245 required: Mode::Off,
246 actual,
247 });
248 }
249
250 self.apply_config(config)?;
251
252 self.write_control(Request::SetTransceiverMode, Mode::Receive as u16, 0, &[])?;
253
254 Ok(())
255 }
256
257 pub fn stop_tx(&self) -> Result<()> {
263 self.write_control(Request::SetTransceiverMode, Mode::Off as u16, 0, &[])?;
278
279 if let Err(actual) = self.mode.compare_exchange(
280 Mode::Transmit,
281 Mode::Off,
282 Ordering::AcqRel,
283 Ordering::Relaxed,
284 ) {
285 return Err(Error::WrongMode {
286 required: Mode::Transmit,
287 actual,
288 });
289 }
290
291 Ok(())
292 }
293
294 pub fn stop_rx(&self) -> Result<()> {
300 self.write_control(Request::SetTransceiverMode, Mode::Off as u16, 0, &[])?;
303
304 if let Err(actual) = self.mode.compare_exchange(
305 Mode::Receive,
306 Mode::Off,
307 Ordering::AcqRel,
308 Ordering::Relaxed,
309 ) {
310 return Err(Error::WrongMode {
311 required: Mode::Receive,
312 actual,
313 });
314 }
315
316 Ok(())
317 }
318
319 pub fn read(&self, samples: &mut [u8]) -> Result<usize> {
324 self.ensure_mode(Mode::Receive)?;
325
326 if samples.len() % 512 != 0 {
327 panic!("samples must be a multiple of 512");
328 }
329
330 const ENDPOINT: u8 = 0x81;
331 let buf = block_on(
332 self.interface
333 .bulk_in(ENDPOINT, RequestBuffer::new(samples.len())),
334 )
335 .into_result()?;
336 samples[..buf.len()].copy_from_slice(&buf);
337
338 Ok(buf.len())
339 }
340
341 pub fn write(&self, samples: &[u8]) -> Result<usize> {
346 self.ensure_mode(Mode::Transmit)?;
347
348 if samples.len() % 512 != 0 {
349 panic!("samples must be a multiple of 512");
350 }
351
352 const ENDPOINT: u8 = 0x02;
353 let buf = Vec::from(samples);
354 let n = block_on(self.interface.bulk_out(ENDPOINT, buf)).into_result()?;
356
357 Ok(n.actual_length())
358 }
359
360 pub fn start_rx_stream(&self, transfer_size: usize) -> Result<RxStream> {
362 if transfer_size % 512 != 0 {
363 panic!("transfer_size must be a multiple of 512");
364 }
365
366 const ENDPOINT: u8 = 0x81;
367 Ok(RxStream {
368 queue: self.interface.bulk_in_queue(ENDPOINT),
369 in_flight_transfers: 3,
370 transfer_size,
371 buf_pos: transfer_size,
372 buf: vec![0u8; transfer_size],
373 })
374 }
375}
376
377pub struct RxStream {
381 queue: Queue<RequestBuffer>,
382 in_flight_transfers: usize,
383 transfer_size: usize,
384 buf_pos: usize,
385 buf: Vec<u8>,
386}
387
388impl RxStream {
389 pub fn read_sync(&mut self, count: usize) -> Result<&[u8]> {
391 let buffered_remaining = self.buf.len() - self.buf_pos;
392 if buffered_remaining > 0 {
393 let to_consume = std::cmp::min(count, buffered_remaining);
394 let ret = &self.buf[self.buf_pos..self.buf_pos + to_consume];
395 self.buf_pos += ret.len();
396 return Ok(ret);
397 }
398
399 while self.queue.pending() < self.in_flight_transfers {
400 self.queue.submit(RequestBuffer::new(self.transfer_size));
401 }
402 let completion = block_on(self.queue.next_complete());
403
404 if let Err(e) = completion.status {
405 return Err(e.into());
406 }
407
408 let reuse = std::mem::replace(&mut self.buf, completion.data);
409 self.buf_pos = 0;
410
411 self.queue
412 .submit(RequestBuffer::reuse(reuse, self.transfer_size));
413
414 self.read_sync(count)
416 }
417}
418
419impl HackRf {
420 fn ensure_mode(&self, expected: Mode) -> Result<()> {
421 let actual = self.mode.load(Ordering::Acquire);
422 if actual != expected {
423 return Err(Error::WrongMode {
424 required: expected,
425 actual,
426 });
427 }
428 Ok(())
429 }
430
431 fn read_control<const N: usize>(
432 &self,
433 request: Request,
434 value: u16,
435 index: u16,
436 ) -> Result<[u8; N]> {
437 let mut res: [u8; N] = [0; N];
438 let buf = block_on(self.interface.control_in(ControlIn {
439 control_type: ControlType::Vendor,
440 recipient: Recipient::Device,
441 request: request as u8,
442 value,
443 index,
444 length: N as u16,
445 }))
446 .into_result()?;
447
448 if buf.len() != N {
449 return Err(Error::TransferTruncated {
450 actual: buf.len(),
451 expected: N,
452 });
453 }
454
455 res.copy_from_slice(&buf);
456 Ok(res)
457 }
458
459 fn write_control(&self, request: Request, value: u16, index: u16, buf: &[u8]) -> Result<()> {
460 let out = block_on(self.interface.control_out(ControlOut {
461 control_type: ControlType::Vendor,
462 recipient: Recipient::Device,
463 request: request as u8,
464 value,
465 index,
466 data: buf,
467 }))
468 .into_result()?;
469
470 if out.actual_length() != buf.len() {
471 Err(Error::TransferTruncated {
472 actual: out.actual_length(),
473 expected: buf.len(),
474 })
475 } else {
476 Ok(())
477 }
478 }
479
480 fn check_api_version(&self, min: UsbVersion) -> Result<()> {
481 fn version_to_u32(v: UsbVersion) -> u32 {
482 ((v.major() as u32) << 16) | ((v.minor() as u32) << 8) | (v.sub_minor() as u32)
483 }
484
485 if version_to_u32(self.version) >= version_to_u32(min) {
486 Ok(())
487 } else {
488 Err(Error::NoApi {
489 device: self.version,
490 min,
491 })
492 }
493 }
494
495 pub fn set_freq(&self, hz: u64) -> Result<()> {
497 let buf: [u8; 8] = freq_params(hz);
498 self.write_control(Request::SetFreq, 0, 0, &buf)
499 }
500
501 pub fn set_amp_enable(&self, enable: bool) -> Result<()> {
506 self.write_control(Request::AmpEnable, enable.into(), 0, &[])
507 }
508
509 pub fn set_baseband_filter_bandwidth(&self, hz: u32) -> Result<()> {
514 self.write_control(
515 Request::BasebandFilterBandwidthSet,
516 (hz & 0xFFFF) as u16,
517 (hz >> 16) as u16,
518 &[],
519 )
520 }
521
522 pub fn set_sample_rate(&self, hz: u32, div: u32) -> Result<()> {
533 let hz: u32 = hz.to_le();
534 let div: u32 = div.to_le();
535 let buf: [u8; 8] = [
536 (hz & 0xFF) as u8,
537 ((hz >> 8) & 0xFF) as u8,
538 ((hz >> 16) & 0xFF) as u8,
539 ((hz >> 24) & 0xFF) as u8,
540 (div & 0xFF) as u8,
541 ((div >> 8) & 0xFF) as u8,
542 ((div >> 16) & 0xFF) as u8,
543 ((div >> 24) & 0xFF) as u8,
544 ];
545 self.write_control(Request::SampleRateSet, 0, 0, &buf)?;
546 self.set_baseband_filter_bandwidth((0.75 * (hz as f32) / (div as f32)) as u32)
547 }
548
549 pub fn set_lna_gain(&self, gain: u16) -> Result<()> {
555 if gain > 40 {
556 Err(Error::Argument("lna gain must be less than 40"))
557 } else {
558 let buf: [u8; 1] = self.read_control(Request::SetLnaGain, 0, gain & !0x07)?;
559 if buf[0] == 0 {
560 panic!("Unexpected return value from read_control(SetLnaGain)");
561 } else {
562 Ok(())
563 }
564 }
565 }
566
567 pub fn set_vga_gain(&self, gain: u16) -> Result<()> {
573 if gain > 62 || gain % 2 == 1 {
574 Err(Error::Argument(
575 "gain parameter out of range. must be even and less than or equal to 62",
576 ))
577 } else {
578 let buf: [u8; 1] = self.read_control(Request::SetVgaGain, 0, gain & !0b1)?;
579 if buf[0] == 0 {
580 panic!("What is this return value?")
581 } else {
582 Ok(())
583 }
584 }
585 }
586
587 pub fn set_txvga_gain(&self, gain: u16) -> Result<()> {
591 if gain > 47 {
592 Err(Error::Argument("gain parameter out of range. max is 47"))
593 } else {
594 let buf: [u8; 1] = self.read_control(Request::SetTxvgaGain, 0, gain)?;
595 if buf[0] == 0 {
596 panic!("What is this return value?")
597 } else {
598 Ok(())
599 }
600 }
601 }
602
603 pub fn set_antenna_enable(&self, value: bool) -> Result<()> {
607 let value = if value { 1 } else { 0 };
608 self.write_control(Request::AntennaEnable, value, 0, &[])
609 }
610}
611
612fn freq_params(hz: u64) -> [u8; 8] {
614 const MHZ: u64 = 1_000_000;
615
616 let l_freq_mhz: u32 = u32::try_from(hz / MHZ).unwrap_or(u32::MAX).to_le();
617 let l_freq_hz: u32 = u32::try_from(hz - u64::from(l_freq_mhz) * MHZ)
618 .unwrap_or(u32::MAX)
619 .to_le();
620
621 [
622 (l_freq_mhz & 0xFF) as u8,
623 ((l_freq_mhz >> 8) & 0xFF) as u8,
624 ((l_freq_mhz >> 16) & 0xFF) as u8,
625 ((l_freq_mhz >> 24) & 0xFF) as u8,
626 (l_freq_hz & 0xFF) as u8,
627 ((l_freq_hz >> 8) & 0xFF) as u8,
628 ((l_freq_hz >> 16) & 0xFF) as u8,
629 ((l_freq_hz >> 24) & 0xFF) as u8,
630 ]
631}
632
633#[cfg(test)]
634mod test {
635 use std::time::Duration;
636
637 use super::*;
638
639 #[test]
640 fn test_freq_params() {
641 assert_eq!(freq_params(915_000_000), [0x93, 0x03, 0, 0, 0, 0, 0, 0]);
642 assert_eq!(freq_params(915_000_001), [0x93, 0x03, 0, 0, 1, 0, 0, 0]);
643 assert_eq!(
644 freq_params(123456789),
645 [0x7B, 0, 0, 0, 0x55, 0xF8, 0x06, 0x00]
646 );
647
648 assert_eq!(freq_params(0), [0; 8]);
649
650 assert_eq!(freq_params(u64::MAX), [0xFF; 8]);
651 }
652
653 #[allow(dead_code)]
657 fn device_states() {
658 let radio = HackRf::open_first().expect("Failed to open hackrf");
659
660 radio
661 .start_tx(&Config {
662 vga_db: 0,
663 txvga_db: 0,
664 lna_db: 0,
665 amp_enable: false,
666 antenna_enable: false,
667 frequency_hz: 915_000_000,
668 sample_rate_hz: 2_000_000,
669 sample_rate_div: 1,
670 })
671 .unwrap();
672 std::thread::sleep(Duration::from_millis(50));
673
674 radio.stop_tx().unwrap();
675 assert!(radio.stop_tx().is_err());
676 assert!(radio.stop_tx().is_err());
677 assert!(radio.stop_rx().is_err());
678 assert!(radio.stop_rx().is_err());
679
680 std::thread::sleep(Duration::from_millis(50));
681
682 radio
683 .start_rx(&Config {
684 vga_db: 0,
685 txvga_db: 0,
686 lna_db: 0,
687 amp_enable: false,
688 antenna_enable: false,
689 frequency_hz: 915_000_000,
690 sample_rate_hz: 2_000_000,
691 sample_rate_div: 1,
692 })
693 .unwrap();
694 std::thread::sleep(Duration::from_millis(50));
695
696 radio.stop_rx().unwrap();
697 assert!(radio.stop_rx().is_err());
698 assert!(radio.stop_rx().is_err());
699 assert!(radio.stop_tx().is_err());
700 assert!(radio.stop_tx().is_err());
701 }
702}