mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
* Fix error code extraction from Python to Rust * Add documentation for dealing with HCI packets
137 lines
4.4 KiB
Markdown
137 lines
4.4 KiB
Markdown
# What is this?
|
|
|
|
Rust wrappers around the [Bumble](https://github.com/google/bumble) Python API.
|
|
|
|
Method calls are mapped to the equivalent Python, and return types adapted where
|
|
relevant.
|
|
|
|
See the CLI in `src/main.rs` or the `examples` directory for how to use the
|
|
Bumble API.
|
|
|
|
# Usage
|
|
|
|
Set up a virtualenv for Bumble, or otherwise have an isolated Python environment
|
|
for Bumble and its dependencies.
|
|
|
|
Due to Python being
|
|
[picky about how its sys path is set up](https://github.com/PyO3/pyo3/issues/1741,
|
|
it's necessary to explicitly point to the virtualenv's `site-packages`. Use
|
|
suitable virtualenv paths as appropriate for your OS, as seen here running
|
|
the `battery_client` example:
|
|
|
|
```
|
|
PYTHONPATH=..:~/.virtualenvs/bumble/lib/python3.10/site-packages/ \
|
|
cargo run --example battery_client -- \
|
|
--transport android-netsim --target-addr F0:F1:F2:F3:F4:F5
|
|
```
|
|
|
|
Run the corresponding `battery_server` Python example, and launch an emulator in
|
|
Android Studio (currently, Canary is required) to run netsim.
|
|
|
|
# CLI
|
|
|
|
Explore the available subcommands:
|
|
|
|
```
|
|
PYTHONPATH=..:[virtualenv site-packages] \
|
|
cargo run --features bumble-tools --bin bumble -- --help
|
|
```
|
|
|
|
Notable subcommands:
|
|
|
|
- `firmware realtek download`: download Realtek firmware for various chipsets so that it can be automatically loaded when needed
|
|
- `usb probe`: show USB devices, highlighting the ones usable for Bluetooth
|
|
|
|
# Development
|
|
|
|
Run the tests:
|
|
|
|
```
|
|
PYTHONPATH=.. cargo test
|
|
```
|
|
|
|
Check lints:
|
|
|
|
```
|
|
cargo clippy --all-targets
|
|
```
|
|
|
|
## Code gen
|
|
|
|
To have the fastest startup while keeping the build simple, code gen for
|
|
assigned numbers is done with the `gen_assigned_numbers` tool. It should
|
|
be re-run whenever the Python assigned numbers are changed. To ensure that the
|
|
generated code is kept up to date, the Rust data is compared to the Python
|
|
in tests at `pytests/assigned_numbers.rs`.
|
|
|
|
To regenerate the assigned number tables based on the Python codebase:
|
|
|
|
```
|
|
PYTHONPATH=.. cargo run --bin gen-assigned-numbers --features dev-tools
|
|
```
|
|
|
|
## HCI packets
|
|
|
|
Sending a command packet from a device is composed to of two major steps.
|
|
There are more generalized ways of dealing with packets in other scenarios.
|
|
|
|
### Construct the command
|
|
Pick a command from `src/internal/hci/packets.pdl` and construct its associated "builder" struct.
|
|
|
|
```rust
|
|
// The "LE Set Scan Enable" command can be found in the Core Bluetooth Spec.
|
|
// It can also be found in `packets.pdl` as `packet LeSetScanEnable : Command`
|
|
fn main() {
|
|
let device = init_device_as_desired();
|
|
|
|
let le_set_scan_enable_command_builder = LeSetScanEnableBuilder {
|
|
filter_duplicates: Enable::Disabled,
|
|
le_scan_enable: Enable::Enabled,
|
|
};
|
|
}
|
|
```
|
|
|
|
### Send the command and interpret the event response
|
|
Send the command from an initialized device, and then receive the response.
|
|
|
|
```rust
|
|
fn main() {
|
|
// ...
|
|
|
|
// `check_result` to false to receive the event response even if the controller returns a failure code
|
|
let event = device.send_command(le_set_scan_enable_command_builder.into(), /*check_result*/ false);
|
|
// Coerce the event into the expected format. A `Command` should have an associated event response
|
|
// "<command name>Complete".
|
|
let le_set_scan_enable_complete_event: LeSetScanEnableComplete = event.try_into().unwrap();
|
|
}
|
|
```
|
|
|
|
### Generic packet handling
|
|
At the very least, you should expect to at least know _which_ kind of base packet you are dealing with. Base packets in
|
|
`packets.pdl` can be identified because they do not extend any other packet. They are easily found with the regex:
|
|
`^packet [^:]* \{`. For Bluetooth LE (BLE) HCI, one should find some kind of header preceding the packet with the purpose of
|
|
packet disambiguation. We do some of that disambiguation for H4 BLE packets using the `WithPacketHeader` trait at `internal/hci/mod.rs`.
|
|
|
|
Say you've identified a series of bytes that are certainly an `Acl` packet. They can be parsed using the `Acl` struct.
|
|
```rust
|
|
fn main() {
|
|
let bytes = bytes_that_are_certainly_acl();
|
|
let acl_packet = Acl::parse(bytes).unwrap();
|
|
}
|
|
```
|
|
|
|
Since you don't yet know what kind of `Acl` packet it is, you need to specialize it and then handle the various
|
|
potential cases.
|
|
```rust
|
|
fn main() {
|
|
// ...
|
|
match acl_packet.specialize() {
|
|
Payload(bytes) => do_something(bytes),
|
|
None => do_something_else(),
|
|
}
|
|
}
|
|
```
|
|
|
|
Some packets may yet further embed other packets, in which case you may need to further specialize until no more
|
|
specialization is needed.
|