Initial commit.

This commit is contained in:
peacememories 2022-01-29 00:41:38 +01:00
commit d4c0cc19a7
4 changed files with 381 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

100
Cargo.lock generated Normal file
View 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
View 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
View 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))
}
}
}