Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlueZ: Replace raw HCI sockets with BlueZ over DBus #105

Merged
merged 40 commits into from
Feb 4, 2021

Conversation

NZSmartie
Copy link
Collaborator

@NZSmartie NZSmartie commented Jan 10, 2021

This is a complete rewrite of the "bluez" platform specific code.

A lot has happened and I did my best to commend code to explain how/why a well as keep commits subjective so that changes can be understood in context.

Things I want to do before merging:

  • Verify I didn't break the existing API (Maintaining a backwards compatible API is a lot harder than I thought and ends up with a lot of messy dead code)
  • Ensure both passive and active discovery work as intended
  • Ensure the code isn't too terrible looking
  • Test this library against multiple versions of BlueZ to see what fails where

Closes #60, #69

@NZSmartie
Copy link
Collaborator Author

I had a look through other open issues and found two related issues that will be resolved with this PR

@NZSmartie
Copy link
Collaborator Author

@qdot, can you add libdbus-1-dev to Linux's installed packages on the CI pipeline?

@NZSmartie NZSmartie force-pushed the feature/bluez-dbus branch 4 times, most recently from dfef5db to e3d2fb5 Compare January 17, 2021 06:45
@NZSmartie NZSmartie changed the title [WIP] BlueZ: Replace raw HCI sockets with BlueZ over DBus BlueZ: Replace raw HCI sockets with BlueZ over DBus Jan 17, 2021
@NZSmartie NZSmartie marked this pull request as ready for review January 17, 2021 23:03
@NZSmartie
Copy link
Collaborator Author

I've done what I can to test this PR, and I'm now relying on others to help test it. Since it works for me™, I'm happy to submit future PRs to fix bugs or other issues that I've yet to face

@NZSmartie
Copy link
Collaborator Author

just rebased on to master

@lnicola
Copy link

lnicola commented Jan 21, 2021

Also closes #52.

@qdot
Copy link
Contributor

qdot commented Jan 21, 2021

Awesome, thanks for the updates. I am definitely trying to get to reviewing this, hopefully in the next day or two.

@qdot
Copy link
Contributor

qdot commented Jan 21, 2021

(Note: This is permission to yell at me in these here comments if I don't get to it soon :3 )

Copy link
Contributor

@qdot qdot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all looking really good! I've just got a few comments I'd like addressed, then it can land.

let lock = listener.lock();
while lock.process(Duration::from_secs(0)).unwrap() {}
drop(lock);
thread::sleep(Duration::from_millis(100));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where'd the 100ms come from here? BLE packet timings can be less than that, so this may lag the system some. I think it could be reduced, would just like to know if there was a source for this.

Also, as the Drop trait needs to kill this, this should be a cancellable sleep. Maybe change it to a condition with a timed wait? That way you aren't blocking the join in the Drop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no source for this. I was going for a way to quickly process any messages there may be, and then sleeping on the thread for 100ms, so that other threads have a chance to use the listener to send/receive

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at my code and later learning about parking_lot I was dealing with mutex fairness, where, the while loop was constantly locking the mutex, not giving any other threads a chance to get a lock, and thus, anything that needed listener would block.

I'll see if parking_lot::ReentrantMutex promotes fairness


if args.interfaces.iter().any(|s| s == BLUEZ_INTERFACE_DEVICE) {
adapter.remove_device(&path).unwrap();
} else if args.interfaces.iter().any(|s| s == BLUEZ_INTERFACE_SERVICE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen here? Can you at least comment the null blocks here and below to let us know whether these are null for a reason or just still need to be filled in?

handle_map: Arc::new(DashMap::new()),
manager: AdapterManager::new(),
};
pub fn is_up(&self) -> Result<bool> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a situation where the adapter would be powered, but not up? Or would this throw if the adapter instance was for a device that has been disconnected?

Cargo.toml Outdated
@@ -36,6 +37,7 @@ uuid = "0.8.2"
serde = { version = "1.0.119", features = ["derive"], default-features = false, optional = true }
dashmap = "4.0.2"
futures = "0.3.12"
parking_lot = "0.11.1"

[dependencies.nom]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we kill the nom dep here now?

.map(|(path, attribute)| Ok(self.add_attribute(path.as_str().unwrap(), attribute)?))
.collect::<Result<()>>()?;

// TODO: Descriptors are nested behind characteristics, and their UUID may be used more than once.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's an issue about descriptor requests, we should probably call this out there. I haven't even looked at other platforms yet.

}

