From d4c0cc19a710ac81fabe68d74181a897b943809a Mon Sep 17 00:00:00 2001 From: peacememories Date: Sat, 29 Jan 2022 00:41:38 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 1 + Cargo.lock | 100 ++++++++++++++++++++ Cargo.toml | 11 +++ src/lib.rs | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1271648 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,100 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + +[[package]] +name = "ctor" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "hackrf" +version = "0.1.0" +dependencies = [ + "ctor", + "libhackrf-sys", + "thiserror", +] + +[[package]] +name = "libhackrf-sys" +version = "0.1.0" +source = "git+https://gitea.peacememori.es/peacememories/libhackrf-sys#a4d3e7eac494061ec634069e8d9c25c536696e42" +dependencies = [ + "cmake", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b91ac3e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hackrf" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libhackrf-sys = { git = "https://gitea.peacememori.es/peacememories/libhackrf-sys"} +ctor = "0.1.21" +thiserror = "1.0.30" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0caa277 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,269 @@ +use ctor::{ctor, dtor}; +use libhackrf_sys::*; +use std::any::Any; +use std::mem::MaybeUninit; +use thiserror::Error; + +/** + * Initializes the library + */ +#[ctor] +unsafe fn constructor() { + if hackrf_init() != hackrf_error_HACKRF_SUCCESS { + panic!("Could not initialize Hackrf library"); + } +} + +#[dtor] +unsafe fn destructor() { + if hackrf_exit() != hackrf_error_HACKRF_SUCCESS { + panic!("Could not deinitialize Hackrf library") + } +} + +#[derive(Error, Debug)] +pub enum HackrfError { + #[error("Invalid parameter")] + InvalidParam, + #[error("Not found")] + NotFound, + #[error("Busy")] + Busy, + #[error("No memory")] + NoMemory, + #[error("Libusb error")] + LibUsb, + #[error("Thread error")] + Thread, + #[error("Streaming thread error")] + StreamingThread, + #[error("Streaming stopped")] + StreamingStopped, + #[error("Streaming exit called")] + StreamingExitCalled, + #[error("Bad USB API version")] + UsbApiVersion, + #[error("Not last device")] + NotLastDevice, + #[error("Unknown error")] + Other, +} + +impl HackrfError { + #[allow(non_upper_case_globals)] + fn from_ctype(errno: std::os::raw::c_int) -> Result<(), HackrfError> { + match errno { + hackrf_error_HACKRF_SUCCESS => Ok(()), + hackrf_error_HACKRF_TRUE => Ok(()), + hackrf_error_HACKRF_ERROR_INVALID_PARAM => Err(Self::InvalidParam), + hackrf_error_HACKRF_ERROR_NOT_FOUND => Err(Self::NotFound), + hackrf_error_HACKRF_ERROR_BUSY => Err(Self::Busy), + hackrf_error_HACKRF_ERROR_NO_MEM => Err(Self::NoMemory), + hackrf_error_HACKRF_ERROR_LIBUSB => Err(Self::LibUsb), + hackrf_error_HACKRF_ERROR_THREAD => Err(Self::Thread), + hackrf_error_HACKRF_ERROR_STREAMING_THREAD_ERR => Err(Self::StreamingThread), + hackrf_error_HACKRF_ERROR_STREAMING_STOPPED => Err(Self::StreamingStopped), + hackrf_error_HACKRF_ERROR_STREAMING_EXIT_CALLED => Err(Self::StreamingExitCalled), + hackrf_error_HACKRF_ERROR_USB_API_VERSION => Err(Self::UsbApiVersion), + hackrf_error_HACKRF_ERROR_NOT_LAST_DEVICE => Err(Self::NotLastDevice), + hackrf_error_HACKRF_ERROR_OTHER => Err(Self::Other), + _ => unreachable!(), + } + } + + fn into_ctype(result: Result) -> std::os::raw::c_int { + match result { + Ok(_) => hackrf_error_HACKRF_SUCCESS, + Err(Self::InvalidParam) => hackrf_error_HACKRF_ERROR_INVALID_PARAM, + Err(Self::NotFound) => hackrf_error_HACKRF_ERROR_NOT_FOUND, + Err(Self::Busy) => hackrf_error_HACKRF_ERROR_BUSY, + Err(Self::NoMemory) => hackrf_error_HACKRF_ERROR_NO_MEM, + Err(Self::LibUsb) => hackrf_error_HACKRF_ERROR_LIBUSB, + Err(Self::Thread) => hackrf_error_HACKRF_ERROR_THREAD, + Err(Self::StreamingThread) => hackrf_error_HACKRF_ERROR_STREAMING_THREAD_ERR, + Err(Self::StreamingStopped) => hackrf_error_HACKRF_ERROR_STREAMING_STOPPED, + Err(Self::StreamingExitCalled) => hackrf_error_HACKRF_ERROR_STREAMING_EXIT_CALLED, + Err(Self::UsbApiVersion) => hackrf_error_HACKRF_ERROR_USB_API_VERSION, + Err(Self::NotLastDevice) => hackrf_error_HACKRF_ERROR_NOT_LAST_DEVICE, + Err(Self::Other) => hackrf_error_HACKRF_ERROR_OTHER, + } + } +} + +pub enum BoardId { + Jellybean, + Jawbreaker, + HackrfOne, + Rad10, +} + +pub enum UsbBoardId { + Jawbreaker, + HackrfOne, + Rad10, +} + +pub struct DeviceList { + device_list: *mut hackrf_device_list_t, + devices: Vec, +} + +impl std::ops::Deref for DeviceList { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.devices + } +} + +impl Drop for DeviceList { + fn drop(&mut self) { + unsafe { hackrf_device_list_free(self.device_list) } + } +} + +pub struct DeviceListEntry { + pub serial_number: String, + pub board_id: BoardId, + pub usb_board_id: UsbBoardId, + pub index: i32, + list: *mut hackrf_device_list_t, +} + +impl DeviceListEntry { + pub fn open(&self) -> Result { + let mut device = MaybeUninit::uninit(); + unsafe { + HackrfError::from_ctype(hackrf_device_list_open( + self.list, + self.index, + device.as_mut_ptr(), + ))?; + + Ok(Device::from_ctype(device.assume_init())) + } + } +} + +pub struct Device { + device: *mut hackrf_device, + rx_cb: Option>, + tx_cb: Option>, +} + +impl Device { + fn from_ctype(device: *mut hackrf_device) -> Device { + Device { + device: device, + rx_cb: None, + tx_cb: None, + } + } + + pub fn open() -> Result { + let mut device = MaybeUninit::uninit(); + unsafe { + HackrfError::from_ctype(hackrf_open(device.as_mut_ptr()))?; + + Ok(Device::from_ctype(device.assume_init())) + } + } + + pub fn open_by_serial(serial_number: &str) -> Result { + let mut device = MaybeUninit::uninit(); + let serial_number = std::ffi::CString::new(serial_number).unwrap(); + unsafe { + HackrfError::from_ctype(hackrf_open_by_serial( + serial_number.as_ptr(), + device.as_mut_ptr(), + ))?; + Ok(Device::from_ctype(device.assume_init())) + } + } + + pub fn start_rx Result<(), HackrfError> + 'static>( + &mut self, + callback: F, + ) -> Result<(), HackrfError> { + let mut guard = RxCallbackGuard { callback: callback }; + unsafe { + HackrfError::from_ctype(hackrf_start_rx( + self.device, + Some(RxCallbackGuard::::trampoline), + &mut guard.callback as *mut _ as *mut std::os::raw::c_void, + ))?; + self.rx_cb = Some(Box::new(guard)); + Ok(()) + } + } + + pub fn stop_rx(&mut self) -> Result<(), HackrfError> { + unsafe { + HackrfError::from_ctype(hackrf_stop_rx(self.device))?; + self.rx_cb = None; + Ok(()) + } + } + + pub fn start_tx Result<(), HackrfError> + 'static>( + &mut self, + callback: F, + ) -> Result<(), HackrfError> { + let mut guard = TxCallbackGuard { callback: callback }; + unsafe { + HackrfError::from_ctype(hackrf_start_tx( + self.device, + Some(TxCallbackGuard::::trampoline), + &mut guard.callback as *mut _ as *mut std::os::raw::c_void, + ))?; + self.tx_cb = Some(Box::new(guard)); + Ok(()) + } + } + + pub fn stop_tx(&mut self) -> Result<(), HackrfError> { + unsafe { + HackrfError::from_ctype(hackrf_stop_tx(self.device))?; + self.tx_cb = None; + Ok(()) + } + } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { hackrf_close(self.device) }; + } +} + +struct RxCallbackGuard { + callback: F, +} + +impl Result<(), HackrfError>> RxCallbackGuard { + extern "C" fn trampoline(transfer: *mut hackrf_transfer) -> std::os::raw::c_int { + unsafe { + let transfer = transfer.as_ref().unwrap(); + let callback: &mut &mut F = std::mem::transmute(transfer.rx_ctx); + let mut device = Device::from_ctype(transfer.device); + let buffer = + std::slice::from_raw_parts(transfer.buffer, transfer.valid_length as usize); + HackrfError::into_ctype(callback(&mut device, buffer)) + } + } +} + +struct TxCallbackGuard { + callback: F, +} + +impl Result<(), HackrfError>> TxCallbackGuard { + extern "C" fn trampoline(transfer: *mut hackrf_transfer) -> std::os::raw::c_int { + unsafe { + let transfer = transfer.as_ref().unwrap(); + let callback: &mut &mut F = std::mem::transmute(transfer.tx_ctx); + let mut device = Device::from_ctype(transfer.device); + HackrfError::into_ctype(callback(&mut device)) + } + } +}