Initial commit.
This commit is contained in:
commit
d4c0cc19a7
4 changed files with 381 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
100
Cargo.lock
generated
Normal file
100
Cargo.lock
generated
Normal file
|
@ -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"
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -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"
|
269
src/lib.rs
Normal file
269
src/lib.rs
Normal file
|
@ -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<T>(result: Result<T, HackrfError>) -> 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<DeviceListEntry>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DeviceList {
|
||||
type Target = Vec<DeviceListEntry>;
|
||||
|
||||
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<Device, HackrfError> {
|
||||
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<Box<dyn Any>>,
|
||||
tx_cb: Option<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
fn from_ctype(device: *mut hackrf_device) -> Device {
|
||||
Device {
|
||||
device: device,
|
||||
rx_cb: None,
|
||||
tx_cb: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open() -> Result<Device, HackrfError> {
|
||||
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<Device, HackrfError> {
|
||||
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<F: FnMut(&mut Device, &[u8]) -> 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::<F>::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<F: FnMut(&mut Device) -> 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::<F>::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<F> {
|
||||
callback: F,
|
||||
}
|
||||
|
||||
impl<F: FnMut(&mut Device, &[u8]) -> Result<(), HackrfError>> RxCallbackGuard<F> {
|
||||
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<F> {
|
||||
callback: F,
|
||||
}
|
||||
|
||||
impl<F: FnMut(&mut Device) -> Result<(), HackrfError>> TxCallbackGuard<F> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue