seify icon indicating copy to clipboard operation
seify copied to clipboard

Rust-native HackRf Driver

Open TroyNeubauer opened this issue 1 year ago • 4 comments

Hi, Thanks for your work on FutureSDR and seify!

Looking to contribute a rust based driver for the HackRF One, and wanted to make double check the approach. I'm assuming following what the rtl sdr does would be reasonable?

So:

  1. Create new repo for the driver impl (which doesn't depend on seify)
  2. Add feature flag for hackrf one, pull in dependency if set
  3. Add relevant variants to seify::Error and seify::Driver (also feature gated)
  4. Implement DeviceTrait on a new struct which wraps the dependency's hackrf type
  5. Test

TroyNeubauer avatar Sep 02 '24 15:09 TroyNeubauer

Yes, that sounds like the way to go.

I'm not sure, if you already implemented the driver. Just recently, I also thought about implementing more Rust drivers and found nusb, which does not depend on libusb and is, therefore, easier to cross-compile (and maybe more efficient). But the main advantage is that there is now also cross_usb, which is a small abstraction layer that compiles to native code with nusb and WebUSB. I did not try this yet, but with cross_usb a single driver would work for all native targets and the web. I think this would be absolutely awesome.

bastibl avatar Sep 02 '24 21:09 bastibl

Yes I have the driver implemented using rusb. I took a look at those crates and they look great! The trouble is that my usecase runs entierly on android, and it looks like nusb lacks android support.

I dug in to see how difficult it would be to add, and its basically the same as libusb_wrap_sys_device, but in Rust (i.e: obtain busnum and devnum from the file descriptor provided by the UsbManager API, then open the corresponding usbfs file, then the rest should work).

I have ported over wrap_sys_device's approach into rust, but currently I am stuck on an access denied error when reading the rust equivalent of this simlink: https://github.com/libusb/libusb/blob/467b6a8896daea3d104958bf0887312c5d14d150/libusb/os/linux_usbfs.c#L604-L605. Its unclear why this fails in Rust, maybe I missed some setup that libusb does?

It looks like theres some prior art in FutureSDR for android. Have you seen anything like this before?

In any case, I am very interested in solving this and using nusb to take advantage of their easy async API to maximize throughput without having to muck around with libusb's async transfer API. It looks like cross_usb doesn't support an async / Queue based API. I dont know the specifics of how usb stuff works on the browser, but I dont see a reason why they couldnt essentially wrap nusb's Queue, and provide a serial implementation for wasm, and the full async one on desktop. Assuming we could get this upstreamed, switching to cross_usb to add wasm support for hackrf would be trivial since the API's are very similar.

Anyway I'll keep cracking away at this and let you know what I can find.

TroyNeubauer avatar Sep 06 '24 05:09 TroyNeubauer

Cool, great that you're looking into this topic!

I was experimenting with Android before libusb added Android support. So this is not the nice, recommended way to do it: I opened the USB device, using the Android UsbManager in Java/Kotlin domain and forwarded the path and file descriptor through environment variables to the Rust flowgraph:

https://github.com/FutureSDR/FutureSDR/blob/main/examples/android-hw/FutureSDR/app/src/main/java/net/bastibl/futuresdrhw/MainActivity.kt#L97-L115

https://github.com/FutureSDR/FutureSDR/blob/main/examples/android-hw/src/lib.rs#L73-L93

Some ideas about things that maybe required (it's some time that I looked into Android):

  • Maybe you have to set something in the manifest, like: https://github.com/FutureSDR/FutureSDR/blob/main/examples/android-hw/FutureSDR/app/src/main/AndroidManifest.xml#L9
  • the app somehow has to ask for user permission for the USB device: https://github.com/FutureSDR/FutureSDR/blob/main/examples/android-hw/FutureSDR/app/src/main/java/net/bastibl/futuresdrhw/MainActivity.kt#L52-L94
  • I think the correct way is to somehow interact with JNIEnv in C++ domain to get permission to access usbfs, but I didn't try this approach yet.

Regarding the APIs of nusb and cross_usb: with WebUSB everything is async, since there are no blocking calls in the browser. Usually, libusb-based drivers only provide a blocking interface. I don't think that any SDR driver uses the async API of libusb. So until now, there was no easy way to unify these domains, since sync and async APIs typically require different Rust traits. But since nusb provides an async API, this should be easier, and I think this is what cross_usb does. My understanding is that there are no queues but it's just the call to the bulk or control transfer that is async (and not a blocking function call):

https://docs.rs/cross_usb/latest/cross_usb/struct.Interface.html#impl-UsbInterface%3C'a%3E-for-Interface

bastibl avatar Sep 11 '24 11:09 bastibl

Good to know, thanks!

I have this working on android with my some changes to nusb. In the fm-receiver example, the AudioSink just works out of the box an android!

Just opened a PR in nusb for the changes I needed to get this working: https://github.com/kevinmehall/nusb/pull/80

Whats remaining on my plate before opening a PR to seify:

  • Break out the hackrf impl crate into its own repo: https://github.com/MerchGuardian/seify/tree/hackrf/crates/hackrf-one-rs
  • Get the nusb changes upstreamed

Hope to have these done in a few days

TroyNeubauer avatar Sep 11 '24 15:09 TroyNeubauer