FutureSDR / seify

Seify: A Rusty SDR Hardware Abstraction Library
https://www.futuresdr.org
Apache License 2.0
23 stars 7 forks source link

Rust-native HackRf Driver #9

Open TroyNeubauer opened 2 weeks ago

TroyNeubauer commented 2 weeks ago

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
bastibl commented 2 weeks ago

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.

TroyNeubauer commented 1 week ago

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.

bastibl commented 6 days ago

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):

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

TroyNeubauer commented 6 days ago

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:

Hope to have these done in a few days