[rust] Add bt-common crate Common datatypes for Core Specification defined tupes used throughout Specifications, as well as assigned numbers and common concepts such as Peer IDs. Test: cargo build && cargo test Change-Id: Ief099564ae5238e741e95cf9375702a5ae864e52 Reviewed-on: https://bluetooth-review.git.corp.google.com/c/bluetooth/+/1240 Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/bt-common/.gitignore b/rust/bt-common/.gitignore new file mode 100644 index 0000000..438e2b1 --- /dev/null +++ b/rust/bt-common/.gitignore
@@ -0,0 +1,17 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Vim swap files. +*.swp
diff --git a/rust/bt-common/Cargo.toml b/rust/bt-common/Cargo.toml new file mode 100644 index 0000000..0e131a5 --- /dev/null +++ b/rust/bt-common/Cargo.toml
@@ -0,0 +1,9 @@ +[package] +name = "bt-common" +version = "0.0.1" +edition = "2021" +license = "BSD-2-Clause" + +[dependencies.uuid] +version = "1.4.1" +features = ["macro-diagnostics"]
diff --git a/rust/bt-common/LICENSE b/rust/bt-common/LICENSE new file mode 100644 index 0000000..e5de3c8 --- /dev/null +++ b/rust/bt-common/LICENSE
@@ -0,0 +1,24 @@ +Copyright 2023 Google LLC + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/rust/bt-common/PATENTS b/rust/bt-common/PATENTS new file mode 100644 index 0000000..3a21f11 --- /dev/null +++ b/rust/bt-common/PATENTS
@@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of this crate + +Google hereby grants to you a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this +section) patent license to make, have made, use, offer to sell, sell, +import, transfer, and otherwise run, modify and propagate the contents +of this implementation, where such license applies only to +those patent claims, both currently owned by Google and acquired in +the future, licensable by Google that are necessarily infringed by +this implementation. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute +or order or agree to the institution of patent litigation or any other +patent enforcement activity against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that this +implementation constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation +shall terminate as of the date such litigation is filed.
diff --git a/rust/bt-common/src/lib.rs b/rust/bt-common/src/lib.rs new file mode 100644 index 0000000..58828f4 --- /dev/null +++ b/rust/bt-common/src/lib.rs
@@ -0,0 +1,20 @@ +// Copyright 2023 Google LLC +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Peers are identified by ids, which should be treated as opaque by service libraries. +/// Stack implementations should ensure that each PeerId identifies a single peer over a single +/// instance of the stack - a [`bt_gatt::Central::connect`] should always attempt to connect to the +/// same peer as long as the PeerId was retrieved after the `Central` was instantiated. +/// PeerIds can be valid longer than that (often if the peer is bonded) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct PeerId(pub u64); + +impl core::fmt::Display for PeerId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!(f, "{:x}", self.0) + } +} + +pub mod uuid; +pub use crate::uuid::Uuid;
diff --git a/rust/bt-common/src/uuid.rs b/rust/bt-common/src/uuid.rs new file mode 100644 index 0000000..0fa24da --- /dev/null +++ b/rust/bt-common/src/uuid.rs
@@ -0,0 +1,82 @@ +// Copyright 2023 Google LLC +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// A Uuid as defined by the Core Specification (v5.4, Vol 3, Part B, Sec 2.5.1) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Uuid(uuid::Uuid); + +impl Uuid { + // Non-changing parts of the Bluetooth Base UUID, for easy comparison and construction. + const BASE_UUID_B_PART: u16 = 0x0000; + const BASE_UUID_C_PART: u16 = 0x1000; + const BASE_UUID_D_PART: [u8; 8] = [0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB]; + + pub const fn from_u16(value: u16) -> Self { + Self::from_u32(value as u32) + } + + pub const fn from_u32(value: u32) -> Self { + Uuid(uuid::Uuid::from_fields( + value, + Self::BASE_UUID_B_PART, + Self::BASE_UUID_C_PART, + &Self::BASE_UUID_D_PART, + )) + } + + pub fn to_u16(&self) -> Option<u16> { + let x: u32 = self.to_u32()?; + x.try_into().ok() + } + + pub fn to_u32(&self) -> Option<u32> { + let (first, second, third, final_bytes) = self.0.as_fields(); + if second != Uuid::BASE_UUID_B_PART + || third != Uuid::BASE_UUID_C_PART + || final_bytes != &Uuid::BASE_UUID_D_PART + { + return None; + } + Some(first) + } +} + +impl From<Uuid> for uuid::Uuid { + fn from(value: Uuid) -> Self { + value.0 + } +} + +impl From<uuid::Uuid> for Uuid { + fn from(value: uuid::Uuid) -> Self { + Uuid(value) + } +} + +impl core::fmt::Display for Uuid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.as_hyphenated()) + } +} + +impl core::str::FromStr for Uuid { + type Err = uuid::Error; + + fn from_str(s: &str) -> core::result::Result<Uuid, Self::Err> { + uuid::Uuid::parse_str(s).map(|uuid| Uuid(uuid)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn uuid16() { + let uuid = Uuid::from_u16(0x180d); + assert_eq!("0000180d-0000-1000-8000-00805f9b34fb", uuid.to_string()); + let as16: u16 = uuid.to_u16().unwrap(); + assert_eq!(0x180d, as16); + } +}