pub fn connect(&self) -> Result<ConnectedAdapter> {
ConnectedAdapter::new(self)
fn active(&self, _enabled: bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere, can we get some comments on the methods that need to be filled in? Makes it easier to file help wanted bugs or at least say issues are claimed later, if we know what needs to be filled in.

connection_tx: Arc<Mutex<Sender<u16>>>,
connection_rx: Arc<Mutex<Receiver<u16>>>,
message_queue: Arc<Mutex<VecDeque<ACLData>>>,
attributes_map: Arc<Mutex<HashMap<u16, (String, Handle, Characteristic)>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you wanted to lose the Mutex here, you could switch to DashMap. I use it heavily up in Buttplug, works great. That said, with everything else so lock heavy here, that's really just the lightest of nits and could be a followup.

if let (Some(id), Some(data)) = (iter.next(), iter.next()) {
// This API is terrible.. why can't I just get an array out, why is it all wrapped in a Variant?
let data: Vec<u8> = data
.as_iter() // 🎶 The Variant is connected to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, please go through the rest of your patch and make your comments rhyme.

.characteristics
.lock()
.unwrap()
.clone()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nit: You might be able to use cloned() after your map (and use refs up to that point), save the copy then discard? But it honestly doesn't matter much in this context, and I can't remember off the top of my head where cloned() comes in.

.as_iter() // Lets convert the
.unwrap() // integers to a
.map(|b| b.as_u64().unwrap() as u8) // array of bytes...
.collect(); // I got too lazy to make it rhyme... 🎶
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do this more nicely with dbus::arg::prop_cast, or if you pass --prop-newtype to dbus-codegen-rust it will give you a type-safe wrapper for the HashMap, and you can do OrgBluezDevice1Properties::manufacturer_data to get a HashMap<u16, Variant<Box<dyn RefArg>>>, and then you can just map the values with dbus::arg::cast::<Vec<u8>>(&v.0).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From memory, I gave this a try from the very start. but I think because i'm passing in a subset of the type that dbus::arg::cast/prop_cast are expecting, i get compile errors. I'll have another go and see if I've missed something.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you may also need to make sure you have at least version 0.9.1 of the dbus crate, to include the fix from diwic/dbus-rs#306.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've sent you NZSmartie#1 to fix this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @qwandor! Though i'm a bit sad to see my silly rhyming comment go away 😢

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to keep some standards in this code, so I'm at least going to need some methods in iambic pentameter going forward.

@qdot
Copy link
Contributor

qdot commented Jan 23, 2021

@qwandor Thanks for the comment!

If you or anyone else has additional guidance on the Bluez side, please feel free to add it here. I was mostly reviewing for structure, I'm far more familiar with the CoreBluetooth and UWP sides of the library, so I can't really comment much on the Bluez.

@NZSmartie NZSmartie requested a review from qdot February 2, 2021 09:02
@qdot qdot changed the base branch from master to dev February 3, 2021 19:58
@qdot
Copy link
Contributor

qdot commented Feb 3, 2021

Ok, well, I'm cool with the changes and will land this, just gotta figure out why changing the base branch to dev borkt things because master and dev are on the same HEAD. I'd also like to keep my fast-forward only rule so I may rebase out the merge that comes in here. So, in short: Your code will be into the dev branch probably this evening, but may look slightly different than your commit lineup here, so you may need to reset if you had changes on your dev.

@qdot qdot merged commit 838d220 into deviceplug:dev Feb 4, 2021
@qdot
Copy link
Contributor

qdot commented Feb 4, 2021

Ok, I just brought this in with the merges for now, turns out the prehistory of rumble is just lousy with them anyways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DeviceLost event is never fired.
5 participants