diff --git a/bumble/utils.py b/bumble/utils.py index dc03725..03b201c 100644 --- a/bumble/utils.py +++ b/bumble/utils.py @@ -33,7 +33,7 @@ from typing import ( Union, overload, ) -from functools import wraps +from functools import wraps, partial from pyee import EventEmitter from .colors import color @@ -410,3 +410,20 @@ class FlowControlAsyncPipe: self.resume_source() self.check_pump() + + +async def async_call(function, *args, **kwargs): + """ + Immediately calls the function with provided args and kwargs, wrapping it in an async function. + Rust's `pyo3_asyncio` library needs functions to be marked async to properly inject a running loop. + + result = await async_call(some_function, ...) + """ + return function(*args, **kwargs) + + +def wrap_async(function): + """ + Wraps the provided function in an async function. + """ + return partial(async_call, function) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c2d0cd3..c443561 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -80,6 +80,37 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "argh" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "argh_shared" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] + [[package]] name = "atty" version = "0.2.14" @@ -130,6 +161,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.6.2" @@ -145,6 +185,7 @@ name = "bumble" version = "0.1.0" dependencies = [ "anyhow", + "bytes", "clap 4.4.1", "directories", "env_logger", @@ -158,6 +199,8 @@ dependencies = [ "nix", "nom", "owo-colors", + "pdl-derive", + "pdl-runtime", "pyo3", "pyo3-asyncio", "rand", @@ -178,9 +221,9 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" @@ -262,6 +305,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -284,6 +337,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam" version = "0.8.2" @@ -351,6 +413,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "directories" version = "5.0.1" @@ -559,6 +641,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -1064,12 +1156,100 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pdl-compiler" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee66995739fb9ddd9155767990a54aadd226ee32408a94f99f94883ff445ceba" +dependencies = [ + "argh", + "codespan-reporting", + "heck", + "pest", + "pest_derive", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.29", +] + +[[package]] +name = "pdl-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113e4a1215c407466b36d2c2f6a6318819d6b22ccdd3acb7bb35e27a68806034" +dependencies = [ + "codespan-reporting", + "pdl-compiler", + "proc-macro2", + "quote", + "syn 2.0.29", + "termcolor", +] + +[[package]] +name = "pdl-runtime" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d36c2f9799613babe78eb5cd9a353d527daaba6c3d1f39a1175657a35790732" +dependencies = [ + "bytes", + "thiserror", +] + [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pest" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "pest_meta" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1094,6 +1274,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2", + "syn 2.0.29", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -1465,6 +1655,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1711,6 +1912,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1738,6 +1951,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "unindent" version = "0.1.11" @@ -1767,6 +1986,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.4.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a553afd..35a0f4c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -23,6 +23,9 @@ hex = "0.4.3" itertools = "0.11.0" lazy_static = "1.4.0" thiserror = "1.0.41" +bytes = "1.5.0" +pdl-derive = "0.2.0" +pdl-runtime = "0.2.0" # Dev tools file-header = { version = "0.1.2", optional = true } diff --git a/rust/examples/scanner.rs b/rust/examples/scanner.rs index 1b68ea5..ec931b5 100644 --- a/rust/examples/scanner.rs +++ b/rust/examples/scanner.rs @@ -20,7 +20,8 @@ use bumble::{ adv::CommonDataType, wrapper::{ - core::AdvertisementDataUnit, device::Device, hci::AddressType, transport::Transport, + core::AdvertisementDataUnit, device::Device, hci::packets::AddressType, + transport::Transport, }, }; use clap::Parser as _; @@ -102,7 +103,9 @@ async fn main() -> PyResult<()> { }; let (type_style, qualifier) = match adv.address()?.address_type()? { - AddressType::PublicIdentity | AddressType::PublicDevice => (Style::new().cyan(), ""), + AddressType::PublicIdentityAddress | AddressType::PublicDeviceAddress => { + (Style::new().cyan(), "") + } _ => { if addr.is_static()? { (Style::new().green(), "(static)") diff --git a/rust/pytests/wrapper.rs b/rust/pytests/wrapper.rs index 8f69dd7..9fd65e7 100644 --- a/rust/pytests/wrapper.rs +++ b/rust/pytests/wrapper.rs @@ -12,9 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bumble::wrapper::{drivers::rtk::DriverInfo, transport::Transport}; +use bumble::wrapper::{ + controller::Controller, + device::Device, + drivers::rtk::DriverInfo, + hci::{ + packets::{ + AddressType, ErrorCode, ReadLocalVersionInformationBuilder, + ReadLocalVersionInformationComplete, + }, + Address, Error, + }, + host::Host, + link::Link, + transport::Transport, +}; use nix::sys::stat::Mode; -use pyo3::PyResult; +use pyo3::{ + exceptions::PyException, + {PyErr, PyResult}, +}; #[pyo3_asyncio::tokio::test] async fn fifo_transport_can_open() -> PyResult<()> { @@ -35,3 +52,26 @@ async fn realtek_driver_info_all_drivers() -> PyResult<()> { assert_eq!(12, DriverInfo::all_drivers()?.len()); Ok(()) } + +#[pyo3_asyncio::tokio::test] +async fn hci_command_wrapper_has_correct_methods() -> PyResult<()> { + let address = Address::new("F0:F1:F2:F3:F4:F5", &AddressType::RandomDeviceAddress)?; + let link = Link::new_local_link()?; + let controller = Controller::new("C1", None, None, Some(link), Some(address.clone())).await?; + let host = Host::new(controller.clone().into(), controller.into()).await?; + let device = Device::new(None, Some(address), None, Some(host), None)?; + + device.power_on().await?; + + // Send some simple command. A successful response means [HciCommandWrapper] has the minimum + // required interface for the Python code to think its an [HCI_Command] object. + let command = ReadLocalVersionInformationBuilder {}; + let event: ReadLocalVersionInformationComplete = device + .send_command(&command.into(), true) + .await? + .try_into() + .map_err(|e: Error| PyErr::new::(e.to_string()))?; + + assert_eq!(ErrorCode::Success, event.get_status()); + Ok(()) +} diff --git a/rust/src/cli/firmware/rtk.rs b/rust/src/cli/firmware/rtk.rs index f5524a4..1028564 100644 --- a/rust/src/cli/firmware/rtk.rs +++ b/rust/src/cli/firmware/rtk.rs @@ -179,7 +179,7 @@ pub(crate) fn parse(firmware_path: &path::Path) -> PyResult<()> { pub(crate) async fn info(transport: &str, force: bool) -> PyResult<()> { let transport = Transport::open(transport).await?; - let mut host = Host::new(transport.source()?, transport.sink()?)?; + let mut host = Host::new(transport.source()?, transport.sink()?).await?; host.reset(DriverFactory::None).await?; if !force && !Driver::check(&host).await? { @@ -203,7 +203,7 @@ pub(crate) async fn info(transport: &str, force: bool) -> PyResult<()> { pub(crate) async fn load(transport: &str, force: bool) -> PyResult<()> { let transport = Transport::open(transport).await?; - let mut host = Host::new(transport.source()?, transport.sink()?)?; + let mut host = Host::new(transport.source()?, transport.sink()?).await?; host.reset(DriverFactory::None).await?; match Driver::for_host(&host, force).await? { @@ -219,7 +219,7 @@ pub(crate) async fn load(transport: &str, force: bool) -> PyResult<()> { pub(crate) async fn drop(transport: &str) -> PyResult<()> { let transport = Transport::open(transport).await?; - let mut host = Host::new(transport.source()?, transport.sink()?)?; + let mut host = Host::new(transport.source()?, transport.sink()?).await?; host.reset(DriverFactory::None).await?; Driver::drop_firmware(&mut host).await?; diff --git a/rust/src/internal/hci/mod.rs b/rust/src/internal/hci/mod.rs new file mode 100644 index 0000000..232c49f --- /dev/null +++ b/rust/src/internal/hci/mod.rs @@ -0,0 +1,161 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use pdl_runtime::{Error, Packet}; + +use crate::internal::hci::packets::{Acl, Command, Event, Sco}; +use pdl_derive::pdl; + +#[allow(missing_docs, warnings, clippy::all)] +#[pdl("src/internal/hci/packets.pdl")] +pub mod packets {} +#[cfg(test)] +mod tests; + +/// HCI Packet type, prepended to the packet. +/// Rootcanal's PDL declaration excludes this from ser/deser and instead is implemented in code. +/// To maintain the ability to easily use future versions of their packet PDL, packet type is +/// implemented here. +#[derive(Debug, PartialEq)] +pub(crate) enum PacketType { + Command = 0x01, + Acl = 0x02, + Sco = 0x03, + Event = 0x04, +} + +impl TryFrom for PacketType { + type Error = PacketTypeParseError; + + fn try_from(value: u8) -> Result { + match value { + 0x01 => Ok(PacketType::Command), + 0x02 => Ok(PacketType::Acl), + 0x03 => Ok(PacketType::Sco), + 0x04 => Ok(PacketType::Event), + _ => Err(PacketTypeParseError::InvalidPacketType { value }), + } + } +} + +impl From for u8 { + fn from(packet_type: PacketType) -> Self { + match packet_type { + PacketType::Command => 0x01, + PacketType::Acl => 0x02, + PacketType::Sco => 0x03, + PacketType::Event => 0x04, + } + } +} + +/// Allows for smoother interoperability between a [Packet] and a bytes representation of it that +/// includes its type as a header +pub(crate) trait WithPacketType { + /// Converts the [Packet] into bytes, prefixed with its type + fn to_vec_with_packet_type(self) -> Vec; + + /// Parses a [Packet] out of bytes that are prefixed with the packet's type + fn parse_with_packet_type(bytes: &[u8]) -> Result; +} + +/// Errors that may arise when parsing a packet that is prefixed with its type +#[derive(Debug, PartialEq, thiserror::Error)] +pub(crate) enum PacketTypeParseError { + #[error("The slice being parsed was empty")] + EmptySlice, + #[error("Packet type ({value:#X}) is invalid")] + InvalidPacketType { value: u8 }, + #[error("Expected packet type: {expected:?}, but got: {actual:?}")] + PacketTypeMismatch { + expected: PacketType, + actual: PacketType, + }, + #[error("Failed to parse packet after header: {error}")] + PacketParse { error: Error }, +} + +impl From for PacketTypeParseError { + fn from(error: Error) -> Self { + Self::PacketParse { error } + } +} + +impl WithPacketType for Command { + fn to_vec_with_packet_type(self) -> Vec { + prepend_packet_type(PacketType::Command, self.to_vec()) + } + + fn parse_with_packet_type(bytes: &[u8]) -> Result { + parse_with_expected_packet_type(Command::parse, PacketType::Command, bytes) + } +} + +impl WithPacketType for Acl { + fn to_vec_with_packet_type(self) -> Vec { + prepend_packet_type(PacketType::Acl, self.to_vec()) + } + + fn parse_with_packet_type(bytes: &[u8]) -> Result { + parse_with_expected_packet_type(Acl::parse, PacketType::Acl, bytes) + } +} + +impl WithPacketType for Sco { + fn to_vec_with_packet_type(self) -> Vec { + prepend_packet_type(PacketType::Sco, self.to_vec()) + } + + fn parse_with_packet_type(bytes: &[u8]) -> Result { + parse_with_expected_packet_type(Sco::parse, PacketType::Sco, bytes) + } +} + +impl WithPacketType for Event { + fn to_vec_with_packet_type(self) -> Vec { + prepend_packet_type(PacketType::Event, self.to_vec()) + } + + fn parse_with_packet_type(bytes: &[u8]) -> Result { + parse_with_expected_packet_type(Event::parse, PacketType::Event, bytes) + } +} + +fn prepend_packet_type(packet_type: PacketType, mut packet_bytes: Vec) -> Vec { + packet_bytes.insert(0, packet_type.into()); + packet_bytes +} + +fn parse_with_expected_packet_type( + parser: F, + expected_packet_type: PacketType, + bytes: &[u8], +) -> Result +where + F: Fn(&[u8]) -> Result, + PacketTypeParseError: From, +{ + let (first_byte, packet_bytes) = bytes + .split_first() + .ok_or(PacketTypeParseError::EmptySlice)?; + let actual_packet_type = PacketType::try_from(*first_byte)?; + if actual_packet_type == expected_packet_type { + Ok(parser(packet_bytes)?) + } else { + Err(PacketTypeParseError::PacketTypeMismatch { + expected: expected_packet_type, + actual: actual_packet_type, + }) + } +} diff --git a/rust/src/internal/hci/packets.pdl b/rust/src/internal/hci/packets.pdl new file mode 100644 index 0000000..694d37c --- /dev/null +++ b/rust/src/internal/hci/packets.pdl @@ -0,0 +1,6253 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Copied from: +// https://github.com/google/rootcanal/blob/2b57c6bb3598babd99b07126e093fb6811637a47/packets/hci_packets.pdl +little_endian_packets + +custom_field Address : 48 "hci/" + +enum Enable : 8 { + DISABLED = 0x00, + ENABLED = 0x01, +} + +// HCI ACL Packets + +enum PacketBoundaryFlag : 2 { + FIRST_NON_AUTOMATICALLY_FLUSHABLE = 0, + CONTINUING_FRAGMENT = 1, + FIRST_AUTOMATICALLY_FLUSHABLE = 2, +} + +enum BroadcastFlag : 2 { + POINT_TO_POINT = 0, + ACTIVE_PERIPHERAL_BROADCAST = 1, +} + +packet Acl { + handle : 12, + packet_boundary_flag : PacketBoundaryFlag, + broadcast_flag : BroadcastFlag, + _size_(_payload_) : 16, + _payload_, +} + +// HCI SCO Packets + +enum PacketStatusFlag : 2 { + CORRECTLY_RECEIVED = 0, + POSSIBLY_INCOMPLETE = 1, + NO_DATA_RECEIVED = 2, + PARTIALLY_LOST = 3, +} + +packet Sco { + handle : 12, + packet_status_flag : PacketStatusFlag, + _reserved_ : 2, // BroadcastFlag + _size_(data) : 8, + data : 8[], +} + +// HCI Command Packets + +enum OpCode : 16 { + NONE = 0x0000, + + // LINK_CONTROL + INQUIRY = 0x0401, + INQUIRY_CANCEL = 0x0402, + PERIODIC_INQUIRY_MODE = 0x0403, + EXIT_PERIODIC_INQUIRY_MODE = 0x0404, + CREATE_CONNECTION = 0x0405, + DISCONNECT = 0x0406, + ADD_SCO_CONNECTION = 0x0407, + CREATE_CONNECTION_CANCEL = 0x0408, + ACCEPT_CONNECTION_REQUEST = 0x0409, + REJECT_CONNECTION_REQUEST = 0x040A, + LINK_KEY_REQUEST_REPLY = 0x040B, + LINK_KEY_REQUEST_NEGATIVE_REPLY = 0x040C, + PIN_CODE_REQUEST_REPLY = 0x040D, + PIN_CODE_REQUEST_NEGATIVE_REPLY = 0x040E, + CHANGE_CONNECTION_PACKET_TYPE = 0x040F, + AUTHENTICATION_REQUESTED = 0x0411, + SET_CONNECTION_ENCRYPTION = 0x0413, + CHANGE_CONNECTION_LINK_KEY = 0x0415, + CENTRAL_LINK_KEY = 0x0417, + REMOTE_NAME_REQUEST = 0x0419, + REMOTE_NAME_REQUEST_CANCEL = 0x041A, + READ_REMOTE_SUPPORTED_FEATURES = 0x041B, + READ_REMOTE_EXTENDED_FEATURES = 0x041C, + READ_REMOTE_VERSION_INFORMATION = 0x041D, + READ_CLOCK_OFFSET = 0x041F, + READ_LMP_HANDLE = 0x0420, + SETUP_SYNCHRONOUS_CONNECTION = 0x0428, + ACCEPT_SYNCHRONOUS_CONNECTION = 0x0429, + REJECT_SYNCHRONOUS_CONNECTION = 0x042A, + IO_CAPABILITY_REQUEST_REPLY = 0x042B, + USER_CONFIRMATION_REQUEST_REPLY = 0x042C, + USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 0x042D, + USER_PASSKEY_REQUEST_REPLY = 0x042E, + USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 0x042F, + REMOTE_OOB_DATA_REQUEST_REPLY = 0x0430, + REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 0x0433, + IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 0x0434, + ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 0x043D, + ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 0x043E, + TRUNCATED_PAGE = 0x043F, + TRUNCATED_PAGE_CANCEL = 0x0440, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST = 0x0441, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE = 0x0442, + START_SYNCHRONIZATION_TRAIN = 0x0443, + RECEIVE_SYNCHRONIZATION_TRAIN = 0x0444, + REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY = 0x0445, + + // LINK_POLICY + HOLD_MODE = 0x0801, + SNIFF_MODE = 0x0803, + EXIT_SNIFF_MODE = 0x0804, + QOS_SETUP = 0x0807, + ROLE_DISCOVERY = 0x0809, + SWITCH_ROLE = 0x080B, + READ_LINK_POLICY_SETTINGS = 0x080C, + WRITE_LINK_POLICY_SETTINGS = 0x080D, + READ_DEFAULT_LINK_POLICY_SETTINGS = 0x080E, + WRITE_DEFAULT_LINK_POLICY_SETTINGS = 0x080F, + FLOW_SPECIFICATION = 0x0810, + SNIFF_SUBRATING = 0x0811, + + // CONTROLLER_AND_BASEBAND + SET_EVENT_MASK = 0x0C01, + RESET = 0x0C03, + SET_EVENT_FILTER = 0x0C05, + FLUSH = 0x0C08, + READ_PIN_TYPE = 0x0C09, + WRITE_PIN_TYPE = 0x0C0A, + READ_STORED_LINK_KEY = 0x0C0D, + WRITE_STORED_LINK_KEY = 0x0C11, + DELETE_STORED_LINK_KEY = 0x0C12, + WRITE_LOCAL_NAME = 0x0C13, + READ_LOCAL_NAME = 0x0C14, + READ_CONNECTION_ACCEPT_TIMEOUT = 0x0C15, + WRITE_CONNECTION_ACCEPT_TIMEOUT = 0x0C16, + READ_PAGE_TIMEOUT = 0x0C17, + WRITE_PAGE_TIMEOUT = 0x0C18, + READ_SCAN_ENABLE = 0x0C19, + WRITE_SCAN_ENABLE = 0x0C1A, + READ_PAGE_SCAN_ACTIVITY = 0x0C1B, + WRITE_PAGE_SCAN_ACTIVITY = 0x0C1C, + READ_INQUIRY_SCAN_ACTIVITY = 0x0C1D, + WRITE_INQUIRY_SCAN_ACTIVITY = 0x0C1E, + READ_AUTHENTICATION_ENABLE = 0x0C1F, + WRITE_AUTHENTICATION_ENABLE = 0x0C20, + READ_CLASS_OF_DEVICE = 0x0C23, + WRITE_CLASS_OF_DEVICE = 0x0C24, + READ_VOICE_SETTING = 0x0C25, + WRITE_VOICE_SETTING = 0x0C26, + READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27, + WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28, + READ_NUM_BROADCAST_RETRANSMITS = 0x0C29, + WRITE_NUM_BROADCAST_RETRANSMITS = 0x0C2A, + READ_HOLD_MODE_ACTIVITY = 0x0C2B, + WRITE_HOLD_MODE_ACTIVITY = 0x0C2C, + READ_TRANSMIT_POWER_LEVEL = 0x0C2D, + READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2E, + WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2F, + SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x0C31, + HOST_BUFFER_SIZE = 0x0C33, + HOST_NUMBER_OF_COMPLETED_PACKETS = 0x0C35, + READ_LINK_SUPERVISION_TIMEOUT = 0x0C36, + WRITE_LINK_SUPERVISION_TIMEOUT = 0x0C37, + READ_NUMBER_OF_SUPPORTED_IAC = 0x0C38, + READ_CURRENT_IAC_LAP = 0x0C39, + WRITE_CURRENT_IAC_LAP = 0x0C3A, + SET_AFH_HOST_CHANNEL_CLASSIFICATION = 0x0C3F, + READ_INQUIRY_SCAN_TYPE = 0x0C42, + WRITE_INQUIRY_SCAN_TYPE = 0x0C43, + READ_INQUIRY_MODE = 0x0C44, + WRITE_INQUIRY_MODE = 0x0C45, + READ_PAGE_SCAN_TYPE = 0x0C46, + WRITE_PAGE_SCAN_TYPE = 0x0C47, + READ_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C48, + WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C49, + READ_EXTENDED_INQUIRY_RESPONSE = 0x0C51, + WRITE_EXTENDED_INQUIRY_RESPONSE = 0x0C52, + REFRESH_ENCRYPTION_KEY = 0x0C53, + READ_SIMPLE_PAIRING_MODE = 0x0C55, + WRITE_SIMPLE_PAIRING_MODE = 0x0C56, + READ_LOCAL_OOB_DATA = 0x0C57, + READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 0x0C58, + WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 0x0C59, + READ_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x0C5A, + WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x0C5B, + ENHANCED_FLUSH = 0x0C5F, + SEND_KEYPRESS_NOTIFICATION = 0x0C60, + SET_EVENT_MASK_PAGE_2 = 0x0C63, + READ_FLOW_CONTROL_MODE = 0x0C66, + WRITE_FLOW_CONTROL_MODE = 0x0C67, + READ_ENHANCED_TRANSMIT_POWER_LEVEL = 0x0C68, + READ_LE_HOST_SUPPORT = 0x0C6C, + WRITE_LE_HOST_SUPPORT = 0x0C6D, + SET_MWS_CHANNEL_PARAMETERS = 0x0C6E, + SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F, + SET_MWS_SIGNALING = 0x0C70, + SET_MWS_TRANSPORT_LAYER = 0x0C71, + SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72, + SET_MWS_PATTERN_CONFIGURATION = 0x0C73, + SET_RESERVED_LT_ADDR = 0x0C74, + DELETE_RESERVED_LT_ADDR = 0x0C75, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA = 0x0C76, + READ_SYNCHRONIZATION_TRAIN_PARAMETERS = 0x0C77, + WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS = 0x0C78, + READ_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C79, + WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C7A, + READ_AUTHENTICATED_PAYLOAD_TIMEOUT = 0x0C7B, + WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT = 0x0C7C, + READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D, + READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E, + WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F, + READ_EXTENDED_INQUIRY_LENGTH = 0x0C80, + WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81, + SET_ECOSYSTEM_BASE_INTERVAL = 0x0C82, + CONFIGURE_DATA_PATH = 0x0C83, + SET_MIN_ENCRYPTION_KEY_SIZE = 0x0C84, + + // INFORMATIONAL_PARAMETERS + READ_LOCAL_VERSION_INFORMATION = 0x1001, + READ_LOCAL_SUPPORTED_COMMANDS = 0x1002, + READ_LOCAL_SUPPORTED_FEATURES = 0x1003, + READ_LOCAL_EXTENDED_FEATURES = 0x1004, + READ_BUFFER_SIZE = 0x1005, + READ_BD_ADDR = 0x1009, + READ_DATA_BLOCK_SIZE = 0x100A, + READ_LOCAL_SUPPORTED_CODECS_V1 = 0x100B, + READ_LOCAL_SIMPLE_PAIRING_OPTIONS = 0x100C, + READ_LOCAL_SUPPORTED_CODECS_V2 = 0x100D, + READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 0x100E, + READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 0x100F, + + // STATUS_PARAMETERS + READ_FAILED_CONTACT_COUNTER = 0x1401, + RESET_FAILED_CONTACT_COUNTER = 0x1402, + READ_LINK_QUALITY = 0x1403, + READ_RSSI = 0x1405, + READ_AFH_CHANNEL_MAP = 0x1406, + READ_CLOCK = 0x1407, + READ_ENCRYPTION_KEY_SIZE = 0x1408, + GET_MWS_TRANSPORT_LAYER_CONFIGURATION = 0x140C, + SET_TRIGGERED_CLOCK_CAPTURE = 0x140D, + + // TESTING + READ_LOOPBACK_MODE = 0x1801, + WRITE_LOOPBACK_MODE = 0x1802, + ENABLE_DEVICE_UNDER_TEST_MODE = 0x1803, + WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x1804, + WRITE_SECURE_CONNECTIONS_TEST_MODE = 0x180A, + + // LE_CONTROLLER + LE_SET_EVENT_MASK = 0x2001, + LE_READ_BUFFER_SIZE_V1 = 0x2002, + LE_READ_LOCAL_SUPPORTED_FEATURES = 0x2003, + LE_SET_RANDOM_ADDRESS = 0x2005, + LE_SET_ADVERTISING_PARAMETERS = 0x2006, + LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER = 0x2007, + LE_SET_ADVERTISING_DATA = 0x2008, + LE_SET_SCAN_RESPONSE_DATA = 0x2009, + LE_SET_ADVERTISING_ENABLE = 0x200A, + LE_SET_SCAN_PARAMETERS = 0x200B, + LE_SET_SCAN_ENABLE = 0x200C, + LE_CREATE_CONNECTION = 0x200D, + LE_CREATE_CONNECTION_CANCEL = 0x200E, + LE_READ_FILTER_ACCEPT_LIST_SIZE = 0x200F, + LE_CLEAR_FILTER_ACCEPT_LIST = 0x2010, + LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST = 0x2011, + LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST = 0x2012, + LE_CONNECTION_UPDATE = 0x2013, + LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x2014, + LE_READ_CHANNEL_MAP = 0x2015, + LE_READ_REMOTE_FEATURES = 0x2016, + LE_ENCRYPT = 0x2017, + LE_RAND = 0x2018, + LE_START_ENCRYPTION = 0x2019, + LE_LONG_TERM_KEY_REQUEST_REPLY = 0x201A, + LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 0x201B, + LE_READ_SUPPORTED_STATES = 0x201C, + LE_RECEIVER_TEST_V1 = 0x201D, + LE_TRANSMITTER_TEST_V1 = 0x201E, + LE_TEST_END = 0x201F, + LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 0x2020, + LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 0x2021, + LE_SET_DATA_LENGTH = 0x2022, + LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2023, + LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2024, + LE_READ_LOCAL_P_256_PUBLIC_KEY = 0x2025, + LE_GENERATE_DHKEY_V1 = 0x2026, + LE_ADD_DEVICE_TO_RESOLVING_LIST = 0x2027, + LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 0x2028, + LE_CLEAR_RESOLVING_LIST = 0x2029, + LE_READ_RESOLVING_LIST_SIZE = 0x202A, + LE_READ_PEER_RESOLVABLE_ADDRESS = 0x202B, + LE_READ_LOCAL_RESOLVABLE_ADDRESS = 0x202C, + LE_SET_ADDRESS_RESOLUTION_ENABLE = 0x202D, + LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 0x202E, + LE_READ_MAXIMUM_DATA_LENGTH = 0x202F, + LE_READ_PHY = 0x2030, + LE_SET_DEFAULT_PHY = 0x2031, + LE_SET_PHY = 0x2032, + LE_RECEIVER_TEST_V2 = 0x2033, + LE_TRANSMITTER_TEST_V2 = 0x2034, + LE_SET_ADVERTISING_SET_RANDOM_ADDRESS = 0x2035, + LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 0x2036, + LE_SET_EXTENDED_ADVERTISING_DATA = 0x2037, + LE_SET_EXTENDED_SCAN_RESPONSE_DATA = 0x2038, + LE_SET_EXTENDED_ADVERTISING_ENABLE = 0x2039, + LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A, + LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B, + LE_REMOVE_ADVERTISING_SET = 0x203C, + LE_CLEAR_ADVERTISING_SETS = 0x203D, + LE_SET_PERIODIC_ADVERTISING_PARAMETERS = 0x203E, + LE_SET_PERIODIC_ADVERTISING_DATA = 0x203F, + LE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040, + LE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041, + LE_SET_EXTENDED_SCAN_ENABLE = 0x2042, + LE_EXTENDED_CREATE_CONNECTION = 0x2043, + LE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044, + LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045, + LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046, + LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST = 0x2047, + LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST = 0x2048, + LE_CLEAR_PERIODIC_ADVERTISER_LIST = 0x2049, + LE_READ_PERIODIC_ADVERTISER_LIST_SIZE = 0x204A, + LE_READ_TRANSMIT_POWER = 0x204B, + LE_READ_RF_PATH_COMPENSATION_POWER = 0x204C, + LE_WRITE_RF_PATH_COMPENSATION_POWER = 0x204D, + LE_SET_PRIVACY_MODE = 0x204E, + LE_RECEIVER_TEST_V3 = 0x204F, + LE_TRANSMITTER_TEST_V3 = 0x2050, + LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS = 0x2051, + LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE = 0x2052, + LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE = 0x2053, + LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS = 0x2054, + LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS = 0x2055, + LE_CONNECTION_CTE_REQUEST_ENABLE = 0x2056, + LE_CONNECTION_CTE_RESPONSE_ENABLE = 0x2057, + LE_READ_ANTENNA_INFORMATION = 0x2058, + LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 0x2059, + LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 0x205A, + LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 0x205B, + LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0x205C, + LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0x205D, + LE_GENERATE_DHKEY_V2 = 0x205E, + LE_MODIFY_SLEEP_CLOCK_ACCURACY = 0x205F, + LE_READ_BUFFER_SIZE_V2 = 0x2060, + LE_READ_ISO_TX_SYNC = 0x2061, + LE_SET_CIG_PARAMETERS = 0x2062, + LE_SET_CIG_PARAMETERS_TEST = 0x2063, + LE_CREATE_CIS = 0x2064, + LE_REMOVE_CIG = 0x2065, + LE_ACCEPT_CIS_REQUEST = 0x2066, + LE_REJECT_CIS_REQUEST = 0x2067, + LE_CREATE_BIG = 0x2068, + LE_CREATE_BIG_TEST = 0x2069, + LE_TERMINATE_BIG = 0x206A, + LE_BIG_CREATE_SYNC = 0x206B, + LE_BIG_TERMINATE_SYNC = 0x206C, + LE_REQUEST_PEER_SCA = 0x206D, + LE_SETUP_ISO_DATA_PATH = 0x206E, + LE_REMOVE_ISO_DATA_PATH = 0x206F, + LE_ISO_TRANSMIT_TEST = 0x2070, + LE_ISO_RECEIVE_TEST = 0x2071, + LE_ISO_READ_TEST_COUNTERS = 0x2072, + LE_ISO_TEST_END = 0x2073, + LE_SET_HOST_FEATURE = 0x2074, + LE_READ_ISO_LINK_QUALITY = 0x2075, + LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 0x2076, + LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 0x2077, + LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 0x2078, + LE_SET_PATH_LOSS_REPORTING_ENABLE = 0x2079, + LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 0x207A, + LE_TRANSMITTER_TEST_V4 = 0x207B, + LE_SET_DATA_RELATED_ADDRESS_CHANGES = 0x207C, + LE_SET_DEFAULT_SUBRATE = 0x207D, + LE_SUBRATE_REQUEST = 0x207E, + + // VENDOR_SPECIFIC + // MSFT_OPCODE_xxxx below is needed for the tests. + MSFT_OPCODE_INTEL = 0xFC1E, + LE_GET_VENDOR_CAPABILITIES = 0xFD53, + LE_BATCH_SCAN = 0xFD56, + LE_APCF = 0xFD57, + LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO = 0xFD59, + LE_EX_SET_SCAN_PARAMETERS = 0xFD5A, + GET_CONTROLLER_DEBUG_INFO = 0xFD5B, + // MSFT_OPCODE_xxxx below are needed for the tests. + MSFT_OPCODE_MEDIATEK = 0xFD30, + MSFT_OPCODE_QUALCOMM = 0xFD70, +} + +// For mapping Local Supported Commands command +// Value = Octet * 10 + bit +enum OpCodeIndex : 16 { + INQUIRY = 0, + INQUIRY_CANCEL = 1, + PERIODIC_INQUIRY_MODE = 2, + EXIT_PERIODIC_INQUIRY_MODE = 3, + CREATE_CONNECTION = 4, + DISCONNECT = 5, + ADD_SCO_CONNECTION = 6, + CREATE_CONNECTION_CANCEL = 7, + ACCEPT_CONNECTION_REQUEST = 10, + REJECT_CONNECTION_REQUEST = 11, + LINK_KEY_REQUEST_REPLY = 12, + LINK_KEY_REQUEST_NEGATIVE_REPLY = 13, + PIN_CODE_REQUEST_REPLY = 14, + PIN_CODE_REQUEST_NEGATIVE_REPLY = 15, + CHANGE_CONNECTION_PACKET_TYPE = 16, + AUTHENTICATION_REQUESTED = 17, + SET_CONNECTION_ENCRYPTION = 20, + CHANGE_CONNECTION_LINK_KEY = 21, + CENTRAL_LINK_KEY = 22, + REMOTE_NAME_REQUEST = 23, + REMOTE_NAME_REQUEST_CANCEL = 24, + READ_REMOTE_SUPPORTED_FEATURES = 25, + READ_REMOTE_EXTENDED_FEATURES = 26, + READ_REMOTE_VERSION_INFORMATION = 27, + READ_CLOCK_OFFSET = 30, + READ_LMP_HANDLE = 31, + HOLD_MODE = 41, + SNIFF_MODE = 42, + EXIT_SNIFF_MODE = 43, + QOS_SETUP = 46, + ROLE_DISCOVERY = 47, + SWITCH_ROLE = 50, + READ_LINK_POLICY_SETTINGS = 51, + WRITE_LINK_POLICY_SETTINGS = 52, + READ_DEFAULT_LINK_POLICY_SETTINGS = 53, + WRITE_DEFAULT_LINK_POLICY_SETTINGS = 54, + FLOW_SPECIFICATION = 55, + SET_EVENT_MASK = 56, + RESET = 57, + SET_EVENT_FILTER = 60, + FLUSH = 61, + READ_PIN_TYPE = 62, + WRITE_PIN_TYPE = 63, + READ_STORED_LINK_KEY = 65, + WRITE_STORED_LINK_KEY = 66, + DELETE_STORED_LINK_KEY = 67, + WRITE_LOCAL_NAME = 70, + READ_LOCAL_NAME = 71, + READ_CONNECTION_ACCEPT_TIMEOUT = 72, + WRITE_CONNECTION_ACCEPT_TIMEOUT = 73, + READ_PAGE_TIMEOUT = 74, + WRITE_PAGE_TIMEOUT = 75, + READ_SCAN_ENABLE = 76, + WRITE_SCAN_ENABLE = 77, + READ_PAGE_SCAN_ACTIVITY = 80, + WRITE_PAGE_SCAN_ACTIVITY = 81, + READ_INQUIRY_SCAN_ACTIVITY = 82, + WRITE_INQUIRY_SCAN_ACTIVITY = 83, + READ_AUTHENTICATION_ENABLE = 84, + WRITE_AUTHENTICATION_ENABLE = 85, + READ_CLASS_OF_DEVICE = 90, + WRITE_CLASS_OF_DEVICE = 91, + READ_VOICE_SETTING = 92, + WRITE_VOICE_SETTING = 93, + READ_AUTOMATIC_FLUSH_TIMEOUT = 94, + WRITE_AUTOMATIC_FLUSH_TIMEOUT = 95, + READ_NUM_BROADCAST_RETRANSMITS = 96, + WRITE_NUM_BROADCAST_RETRANSMITS = 97, + READ_HOLD_MODE_ACTIVITY = 100, + WRITE_HOLD_MODE_ACTIVITY = 101, + READ_TRANSMIT_POWER_LEVEL = 102, + READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 103, + WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 104, + SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 105, + HOST_BUFFER_SIZE = 106, + HOST_NUMBER_OF_COMPLETED_PACKETS = 107, + READ_LINK_SUPERVISION_TIMEOUT = 110, + WRITE_LINK_SUPERVISION_TIMEOUT = 111, + READ_NUMBER_OF_SUPPORTED_IAC = 112, + READ_CURRENT_IAC_LAP = 113, + WRITE_CURRENT_IAC_LAP = 114, + SET_AFH_HOST_CHANNEL_CLASSIFICATION = 121, + READ_INQUIRY_SCAN_TYPE = 124, + WRITE_INQUIRY_SCAN_TYPE = 125, + READ_INQUIRY_MODE = 126, + WRITE_INQUIRY_MODE = 127, + READ_PAGE_SCAN_TYPE = 130, + WRITE_PAGE_SCAN_TYPE = 131, + READ_AFH_CHANNEL_ASSESSMENT_MODE = 132, + WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 133, + READ_LOCAL_VERSION_INFORMATION = 143, + READ_LOCAL_SUPPORTED_FEATURES = 145, + READ_LOCAL_EXTENDED_FEATURES = 146, + READ_BUFFER_SIZE = 147, + READ_BD_ADDR = 151, + READ_FAILED_CONTACT_COUNTER = 152, + RESET_FAILED_CONTACT_COUNTER = 153, + READ_LINK_QUALITY = 154, + READ_RSSI = 155, + READ_AFH_CHANNEL_MAP = 156, + READ_CLOCK = 157, + READ_LOOPBACK_MODE = 160, + WRITE_LOOPBACK_MODE = 161, + ENABLE_DEVICE_UNDER_TEST_MODE = 162, + SETUP_SYNCHRONOUS_CONNECTION = 163, + ACCEPT_SYNCHRONOUS_CONNECTION = 164, + REJECT_SYNCHRONOUS_CONNECTION = 165, + READ_EXTENDED_INQUIRY_RESPONSE = 170, + WRITE_EXTENDED_INQUIRY_RESPONSE = 171, + REFRESH_ENCRYPTION_KEY = 172, + SNIFF_SUBRATING = 174, + READ_SIMPLE_PAIRING_MODE = 175, + WRITE_SIMPLE_PAIRING_MODE = 176, + READ_LOCAL_OOB_DATA = 177, + READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 180, + WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 181, + READ_DEFAULT_ERRONEOUS_DATA_REPORTING = 182, + WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING = 183, + IO_CAPABILITY_REQUEST_REPLY = 187, + USER_CONFIRMATION_REQUEST_REPLY = 190, + USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 191, + USER_PASSKEY_REQUEST_REPLY = 192, + USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 193, + REMOTE_OOB_DATA_REQUEST_REPLY = 194, + WRITE_SIMPLE_PAIRING_DEBUG_MODE = 195, + ENHANCED_FLUSH = 196, + REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 197, + SEND_KEYPRESS_NOTIFICATION = 202, + IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 203, + READ_ENCRYPTION_KEY_SIZE = 204, + SET_EVENT_MASK_PAGE_2 = 222, + READ_FLOW_CONTROL_MODE = 230, + WRITE_FLOW_CONTROL_MODE = 231, + READ_DATA_BLOCK_SIZE = 232, + READ_ENHANCED_TRANSMIT_POWER_LEVEL = 240, + READ_LE_HOST_SUPPORT = 245, + WRITE_LE_HOST_SUPPORT = 246, + LE_SET_EVENT_MASK = 250, + LE_READ_BUFFER_SIZE_V1 = 251, + LE_READ_LOCAL_SUPPORTED_FEATURES = 252, + LE_SET_RANDOM_ADDRESS = 254, + LE_SET_ADVERTISING_PARAMETERS = 255, + LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER = 256, + LE_SET_ADVERTISING_DATA = 257, + LE_SET_SCAN_RESPONSE_DATA = 260, + LE_SET_ADVERTISING_ENABLE = 261, + LE_SET_SCAN_PARAMETERS = 262, + LE_SET_SCAN_ENABLE = 263, + LE_CREATE_CONNECTION = 264, + LE_CREATE_CONNECTION_CANCEL = 265, + LE_READ_FILTER_ACCEPT_LIST_SIZE = 266, + LE_CLEAR_FILTER_ACCEPT_LIST = 267, + LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST = 270, + LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST = 271, + LE_CONNECTION_UPDATE = 272, + LE_SET_HOST_CHANNEL_CLASSIFICATION = 273, + LE_READ_CHANNEL_MAP = 274, + LE_READ_REMOTE_FEATURES = 275, + LE_ENCRYPT = 276, + LE_RAND = 277, + LE_START_ENCRYPTION = 280, + LE_LONG_TERM_KEY_REQUEST_REPLY = 281, + LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 282, + LE_READ_SUPPORTED_STATES = 283, + LE_RECEIVER_TEST_V1 = 284, + LE_TRANSMITTER_TEST_V1 = 285, + LE_TEST_END = 286, + ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 293, + ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 294, + READ_LOCAL_SUPPORTED_CODECS_V1 = 295, + SET_MWS_CHANNEL_PARAMETERS = 296, + SET_EXTERNAL_FRAME_CONFIGURATION = 297, + SET_MWS_SIGNALING = 300, + SET_MWS_TRANSPORT_LAYER = 301, + SET_MWS_SCAN_FREQUENCY_TABLE = 302, + GET_MWS_TRANSPORT_LAYER_CONFIGURATION = 303, + SET_MWS_PATTERN_CONFIGURATION = 304, + SET_TRIGGERED_CLOCK_CAPTURE = 305, + TRUNCATED_PAGE = 306, + TRUNCATED_PAGE_CANCEL = 307, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST = 310, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE = 311, + START_SYNCHRONIZATION_TRAIN = 312, + RECEIVE_SYNCHRONIZATION_TRAIN = 313, + SET_RESERVED_LT_ADDR = 314, + DELETE_RESERVED_LT_ADDR = 315, + SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA = 316, + READ_SYNCHRONIZATION_TRAIN_PARAMETERS = 317, + WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS = 320, + REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY = 321, + READ_SECURE_CONNECTIONS_HOST_SUPPORT = 322, + WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 323, + READ_AUTHENTICATED_PAYLOAD_TIMEOUT = 324, + WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT = 325, + READ_LOCAL_OOB_EXTENDED_DATA = 326, + WRITE_SECURE_CONNECTIONS_TEST_MODE = 327, + READ_EXTENDED_PAGE_TIMEOUT = 330, + WRITE_EXTENDED_PAGE_TIMEOUT = 331, + READ_EXTENDED_INQUIRY_LENGTH = 332, + WRITE_EXTENDED_INQUIRY_LENGTH = 333, + LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 334, + LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 335, + LE_SET_DATA_LENGTH = 336, + LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 337, + LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 340, + LE_READ_LOCAL_P_256_PUBLIC_KEY = 341, + LE_GENERATE_DHKEY_V1 = 342, + LE_ADD_DEVICE_TO_RESOLVING_LIST = 343, + LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 344, + LE_CLEAR_RESOLVING_LIST = 345, + LE_READ_RESOLVING_LIST_SIZE = 346, + LE_READ_PEER_RESOLVABLE_ADDRESS = 347, + LE_READ_LOCAL_RESOLVABLE_ADDRESS = 350, + LE_SET_ADDRESS_RESOLUTION_ENABLE = 351, + LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 352, + LE_READ_MAXIMUM_DATA_LENGTH = 353, + LE_READ_PHY = 354, + LE_SET_DEFAULT_PHY = 355, + LE_SET_PHY = 356, + LE_RECEIVER_TEST_V2 = 357, + LE_TRANSMITTER_TEST_V2 = 360, + LE_SET_ADVERTISING_SET_RANDOM_ADDRESS = 361, + LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 362, + LE_SET_EXTENDED_ADVERTISING_DATA = 363, + LE_SET_EXTENDED_SCAN_RESPONSE_DATA = 364, + LE_SET_EXTENDED_ADVERTISING_ENABLE = 365, + LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 366, + LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 367, + LE_REMOVE_ADVERTISING_SET = 370, + LE_CLEAR_ADVERTISING_SETS = 371, + LE_SET_PERIODIC_ADVERTISING_PARAMETERS = 372, + LE_SET_PERIODIC_ADVERTISING_DATA = 373, + LE_SET_PERIODIC_ADVERTISING_ENABLE = 374, + LE_SET_EXTENDED_SCAN_PARAMETERS = 375, + LE_SET_EXTENDED_SCAN_ENABLE = 376, + LE_EXTENDED_CREATE_CONNECTION = 377, + LE_PERIODIC_ADVERTISING_CREATE_SYNC = 380, + LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 381, + LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 382, + LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST = 383, + LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST = 384, + LE_CLEAR_PERIODIC_ADVERTISER_LIST = 385, + LE_READ_PERIODIC_ADVERTISER_LIST_SIZE = 386, + LE_READ_TRANSMIT_POWER = 387, + LE_READ_RF_PATH_COMPENSATION_POWER = 390, + LE_WRITE_RF_PATH_COMPENSATION_POWER = 391, + LE_SET_PRIVACY_MODE = 392, + LE_RECEIVER_TEST_V3 = 393, + LE_TRANSMITTER_TEST_V3 = 394, + LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS = 395, + LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE = 396, + LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE = 397, + LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS = 400, + LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS = 401, + LE_CONNECTION_CTE_REQUEST_ENABLE = 402, + LE_CONNECTION_CTE_RESPONSE_ENABLE = 403, + LE_READ_ANTENNA_INFORMATION = 404, + LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 405, + LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 406, + LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 407, + LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 410, + LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 411, + LE_GENERATE_DHKEY_V2 = 412, + READ_LOCAL_SIMPLE_PAIRING_OPTIONS = 413, + LE_MODIFY_SLEEP_CLOCK_ACCURACY = 414, + LE_READ_BUFFER_SIZE_V2 = 415, + LE_READ_ISO_TX_SYNC = 416, + LE_SET_CIG_PARAMETERS = 417, + LE_SET_CIG_PARAMETERS_TEST = 420, + LE_CREATE_CIS = 421, + LE_REMOVE_CIG = 422, + LE_ACCEPT_CIS_REQUEST = 423, + LE_REJECT_CIS_REQUEST = 424, + LE_CREATE_BIG = 425, + LE_CREATE_BIG_TEST = 426, + LE_TERMINATE_BIG = 427, + LE_BIG_CREATE_SYNC = 430, + LE_BIG_TERMINATE_SYNC = 431, + LE_REQUEST_PEER_SCA = 432, + LE_SETUP_ISO_DATA_PATH = 433, + LE_REMOVE_ISO_DATA_PATH = 434, + LE_ISO_TRANSMIT_TEST = 435, + LE_ISO_RECEIVE_TEST = 436, + LE_ISO_READ_TEST_COUNTERS = 437, + LE_ISO_TEST_END = 440, + LE_SET_HOST_FEATURE = 441, + LE_READ_ISO_LINK_QUALITY = 442, + LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 443, + LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 444, + LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 445, + LE_SET_PATH_LOSS_REPORTING_ENABLE = 446, + LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 447, + LE_TRANSMITTER_TEST_V4 = 450, + SET_ECOSYSTEM_BASE_INTERVAL = 451, + READ_LOCAL_SUPPORTED_CODECS_V2 = 452, + READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 453, + READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 454, + CONFIGURE_DATA_PATH = 455, + LE_SET_DATA_RELATED_ADDRESS_CHANGES = 456, + SET_MIN_ENCRYPTION_KEY_SIZE = 457, + LE_SET_DEFAULT_SUBRATE = 460, + LE_SUBRATE_REQUEST = 461, +} + +packet Command { + op_code : OpCode, + _size_(_payload_) : 8, + _payload_, +} + +// HCI Event Packets + +enum EventCode : 8 { + INQUIRY_COMPLETE = 0x01, + INQUIRY_RESULT = 0x02, + CONNECTION_COMPLETE = 0x03, + CONNECTION_REQUEST = 0x04, + DISCONNECTION_COMPLETE = 0x05, + AUTHENTICATION_COMPLETE = 0x06, + REMOTE_NAME_REQUEST_COMPLETE = 0x07, + ENCRYPTION_CHANGE = 0x08, + CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09, + CENTRAL_LINK_KEY_COMPLETE = 0x0A, + READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B, + READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C, + QOS_SETUP_COMPLETE = 0x0D, + COMMAND_COMPLETE = 0x0E, + COMMAND_STATUS = 0x0F, + HARDWARE_ERROR = 0x10, + FLUSH_OCCURRED = 0x11, + ROLE_CHANGE = 0x12, + NUMBER_OF_COMPLETED_PACKETS = 0x13, + MODE_CHANGE = 0x14, + RETURN_LINK_KEYS = 0x15, + PIN_CODE_REQUEST = 0x16, + LINK_KEY_REQUEST = 0x17, + LINK_KEY_NOTIFICATION = 0x18, + LOOPBACK_COMMAND = 0x19, + DATA_BUFFER_OVERFLOW = 0x1A, + MAX_SLOTS_CHANGE = 0x1B, + READ_CLOCK_OFFSET_COMPLETE = 0x1C, + CONNECTION_PACKET_TYPE_CHANGED = 0x1D, + QOS_VIOLATION = 0x1E, + PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20, + FLOW_SPECIFICATION_COMPLETE = 0x21, + INQUIRY_RESULT_WITH_RSSI = 0x22, + READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 0x23, + SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C, + SYNCHRONOUS_CONNECTION_CHANGED = 0x2D, + SNIFF_SUBRATING = 0x2E, + EXTENDED_INQUIRY_RESULT = 0x2F, + ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30, + IO_CAPABILITY_REQUEST = 0x31, + IO_CAPABILITY_RESPONSE = 0x32, + USER_CONFIRMATION_REQUEST = 0x33, + USER_PASSKEY_REQUEST = 0x34, + REMOTE_OOB_DATA_REQUEST = 0x35, + SIMPLE_PAIRING_COMPLETE = 0x36, + LINK_SUPERVISION_TIMEOUT_CHANGED = 0x38, + ENHANCED_FLUSH_COMPLETE = 0x39, + USER_PASSKEY_NOTIFICATION = 0x3B, + KEYPRESS_NOTIFICATION = 0x3C, + REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 0x3D, + LE_META_EVENT = 0x3e, + NUMBER_OF_COMPLETED_DATA_BLOCKS = 0x48, + VENDOR_SPECIFIC = 0xFF, +} + +// LE events +enum SubeventCode : 8 { + CONNECTION_COMPLETE = 0x01, + ADVERTISING_REPORT = 0x02, + CONNECTION_UPDATE_COMPLETE = 0x03, + READ_REMOTE_FEATURES_COMPLETE = 0x04, + LONG_TERM_KEY_REQUEST = 0x05, + REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06, + DATA_LENGTH_CHANGE = 0x07, + READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08, + GENERATE_DHKEY_COMPLETE = 0x09, + ENHANCED_CONNECTION_COMPLETE = 0x0a, + DIRECTED_ADVERTISING_REPORT = 0x0b, + PHY_UPDATE_COMPLETE = 0x0c, + EXTENDED_ADVERTISING_REPORT = 0x0D, + PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E, + PERIODIC_ADVERTISING_REPORT = 0x0F, + PERIODIC_ADVERTISING_SYNC_LOST = 0x10, + SCAN_TIMEOUT = 0x11, + ADVERTISING_SET_TERMINATED = 0x12, + SCAN_REQUEST_RECEIVED = 0x13, + CHANNEL_SELECTION_ALGORITHM = 0x14, + CONNECTIONLESS_IQ_REPORT = 0x15, + CONNECTION_IQ_REPORT = 0x16, + CTE_REQUEST_FAILED = 0x17, + PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED = 0x18, + CIS_ESTABLISHED = 0x19, + CIS_REQUEST = 0x1A, + CREATE_BIG_COMPLETE = 0x1B, + TERMINATE_BIG_COMPLETE = 0x1C, + BIG_SYNC_ESTABLISHED = 0x1D, + BIG_SYNC_LOST = 0x1E, + REQUEST_PEER_SCA_COMPLETE = 0x1F, + PATH_LOSS_THRESHOLD = 0x20, + TRANSMIT_POWER_REPORTING = 0x21, + BIG_INFO_ADVERTISING_REPORT = 0x22, + LE_SUBRATE_CHANGE = 0x23, +} + +// Vendor specific events +enum VseSubeventCode : 8 { + STORAGE_THRESHOLD_BREACH = 0x54, + LE_MULTI_ADVERTISING_STATE_CHANGE = 0x55, + LE_ADVERTISEMENT_TRACKING = 0x56, + CONTROLLER_DEBUG_INFO = 0x57, + BLUETOOTH_QUALITY_REPORT = 0x58, +} + +packet Event { + event_code : EventCode, + _size_(_payload_) : 8, + _payload_, +} + +// Common definitions for commands and events + +enum FeatureFlag : 1 { + UNSUPPORTED = 0, + SUPPORTED = 1, +} + +enum ErrorCode: 8 { + STATUS_UNKNOWN = 0xFF, + SUCCESS = 0x00, + UNKNOWN_HCI_COMMAND = 0x01, + UNKNOWN_CONNECTION = 0x02, + HARDWARE_FAILURE = 0x03, + PAGE_TIMEOUT = 0x04, + AUTHENTICATION_FAILURE = 0x05, + PIN_OR_KEY_MISSING = 0x06, + MEMORY_CAPACITY_EXCEEDED = 0x07, + CONNECTION_TIMEOUT = 0x08, + CONNECTION_LIMIT_EXCEEDED = 0x09, + SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x0A, + CONNECTION_ALREADY_EXISTS = 0x0B, + COMMAND_DISALLOWED = 0x0C, + CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0D, + CONNECTION_REJECTED_SECURITY_REASONS = 0x0E, + CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0F, + CONNECTION_ACCEPT_TIMEOUT = 0x10, + UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE = 0x11, + INVALID_HCI_COMMAND_PARAMETERS = 0x12, + REMOTE_USER_TERMINATED_CONNECTION = 0x13, + REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14, + REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15, + CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16, + REPEATED_ATTEMPTS = 0x17, + PAIRING_NOT_ALLOWED = 0x18, + UNKNOWN_LMP_PDU = 0x19, + UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1A, + SCO_OFFSET_REJECTED = 0x1B, + SCO_INTERVAL_REJECTED = 0x1C, + SCO_AIR_MODE_REJECTED = 0x1D, + INVALID_LMP_OR_LL_PARAMETERS = 0x1E, + UNSPECIFIED_ERROR = 0x1F, + UNSUPPORTED_LMP_OR_LL_PARAMETER = 0x20, + ROLE_CHANGE_NOT_ALLOWED = 0x21, + TRANSACTION_RESPONSE_TIMEOUT = 0x22, + LINK_LAYER_COLLISION = 0x23, + ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x25, + ROLE_SWITCH_FAILED = 0x35, + HOST_BUSY = 0x38, + CONTROLLER_BUSY = 0x3A, + ADVERTISING_TIMEOUT = 0x3C, + CONNECTION_FAILED_ESTABLISHMENT = 0x3E, + UNKNOWN_ADVERTISING_IDENTIFIER = 0x42, + LIMIT_REACHED = 0x43, + OPERATION_CANCELLED_BY_HOST = 0x44, + PACKET_TOO_LONG = 0x45, +} + +// Events that are defined with their respective commands + +packet CommandComplete : Event (event_code = COMMAND_COMPLETE) { + num_hci_command_packets : 8, + command_op_code : OpCode, + _payload_, +} + +packet CommandStatus : Event (event_code = COMMAND_STATUS) { + status : ErrorCode, // SUCCESS means PENDING + num_hci_command_packets : 8, + command_op_code : OpCode, + _payload_, +} + +packet VendorSpecificEvent : Event (event_code = VENDOR_SPECIFIC) { + subevent_code : VseSubeventCode, + _payload_, +} + + // Credits +packet NoCommandComplete : CommandComplete (command_op_code = NONE) { +} + +struct Lap { // Lower Address Part + lap : 6, + _reserved_ : 2, + _fixed_ = 0x9e8b : 16, +} + + // LINK_CONTROL +packet Inquiry : Command (op_code = INQUIRY) { + lap : Lap, + inquiry_length : 8, // 0x1 - 0x30 (times 1.28s) + num_responses : 8, // 0x00 unlimited +} + +test Inquiry { + "\x01\x04\x05\x33\x8b\x9e\xaa\xbb", +} + +packet InquiryStatus : CommandStatus (command_op_code = INQUIRY) { +} + +test InquiryStatus { + "\x0f\x04\x00\x01\x01\x04", +} + +packet InquiryCancel : Command (op_code = INQUIRY_CANCEL) { +} + +test InquiryCancel { + "\x02\x04\x00", +} + +packet InquiryCancelComplete : CommandComplete (command_op_code = INQUIRY_CANCEL) { + status : ErrorCode, +} + +test InquiryCancelComplete { + "\x0e\x04\x01\x02\x04\x00", +} + +packet PeriodicInquiryMode : Command (op_code = PERIODIC_INQUIRY_MODE) { + max_period_length : 16, // Range 0x0003 to 0xffff (times 1.28s) + min_period_length : 16, // Range 0x0002 to 0xfffe (times 1.28s) + lap : Lap, + inquiry_length : 8, // 0x1 - 0x30 (times 1.28s) + num_responses : 8, // 0x00 unlimited +} + +test PeriodicInquiryMode { + "\x03\x04\x09\x12\x34\x56\x78\x11\x8b\x9e\x9a\xbc", +} + +packet PeriodicInquiryModeComplete : CommandComplete (command_op_code = PERIODIC_INQUIRY_MODE) { + status : ErrorCode, +} + +test PeriodicInquiryModeComplete { + "\x0e\x04\x01\x03\x04\x00", +} + +packet ExitPeriodicInquiryMode : Command (op_code = EXIT_PERIODIC_INQUIRY_MODE) { +} + +test ExitPeriodicInquiryMode { + "\x04\x04\x00", +} + +packet ExitPeriodicInquiryModeComplete : CommandComplete (command_op_code = EXIT_PERIODIC_INQUIRY_MODE) { + status : ErrorCode, +} + +test ExitPeriodicInquiryModeComplete { + "\x0e\x04\x01\x04\x04\x00", +} + +enum PageScanRepetitionMode : 8 { + R0 = 0x00, + R1 = 0x01, + R2 = 0x02, +} + +enum ClockOffsetValid : 1 { + INVALID = 0, + VALID = 1, +} + +enum CreateConnectionRoleSwitch : 8 { + REMAIN_CENTRAL = 0x00, + ALLOW_ROLE_SWITCH = 0x01, +} + +packet CreateConnection : Command (op_code = CREATE_CONNECTION) { + bd_addr : Address, + packet_type : 16, + page_scan_repetition_mode : PageScanRepetitionMode, + _reserved_ : 8, + clock_offset : 15, + clock_offset_valid : ClockOffsetValid, + allow_role_switch : CreateConnectionRoleSwitch, +} + +packet CreateConnectionStatus : CommandStatus (command_op_code = CREATE_CONNECTION) { +} + +enum DisconnectReason : 8 { + AUTHENTICATION_FAILURE = 0x05, + REMOTE_USER_TERMINATED_CONNECTION = 0x13, + REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14, + REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15, + UNSUPPORTED_REMOTE_FEATURE = 0x1A, + PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29, + UNACCEPTABLE_CONNECTION_PARAMETERS = 0x3B, +} + +packet Disconnect : Command (op_code = DISCONNECT) { + connection_handle : 12, + _reserved_ : 4, + reason : DisconnectReason, +} + +packet DisconnectStatus : CommandStatus (command_op_code = DISCONNECT) { +} + +packet AddScoConnection : Command (op_code = ADD_SCO_CONNECTION) { + connection_handle : 12, + _reserved_ : 4, + packet_type : 16, +} + +packet AddScoConnectionStatus : CommandStatus (command_op_code = ADD_SCO_CONNECTION) { +} + +packet CreateConnectionCancel : Command (op_code = CREATE_CONNECTION_CANCEL) { + bd_addr : Address, +} + +packet CreateConnectionCancelComplete : CommandComplete (command_op_code = CREATE_CONNECTION_CANCEL) { + status : ErrorCode, + bd_addr : Address, +} + +enum AcceptConnectionRequestRole : 8 { + BECOME_CENTRAL = 0x00, + REMAIN_PERIPHERAL = 0x01, +} + +packet AcceptConnectionRequest : Command (op_code = ACCEPT_CONNECTION_REQUEST) { + bd_addr : Address, + role : AcceptConnectionRequestRole, +} + +packet AcceptConnectionRequestStatus : CommandStatus (command_op_code = ACCEPT_CONNECTION_REQUEST) { +} + +enum RejectConnectionReason : 8 { + LIMITED_RESOURCES = 0x0D, + SECURITY_REASONS = 0x0E, + UNACCEPTABLE_BD_ADDR = 0x0F, +} + +packet RejectConnectionRequest : Command (op_code = REJECT_CONNECTION_REQUEST) { + bd_addr : Address, + reason : RejectConnectionReason, +} + +packet RejectConnectionRequestStatus : CommandStatus (command_op_code = REJECT_CONNECTION_REQUEST) { +} + +packet LinkKeyRequestReply : Command (op_code = LINK_KEY_REQUEST_REPLY) { + bd_addr : Address, + link_key : 8[16], +} + +packet LinkKeyRequestReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet LinkKeyRequestNegativeReply : Command (op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, +} + +packet LinkKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet PinCodeRequestReply : Command (op_code = PIN_CODE_REQUEST_REPLY) { + bd_addr : Address, + pin_code_length : 5, // 0x01 - 0x10 + _reserved_ : 3, + pin_code : 8[16], // string parameter, first octet first +} + +packet PinCodeRequestReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet PinCodeRequestNegativeReply : Command (op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, +} + +packet PinCodeRequestNegativeReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet ChangeConnectionPacketType : Command (op_code = CHANGE_CONNECTION_PACKET_TYPE) { + connection_handle : 12, + _reserved_ : 4, + packet_type : 16, +} + +packet ChangeConnectionPacketTypeStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_PACKET_TYPE) { +} + +packet AuthenticationRequested : Command (op_code = AUTHENTICATION_REQUESTED) { + connection_handle : 12, + _reserved_ : 4, +} + +packet AuthenticationRequestedStatus : CommandStatus (command_op_code = AUTHENTICATION_REQUESTED) { +} + +packet SetConnectionEncryption : Command (op_code = SET_CONNECTION_ENCRYPTION) { + connection_handle : 12, + _reserved_ : 4, + encryption_enable : Enable, +} + +packet SetConnectionEncryptionStatus : CommandStatus (command_op_code = SET_CONNECTION_ENCRYPTION) { +} + +packet ChangeConnectionLinkKey : Command (op_code = CHANGE_CONNECTION_LINK_KEY) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ChangeConnectionLinkKeyStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_LINK_KEY) { +} + +enum KeyFlag : 8 { + SEMI_PERMANENT = 0x00, + TEMPORARY = 0x01, +} + +packet CentralLinkKey : Command (op_code = CENTRAL_LINK_KEY) { + key_flag : KeyFlag, +} + +packet CentralLinkKeyStatus : CommandStatus (command_op_code = CENTRAL_LINK_KEY) { +} + +packet RemoteNameRequest : Command (op_code = REMOTE_NAME_REQUEST) { + bd_addr : Address, + page_scan_repetition_mode : PageScanRepetitionMode, + _reserved_ : 8, + clock_offset : 15, + clock_offset_valid : ClockOffsetValid, +} + +packet RemoteNameRequestStatus : CommandStatus (command_op_code = REMOTE_NAME_REQUEST) { +} + +packet RemoteNameRequestCancel : Command (op_code = REMOTE_NAME_REQUEST_CANCEL) { + bd_addr : Address, +} + +packet RemoteNameRequestCancelComplete : CommandComplete (command_op_code = REMOTE_NAME_REQUEST_CANCEL) { + status : ErrorCode, + bd_addr : Address, +} + +packet ReadRemoteSupportedFeatures : Command (op_code = READ_REMOTE_SUPPORTED_FEATURES) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) { +} + +packet ReadRemoteExtendedFeatures : Command (op_code = READ_REMOTE_EXTENDED_FEATURES) { + connection_handle : 12, + _reserved_ : 4, + page_number : 8, +} + +packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) { +} + +packet ReadRemoteVersionInformation : Command (op_code = READ_REMOTE_VERSION_INFORMATION) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadRemoteVersionInformationStatus : CommandStatus (command_op_code = READ_REMOTE_VERSION_INFORMATION) { +} + +packet ReadClockOffset : Command (op_code = READ_CLOCK_OFFSET) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadClockOffsetStatus : CommandStatus (command_op_code = READ_CLOCK_OFFSET) { +} + +packet ReadLmpHandle : Command (op_code = READ_LMP_HANDLE) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadLmpHandleComplete : CommandComplete (command_op_code = READ_LMP_HANDLE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + lmp_handle : 8, + _reserved_ : 32, +} + +enum SynchronousPacketTypeBits : 16 { + HV1_ALLOWED = 0x0001, + HV2_ALLOWED = 0x0002, + HV3_ALLOWED = 0x0004, + EV3_ALLOWED = 0x0008, + EV4_ALLOWED = 0x0010, + EV5_ALLOWED = 0x0020, + NO_2_EV3_ALLOWED = 0x0040, + NO_3_EV3_ALLOWED = 0x0080, + NO_2_EV5_ALLOWED = 0x0100, + NO_3_EV5_ALLOWED = 0x0200, +} + +enum RetransmissionEffort : 8 { + NO_RETRANSMISSION = 0x00, + OPTIMIZED_FOR_POWER = 0x01, + OPTIMIZED_FOR_LINK_QUALITY = 0x02, + DO_NOT_CARE = 0xFF, +} + +packet SetupSynchronousConnection : Command (op_code = SETUP_SYNCHRONOUS_CONNECTION) { + connection_handle : 12, + _reserved_ : 4, + transmit_bandwidth : 32, + receive_bandwidth : 32, + max_latency : 16, // 0-3 reserved, 0xFFFF = don't care + voice_setting : 10, + _reserved_ : 6, + retransmission_effort : RetransmissionEffort, + packet_type : 16, // See SynchronousPacketTypeBits +} + +packet SetupSynchronousConnectionStatus : CommandStatus (command_op_code = SETUP_SYNCHRONOUS_CONNECTION) { +} + +packet AcceptSynchronousConnection : Command (op_code = ACCEPT_SYNCHRONOUS_CONNECTION) { + bd_addr : Address, + transmit_bandwidth : 32, + receive_bandwidth : 32, + max_latency : 16, // 0-3 reserved, 0xFFFF = don't care + voice_setting : 10, + _reserved_ : 6, + retransmission_effort : RetransmissionEffort, + packet_type : 16, // See SynchronousPacketTypeBits +} + +packet AcceptSynchronousConnectionStatus : CommandStatus (command_op_code = ACCEPT_SYNCHRONOUS_CONNECTION) { +} + +packet RejectSynchronousConnection : Command (op_code = REJECT_SYNCHRONOUS_CONNECTION) { + bd_addr : Address, + reason : RejectConnectionReason, +} + +packet RejectSynchronousConnectionStatus : CommandStatus (command_op_code = REJECT_SYNCHRONOUS_CONNECTION) { +} + +enum IoCapability : 8 { + DISPLAY_ONLY = 0x00, + DISPLAY_YES_NO = 0x01, + KEYBOARD_ONLY = 0x02, + NO_INPUT_NO_OUTPUT = 0x03, +} + +enum OobDataPresent : 8 { + NOT_PRESENT = 0x00, + P_192_PRESENT = 0x01, + P_256_PRESENT = 0x02, + P_192_AND_256_PRESENT = 0x03, +} + +enum AuthenticationRequirements : 8 { + NO_BONDING = 0x00, + NO_BONDING_MITM_PROTECTION = 0x01, + DEDICATED_BONDING = 0x02, + DEDICATED_BONDING_MITM_PROTECTION = 0x03, + GENERAL_BONDING = 0x04, + GENERAL_BONDING_MITM_PROTECTION = 0x05, +} + +packet IoCapabilityRequestReply : Command (op_code = IO_CAPABILITY_REQUEST_REPLY) { + bd_addr : Address, + io_capability : IoCapability, + oob_present : OobDataPresent, + authentication_requirements : AuthenticationRequirements, +} + +packet IoCapabilityRequestReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet UserConfirmationRequestReply : Command (op_code = USER_CONFIRMATION_REQUEST_REPLY) { + bd_addr : Address, +} + +packet UserConfirmationRequestReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet UserConfirmationRequestNegativeReply : Command (op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, +} + +packet UserConfirmationRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet UserPasskeyRequestReply : Command (op_code = USER_PASSKEY_REQUEST_REPLY) { + bd_addr : Address, + numeric_value : 32, // 000000-999999 decimal or 0x0-0xF423F +} + +packet UserPasskeyRequestReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet UserPasskeyRequestNegativeReply : Command (op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, +} + +packet UserPasskeyRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet RemoteOobDataRequestReply : Command (op_code = REMOTE_OOB_DATA_REQUEST_REPLY) { + bd_addr : Address, + c : 8[16], + r : 8[16], +} + +packet RemoteOobDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet RemoteOobDataRequestNegativeReply : Command (op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, +} + +packet RemoteOobDataRequestNegativeReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +packet IoCapabilityRequestNegativeReply : Command (op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) { + bd_addr : Address, + reason : ErrorCode, +} + +packet IoCapabilityRequestNegativeReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + +enum ScoCodingFormatValues : 8 { + ULAW_LONG = 0x00, + ALAW_LONG = 0x01, + CVSD = 0x02, + TRANSPARENT = 0x03, + LINEAR_PCM = 0x04, + MSBC = 0x05, + LC3 = 0x06, + VENDOR_SPECIFIC = 0xFF, +} + +struct ScoCodingFormat { + coding_format : ScoCodingFormatValues, + company_id : 16, + vendor_specific_codec_id : 16, +} + +enum ScoPcmDataFormat : 8 { + NOT_USED = 0x00, + ONES_COMPLEMENT = 0x01, + TWOS_COMPLEMENT = 0x02, + SIGN_MAGNITUDE = 0x03, + UNSIGNED = 0x04, +} + +enum ScoDataPath : 8 { + HCI = 0x00, + // 0x01 to 0xFE are Logical_Channel_Number. + // The meaning of the logical channels will be vendor specific. + // In GD and legacy Android Bluetooth stack, we use channel 0x01 for hardware + // offloaded SCO encoding + GD_PCM = 0x01, + AUDIO_TEST_MODE = 0xFF, +} + +packet EnhancedSetupSynchronousConnection : Command (op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) { + connection_handle: 12, + _reserved_ : 4, + // Next two items + // [0x00000000, 0xFFFFFFFE] Bandwidth in octets per second. + // [0xFFFFFFFF]: Don't care + transmit_bandwidth : 32, + receive_bandwidth : 32, + transmit_coding_format : ScoCodingFormat, + receive_coding_format : ScoCodingFormat, + // Next two items + // [0x0001, 0xFFFF]: the actual size of the over-the-air encoded frame in + // octets. + transmit_codec_frame_size : 16, + receive_codec_frame_size : 16, + // Next two items + // Host to Controller nominal data rate in octets per second. + input_bandwidth : 32, + output_bandwidth : 32, + input_coding_format : ScoCodingFormat, + output_coding_format : ScoCodingFormat, + // Next two items + // Size, in bits, of the sample or framed data + input_coded_data_bits : 16, + output_coded_data_bits : 16, + input_pcm_data_format : ScoPcmDataFormat, + output_pcm_data_format : ScoPcmDataFormat, + // Next two items + // The number of bit positions within an audio sample that the MSB of the + // sample is away from starting at the MSB of the data. + input_pcm_sample_payload_msb_position : 8, + output_pcm_sample_payload_msb_position : 8, + input_data_path : ScoDataPath, + output_data_path : ScoDataPath, + // Next two items + // [1, 255] The number of bits in each unit of data received from the Host + // over the audio data transport. + // [0] Not applicable (implied by the choice of audio data transport) + input_transport_unit_bits : 8, + output_transport_unit_bits : 8, + // [0x0004, 0xFFFE]: in milliseconds + // Upper limit represent the sum of the synchronous interval and the size + // of the eSCO window, where the eSCO window is reserved slots plus the + // retransmission window + // [0xFFFF]: don't care + max_latency: 16, + packet_type : 16, // See SynchronousPacketTypeBits + retransmission_effort : RetransmissionEffort, +} + +test EnhancedSetupSynchronousConnection { + "\x3d\x04\x3b\x02\x00\x40\x1f\x00\x00\x40\x1f\x00\x00\x05\x00\x00\x00\x00\x05\x00\x00\x00\x00\x3c\x00\x3c\x00\x00\x7d\x00\x00\x00\x7d\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x00\x10\x00\x10\x00\x02\x02\x00\x00\x01\x01\x00\x00\x0d\x00\x88\x03\x02", +} + +packet EnhancedSetupSynchronousConnectionStatus : CommandStatus (command_op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) { +} + +packet EnhancedAcceptSynchronousConnection : Command (op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) { + bd_addr : Address, + // Next two items + // [0x00000000, 0xFFFFFFFE] Bandwidth in octets per second. + // [0xFFFFFFFF]: Don't care + transmit_bandwidth : 32, + receive_bandwidth : 32, + transmit_coding_format : ScoCodingFormat, + receive_coding_format : ScoCodingFormat, + // Next two items + // [0x0001, 0xFFFF]: the actual size of the over-the-air encoded frame in + // octets. + transmit_codec_frame_size : 16, + receive_codec_frame_size : 16, + // Next two items + // Host to Controller nominal data rate in octets per second. + input_bandwidth : 32, + output_bandwidth : 32, + input_coding_format : ScoCodingFormat, + output_coding_format : ScoCodingFormat, + // Next two items + // Size, in bits, of the sample or framed data + input_coded_data_bits : 16, + output_coded_data_bits : 16, + input_pcm_data_format : ScoPcmDataFormat, + output_pcm_data_format : ScoPcmDataFormat, + // Next two items + // The number of bit positions within an audio sample that the MSB of the + // sample is away from starting at the MSB of the data. + input_pcm_sample_payload_msb_position : 8, + output_pcm_sample_payload_msb_position : 8, + input_data_path : ScoDataPath, + output_data_path : ScoDataPath, + // Next two items + // [1, 255] The number of bits in each unit of data received from the Host + // over the audio data transport. + // [0] Not applicable (implied by the choice of audio data transport) + input_transport_unit_bits : 8, + output_transport_unit_bits : 8, + // [0x0004, 0xFFFE]: in milliseconds + // Upper limit represent the sum of the synchronous interval and the size + // of the eSCO window, where the eSCO window is reserved slots plus the + // retransmission window + // [0xFFFF]: don't care + max_latency : 16, + packet_type : 16, // See SynchronousPacketTypeBits + retransmission_effort : RetransmissionEffort, +} + +packet EnhancedAcceptSynchronousConnectionStatus : CommandStatus (command_op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) { +} + +packet RemoteOobExtendedDataRequestReply : Command (op_code = REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY) { + bd_addr : Address, + c_192 : 8[16], + r_192 : 8[16], + c_256 : 8[16], + r_256 : 8[16], +} + +packet RemoteOobExtendedDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY) { + status : ErrorCode, + bd_addr : Address, +} + + + // LINK_POLICY +packet HoldMode : Command (op_code = HOLD_MODE) { + connection_handle : 12, + _reserved_ : 4, + hold_mode_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s) + hold_mode_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s) +} + +packet HoldModeStatus : CommandStatus (command_op_code = HOLD_MODE) { +} + + +packet SniffMode : Command (op_code = SNIFF_MODE) { + connection_handle : 12, + _reserved_ : 4, + sniff_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s) + sniff_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s) + sniff_attempt: 16, // 0x0001-0x7FFF (1.25ms-40.9s) + sniff_timeout: 16, // 0x0000-0x7FFF (0ms-40.9s) +} + +packet SniffModeStatus : CommandStatus (command_op_code = SNIFF_MODE) { +} + + +packet ExitSniffMode : Command (op_code = EXIT_SNIFF_MODE) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ExitSniffModeStatus : CommandStatus (command_op_code = EXIT_SNIFF_MODE) { +} + +enum ServiceType : 8 { + NO_TRAFFIC = 0x00, + BEST_EFFORT = 0x01, + GUARANTEED = 0x02, +} + +packet QosSetup : Command (op_code = QOS_SETUP) { + connection_handle : 12, + _reserved_ : 4, + _reserved_ : 8, + service_type : ServiceType, + token_rate : 32, // Octets/s + peak_bandwidth : 32, // Octets/s + latency : 32, // Octets/s + delay_variation : 32, // microseconds +} + +packet QosSetupStatus : CommandStatus (command_op_code = QOS_SETUP) { +} + +packet RoleDiscovery : Command (op_code = ROLE_DISCOVERY) { + connection_handle : 12, + _reserved_ : 4, +} + +enum Role : 8 { + CENTRAL = 0x00, + PERIPHERAL = 0x01, +} + +packet RoleDiscoveryComplete : CommandComplete (command_op_code = ROLE_DISCOVERY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + current_role : Role, +} + +packet SwitchRole : Command (op_code = SWITCH_ROLE) { + bd_addr : Address, + role : Role, +} + +packet SwitchRoleStatus : CommandStatus (command_op_code = SWITCH_ROLE) { +} + + +packet ReadLinkPolicySettings : Command (op_code = READ_LINK_POLICY_SETTINGS) { + connection_handle : 12, + _reserved_ : 4, +} + +enum LinkPolicy : 16 { + ENABLE_ROLE_SWITCH = 0x01, + ENABLE_HOLD_MODE = 0x02, + ENABLE_SNIFF_MODE = 0x04, + ENABLE_PARK_MODE = 0x08, // deprecated after 5.0 +} + +packet ReadLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_LINK_POLICY_SETTINGS) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + link_policy_settings : 16, +} + +packet WriteLinkPolicySettings : Command (op_code = WRITE_LINK_POLICY_SETTINGS) { + connection_handle : 12, + _reserved_ : 4, + link_policy_settings : 16, +} + +packet WriteLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_LINK_POLICY_SETTINGS) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadDefaultLinkPolicySettings : Command (op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) { +} + +packet ReadDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) { + status : ErrorCode, + default_link_policy_settings : 16, +} + +packet WriteDefaultLinkPolicySettings : Command (op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) { + default_link_policy_settings : 16, +} + +packet WriteDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) { + status : ErrorCode, +} + +enum FlowDirection : 8 { + OUTGOING_FLOW = 0x00, + INCOMING_FLOW = 0x01, +} + +packet FlowSpecification : Command (op_code = FLOW_SPECIFICATION) { + connection_handle : 12, + _reserved_ : 4, + _reserved_ : 8, + flow_direction : FlowDirection, + service_type : ServiceType, + token_rate : 32, // Octets/s + token_bucket_size : 32, + peak_bandwidth : 32, // Octets/s + access_latency : 32, // Octets/s +} + +packet FlowSpecificationStatus : CommandStatus (command_op_code = FLOW_SPECIFICATION) { +} + +packet SniffSubrating : Command (op_code = SNIFF_SUBRATING) { + connection_handle : 12, + _reserved_ : 4, + maximum_latency : 16, // 0x0002-0xFFFE (1.25ms-40.9s) + minimum_remote_timeout : 16, // 0x0000-0xFFFE (0-40.9s) + minimum_local_timeout: 16, // 0x0000-0xFFFE (0-40.9s) +} + +packet SniffSubratingComplete : CommandComplete (command_op_code = SNIFF_SUBRATING) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + + // CONTROLLER_AND_BASEBAND +packet SetEventMask : Command (op_code = SET_EVENT_MASK) { + event_mask : 64, +} + +packet SetEventMaskComplete : CommandComplete (command_op_code = SET_EVENT_MASK) { + status : ErrorCode, +} + +packet Reset : Command (op_code = RESET) { +} + +test Reset { + "\x03\x0c\x00", +} + +packet ResetComplete : CommandComplete (command_op_code = RESET) { + status : ErrorCode, +} + +test ResetComplete { + "\x0e\x04\x01\x03\x0c\x00", + "\x0e\x04\x01\x03\x0c\x01", // unknown command +} + +enum FilterType : 8 { + CLEAR_ALL_FILTERS = 0x00, + INQUIRY_RESULT = 0x01, + CONNECTION_SETUP = 0x02, +} + +packet SetEventFilter : Command (op_code = SET_EVENT_FILTER) { + filter_type : FilterType, + _body_, +} + +packet SetEventFilterComplete : CommandComplete (command_op_code = SET_EVENT_FILTER) { + status : ErrorCode, +} + +packet SetEventFilterClearAll : SetEventFilter (filter_type = CLEAR_ALL_FILTERS) { +} + +enum FilterConditionType : 8 { + ALL_DEVICES = 0x00, + CLASS_OF_DEVICE = 0x01, + ADDRESS = 0x02, +} + +packet SetEventFilterInquiryResult : SetEventFilter (filter_type = INQUIRY_RESULT) { + filter_condition_type : FilterConditionType, + _body_, +} + +packet SetEventFilterInquiryResultAllDevices : SetEventFilterInquiryResult (filter_condition_type = ALL_DEVICES) { +} + +packet SetEventFilterInquiryResultClassOfDevice : SetEventFilterInquiryResult (filter_condition_type = CLASS_OF_DEVICE) { + class_of_device : 24, + class_of_device_mask : 24, +} + +packet SetEventFilterInquiryResultAddress : SetEventFilterInquiryResult (filter_condition_type = ADDRESS) { + address : Address, +} + +packet SetEventFilterConnectionSetup : SetEventFilter (filter_type = CONNECTION_SETUP) { + filter_condition_type : FilterConditionType, + _body_, +} + +enum AutoAcceptFlag : 8 { + AUTO_ACCEPT_OFF = 0x01, + AUTO_ACCEPT_ON_ROLE_SWITCH_DISABLED = 0x02, + AUTO_ACCEPT_ON_ROLE_SWITCH_ENABLED = 0x03, +} + +packet SetEventFilterConnectionSetupAllDevices : SetEventFilterConnectionSetup (filter_condition_type = ALL_DEVICES) { + auto_accept_flag : AutoAcceptFlag, +} + +packet SetEventFilterConnectionSetupClassOfDevice : SetEventFilterConnectionSetup (filter_condition_type = CLASS_OF_DEVICE) { + class_of_device : 24, + class_of_device_mask : 24, + auto_accept_flag : AutoAcceptFlag, +} + +packet SetEventFilterConnectionSetupAddress : SetEventFilterConnectionSetup (filter_condition_type = ADDRESS) { + address : Address, + auto_accept_flag : AutoAcceptFlag, +} + +packet Flush : Command (op_code = FLUSH) { + connection_handle : 12, + _reserved_ : 4, +} + +packet FlushComplete : CommandComplete (command_op_code = FLUSH) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +enum PinType : 8 { + VARIABLE = 0, + FIXED = 1, +} + +packet ReadPinType : Command (op_code = READ_PIN_TYPE) { +} + +packet ReadPinTypeComplete : CommandComplete (command_op_code = READ_PIN_TYPE) { + status : ErrorCode, + pin_type : PinType, +} + +packet WritePinType : Command (op_code = WRITE_PIN_TYPE) { + pin_type : PinType, +} + +packet WritePinTypeComplete : CommandComplete (command_op_code = WRITE_PIN_TYPE) { + status : ErrorCode, +} + +enum ReadStoredLinkKeyReadAllFlag : 8 { + SPECIFIED_BD_ADDR = 0x00, + ALL = 0x01, +} + +packet ReadStoredLinkKey : Command (op_code = READ_STORED_LINK_KEY) { + bd_addr : Address, + read_all_flag : ReadStoredLinkKeyReadAllFlag, +} + +packet ReadStoredLinkKeyComplete : CommandComplete (command_op_code = READ_STORED_LINK_KEY) { + status : ErrorCode, + max_num_keys : 16, + num_keys_read : 16, +} + +struct KeyAndAddress { + address : Address, + link_key : 8[16], +} + +packet WriteStoredLinkKey : Command (op_code = WRITE_STORED_LINK_KEY) { + _count_(keys_to_write) : 8, // 0x01-0x0B + keys_to_write : KeyAndAddress[], +} + +packet WriteStoredLinkKeyComplete : CommandComplete (command_op_code = WRITE_STORED_LINK_KEY) { + status : ErrorCode, + num_keys_written : 8, +} + +enum DeleteStoredLinkKeyDeleteAllFlag : 8 { + SPECIFIED_BD_ADDR = 0x00, + ALL = 0x01, +} + +packet DeleteStoredLinkKey : Command (op_code = DELETE_STORED_LINK_KEY) { + bd_addr : Address, + delete_all_flag : DeleteStoredLinkKeyDeleteAllFlag, +} + +packet DeleteStoredLinkKeyComplete : CommandComplete (command_op_code = DELETE_STORED_LINK_KEY) { + status : ErrorCode, + num_keys_deleted : 16, +} + +packet WriteLocalName : Command (op_code = WRITE_LOCAL_NAME) { + local_name : 8[248], // Null-terminated UTF-8 encoded name +} + +packet WriteLocalNameComplete : CommandComplete (command_op_code = WRITE_LOCAL_NAME) { + status : ErrorCode, +} + +packet ReadLocalName : Command (op_code = READ_LOCAL_NAME) { +} + +packet ReadLocalNameComplete : CommandComplete (command_op_code = READ_LOCAL_NAME) { + status : ErrorCode, + local_name : 8[248], // Null-terminated UTF-8 encoded name +} + +packet ReadConnectionAcceptTimeout : Command (op_code = READ_CONNECTION_ACCEPT_TIMEOUT) { +} + +packet ReadConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = READ_CONNECTION_ACCEPT_TIMEOUT) { + status : ErrorCode, + conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s +} + +packet WriteConnectionAcceptTimeout : Command (op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) { + conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s, Default 0x1FA0, 5.06s +} + +packet WriteConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) { + status : ErrorCode, +} + +packet ReadPageTimeout : Command (op_code = READ_PAGE_TIMEOUT) { +} + +test ReadPageTimeout { + "\x17\x0c\x00", +} + +packet ReadPageTimeoutComplete : CommandComplete (command_op_code = READ_PAGE_TIMEOUT) { + status : ErrorCode, + page_timeout : 16, +} + +test ReadPageTimeoutComplete { + "\x0e\x06\x01\x17\x0c\x00\x11\x22", +} + +packet WritePageTimeout : Command (op_code = WRITE_PAGE_TIMEOUT) { + page_timeout : 16, +} + +test WritePageTimeout { + "\x18\x0c\x02\x00\x20", +} + +packet WritePageTimeoutComplete : CommandComplete (command_op_code = WRITE_PAGE_TIMEOUT) { + status : ErrorCode, +} + +test WritePageTimeoutComplete { + "\x0e\x04\x01\x18\x0c\x00", +} + +enum ScanEnable : 8 { + NO_SCANS = 0x00, + INQUIRY_SCAN_ONLY = 0x01, + PAGE_SCAN_ONLY = 0x02, + INQUIRY_AND_PAGE_SCAN = 0x03, +} + +packet ReadScanEnable : Command (op_code = READ_SCAN_ENABLE) { +} + +packet ReadScanEnableComplete : CommandComplete (command_op_code = READ_SCAN_ENABLE) { + status : ErrorCode, + scan_enable : ScanEnable, +} + +packet WriteScanEnable : Command (op_code = WRITE_SCAN_ENABLE) { + scan_enable : ScanEnable, +} + +packet WriteScanEnableComplete : CommandComplete (command_op_code = WRITE_SCAN_ENABLE) { + status : ErrorCode, +} + +packet ReadPageScanActivity : Command (op_code = READ_PAGE_SCAN_ACTIVITY) { +} + +packet ReadPageScanActivityComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_ACTIVITY) { + status : ErrorCode, + page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms + page_scan_window : 16, // 0x0011 to PageScanInterval +} + +packet WritePageScanActivity : Command (op_code = WRITE_PAGE_SCAN_ACTIVITY) { + page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms + page_scan_window : 16, // 0x0011 to PageScanInterval +} + +packet WritePageScanActivityComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_ACTIVITY) { + status : ErrorCode, +} + +packet ReadInquiryScanActivity : Command (op_code = READ_INQUIRY_SCAN_ACTIVITY) { +} + +test ReadInquiryScanActivity { + "\x1d\x0c\x00", +} + +packet ReadInquiryScanActivityComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_ACTIVITY) { + status : ErrorCode, + inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms + inquiry_scan_window : 16, // Range: 0x0011 to 0x1000 +} + +test ReadInquiryScanActivityComplete { + "\x0e\x08\x01\x1d\x0c\x00\xaa\xbb\xcc\xdd", +} + +packet WriteInquiryScanActivity : Command (op_code = WRITE_INQUIRY_SCAN_ACTIVITY) { + inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms + inquiry_scan_window : 16, // Range: 0x0011 to 0x1000 +} + +test WriteInquiryScanActivity { + "\x1e\x0c\x04\x00\x08\x12\x00", +} + +packet WriteInquiryScanActivityComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_ACTIVITY) { + status : ErrorCode, +} + +test WriteInquiryScanActivityComplete { + "\x0e\x04\x01\x1e\x0c\x00", +} + +enum AuthenticationEnable : 8 { + NOT_REQUIRED = 0x00, + REQUIRED = 0x01, +} + +packet ReadAuthenticationEnable : Command (op_code = READ_AUTHENTICATION_ENABLE) { +} + +packet ReadAuthenticationEnableComplete : CommandComplete (command_op_code = READ_AUTHENTICATION_ENABLE) { + status : ErrorCode, + authentication_enable : AuthenticationEnable, +} + +packet WriteAuthenticationEnable : Command (op_code = WRITE_AUTHENTICATION_ENABLE) { + authentication_enable : AuthenticationEnable, +} + +packet WriteAuthenticationEnableComplete : CommandComplete (command_op_code = WRITE_AUTHENTICATION_ENABLE) { + status : ErrorCode, +} + +packet ReadClassOfDevice : Command (op_code = READ_CLASS_OF_DEVICE) { +} + +packet ReadClassOfDeviceComplete : CommandComplete (command_op_code = READ_CLASS_OF_DEVICE) { + status : ErrorCode, + class_of_device : 24, +} + +packet WriteClassOfDevice : Command (op_code = WRITE_CLASS_OF_DEVICE) { + class_of_device : 24, +} + +packet WriteClassOfDeviceComplete : CommandComplete (command_op_code = WRITE_CLASS_OF_DEVICE) { + status : ErrorCode, +} + +packet ReadVoiceSetting : Command (op_code = READ_VOICE_SETTING) { +} + +packet ReadVoiceSettingComplete : CommandComplete (command_op_code = READ_VOICE_SETTING) { + status : ErrorCode, + voice_setting : 10, + _reserved_ : 6, +} + +packet WriteVoiceSetting : Command (op_code = WRITE_VOICE_SETTING) { + voice_setting : 10, + _reserved_ : 6, +} + +packet WriteVoiceSettingComplete : CommandComplete (command_op_code = WRITE_VOICE_SETTING) { + status : ErrorCode, +} + +packet ReadAutomaticFlushTimeout : Command (op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + flush_timeout : 16, +} + +packet WriteAutomaticFlushTimeout : Command (op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) { + connection_handle : 12, + _reserved_ : 4, + flush_timeout : 16, // 0x0000-0x07FF Default 0x0000 (No Automatic Flush) +} + +packet WriteAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadNumBroadcastRetransmits : Command (op_code = READ_NUM_BROADCAST_RETRANSMITS) { +} + +packet ReadNumBroadcastRetransmitsComplete : CommandComplete (command_op_code = READ_NUM_BROADCAST_RETRANSMITS) { + status : ErrorCode, + num_broadcast_retransmissions : 8, +} + +packet WriteNumBroadcastRetransmits : Command (op_code = WRITE_NUM_BROADCAST_RETRANSMITS) { + num_broadcast_retransmissions : 8, +} + +packet WriteNumBroadcastRetransmitsComplete : CommandComplete (command_op_code = WRITE_NUM_BROADCAST_RETRANSMITS) { + status : ErrorCode, +} + +enum HoldModeActivity : 8 { + MAINTAIN_CURRENT_POWER_STATE = 0x00, + SUSPEND_PAGE_SCAN = 0x01, + SUSPEND_INQUIRY_SCAN = 0x02, + SUSPEND_PERIODIC_INQUIRY = 0x03, +} + +packet ReadHoldModeActivity : Command (op_code = READ_HOLD_MODE_ACTIVITY) { +} + +packet ReadHoldModeActivityComplete : CommandComplete (command_op_code = READ_HOLD_MODE_ACTIVITY) { + status : ErrorCode, + hold_mode_activity : HoldModeActivity, +} + +packet WriteHoldModeActivity : Command (op_code = WRITE_HOLD_MODE_ACTIVITY) { + hold_mode_activity : HoldModeActivity, +} + +packet WriteHoldModeActivityComplete : CommandComplete (command_op_code = WRITE_HOLD_MODE_ACTIVITY) { + status : ErrorCode, +} + +enum TransmitPowerLevelType : 8 { + CURRENT = 0x00, + MAXIMUM = 0x01, +} + +packet ReadTransmitPowerLevel : Command (op_code = READ_TRANSMIT_POWER_LEVEL) { + connection_handle : 12, + _reserved_ : 4, + transmit_power_level_type : TransmitPowerLevelType, +} + +packet ReadTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_TRANSMIT_POWER_LEVEL) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + transmit_power_level : 8, +} + +packet ReadSynchronousFlowControlEnable : Command (op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) { +} + +packet ReadSynchronousFlowControlEnableComplete : CommandComplete (command_op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) { + status : ErrorCode, + enable : Enable, +} + +packet WriteSynchronousFlowControlEnable : Command (op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) { + enable : Enable, +} + +packet WriteSynchronousFlowControlEnableComplete : CommandComplete (command_op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) { + status : ErrorCode, +} + +packet SetControllerToHostFlowControl : Command (op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) { + acl : 1, + synchronous : 1, + _reserved_ : 6, +} + +packet SetControllerToHostFlowControlComplete : CommandComplete (command_op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) { + status : ErrorCode, +} + +packet HostBufferSize : Command (op_code = HOST_BUFFER_SIZE) { + host_acl_data_packet_length : 16, + host_synchronous_data_packet_length : 8, + host_total_num_acl_data_packets : 16, + host_total_num_synchronous_data_packets : 16, +} + +test HostBufferSize { + "\x33\x0c\x07\x9b\x06\xff\x14\x00\x0a\x00", +} + +packet HostBufferSizeComplete : CommandComplete (command_op_code = HOST_BUFFER_SIZE) { + status : ErrorCode, +} + +test HostBufferSizeComplete { + "\x0e\x04\x01\x33\x0c\x00", +} + +struct CompletedPackets { + connection_handle : 12, + _reserved_ : 4, + host_num_of_completed_packets : 16, +} + +packet HostNumCompletedPackets : Command (op_code = HOST_NUMBER_OF_COMPLETED_PACKETS) { + _count_(completed_packets) : 8, + completed_packets : CompletedPackets[], +} + +packet HostNumCompletedPacketsError : CommandComplete (command_op_code = HOST_NUMBER_OF_COMPLETED_PACKETS) { + error_code : ErrorCode, +} + +packet ReadLinkSupervisionTimeout : Command (op_code = READ_LINK_SUPERVISION_TIMEOUT) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = READ_LINK_SUPERVISION_TIMEOUT) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s) +} + +packet WriteLinkSupervisionTimeout : Command (op_code = WRITE_LINK_SUPERVISION_TIMEOUT) { + connection_handle : 12, + _reserved_ : 4, + link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s) +} + +packet WriteLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = WRITE_LINK_SUPERVISION_TIMEOUT) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadNumberOfSupportedIac : Command (op_code = READ_NUMBER_OF_SUPPORTED_IAC) { +} + +test ReadNumberOfSupportedIac { + "\x38\x0c\x00", +} + +packet ReadNumberOfSupportedIacComplete : CommandComplete (command_op_code = READ_NUMBER_OF_SUPPORTED_IAC) { + status : ErrorCode, + num_support_iac : 8, +} + +test ReadNumberOfSupportedIacComplete { + "\x0e\x05\x01\x38\x0c\x00\x99", +} + +packet ReadCurrentIacLap : Command (op_code = READ_CURRENT_IAC_LAP) { +} + +test ReadCurrentIacLap { + "\x39\x0c\x00", +} + +packet ReadCurrentIacLapComplete : CommandComplete (command_op_code = READ_CURRENT_IAC_LAP) { + status : ErrorCode, + _count_(laps_to_read) : 8, + laps_to_read : Lap[], +} + +test ReadCurrentIacLapComplete { + "\x0e\x0b\x01\x39\x0c\x00\x02\x11\x8b\x9e\x22\x8b\x9e", +} + +packet WriteCurrentIacLap : Command (op_code = WRITE_CURRENT_IAC_LAP) { + _count_(laps_to_write) : 8, + laps_to_write : Lap[], +} + +test WriteCurrentIacLap { + "\x3a\x0c\x07\x02\x11\x8b\x9e\x22\x8b\x9e", +} + +packet WriteCurrentIacLapComplete : CommandComplete (command_op_code = WRITE_CURRENT_IAC_LAP) { + status : ErrorCode, +} + +test WriteCurrentIacLapComplete { + "\x0e\x04\x01\x3a\x0c\x00", +} + +packet SetAfhHostChannelClassification : Command (op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) { + afh_host_channel_classification : 8[10], +} + +packet SetAfhHostChannelClassificationComplete : CommandComplete (command_op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) { + status : ErrorCode, +} + +enum InquiryScanType : 8 { + STANDARD = 0x00, + INTERLACED = 0x01, +} + +packet ReadInquiryScanType : Command (op_code = READ_INQUIRY_SCAN_TYPE) { +} + +packet ReadInquiryScanTypeComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_TYPE) { + status : ErrorCode, + inquiry_scan_type : InquiryScanType, +} + +packet WriteInquiryScanType : Command (op_code = WRITE_INQUIRY_SCAN_TYPE) { + inquiry_scan_type : InquiryScanType, +} + +packet WriteInquiryScanTypeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_TYPE) { + status : ErrorCode, +} + +enum InquiryMode : 8 { + STANDARD = 0x00, + RSSI = 0x01, + RSSI_OR_EXTENDED = 0x02, +} + +packet ReadInquiryMode : Command (op_code = READ_INQUIRY_MODE) { +} + +packet ReadInquiryModeComplete : CommandComplete (command_op_code = READ_INQUIRY_MODE) { + status : ErrorCode, + inquiry_mode : InquiryMode, +} + +packet WriteInquiryMode : Command (op_code = WRITE_INQUIRY_MODE) { + inquiry_mode : InquiryMode, +} + +packet WriteInquiryModeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_MODE) { + status : ErrorCode, +} + +enum PageScanType : 8 { + STANDARD = 0x00, + INTERLACED = 0x01, +} + +packet ReadPageScanType : Command (op_code = READ_PAGE_SCAN_TYPE) { +} + +packet ReadPageScanTypeComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_TYPE) { + status : ErrorCode, + page_scan_type : PageScanType, +} + +packet WritePageScanType : Command (op_code = WRITE_PAGE_SCAN_TYPE) { + page_scan_type : PageScanType, +} + +packet WritePageScanTypeComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_TYPE) { + status : ErrorCode, +} + +packet ReadAfhChannelAssessmentMode : Command (op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) { +} + +packet ReadAfhChannelAssessmentModeComplete : CommandComplete (command_op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) { + status : ErrorCode, + controller_channel_assessment : Enable, +} + +packet WriteAfhChannelAssessmentMode : Command (op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) { + controller_channel_assessment : Enable, +} + +packet WriteAfhChannelAssessmentModeComplete : CommandComplete (command_op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) { + status : ErrorCode, +} + +enum FecRequired : 8 { + NOT_REQUIRED = 0x00, + REQUIRED = 0x01, +} + +packet ReadExtendedInquiryResponse : Command (op_code = READ_EXTENDED_INQUIRY_RESPONSE) { +} + +packet ReadExtendedInquiryResponseComplete : CommandComplete (command_op_code = READ_EXTENDED_INQUIRY_RESPONSE) { + status : ErrorCode, + fec_required : FecRequired, + extended_inquiry_response : 8[240], +} + +packet WriteExtendedInquiryResponse : Command (op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) { + fec_required : FecRequired, + extended_inquiry_response : 8[240], +} + +packet WriteExtendedInquiryResponseComplete : CommandComplete (command_op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) { + status : ErrorCode, +} + +packet RefreshEncryptionKey : Command (op_code = REFRESH_ENCRYPTION_KEY) { + connection_handle : 12, + _reserved_ : 4, +} + +packet RefreshEncryptionKeyStatus : CommandStatus (command_op_code = REFRESH_ENCRYPTION_KEY) { +} + +packet ReadSimplePairingMode : Command (op_code = READ_SIMPLE_PAIRING_MODE) { +} + +packet ReadSimplePairingModeComplete : CommandComplete (command_op_code = READ_SIMPLE_PAIRING_MODE) { + status : ErrorCode, + simple_pairing_mode : Enable, +} + +packet WriteSimplePairingMode : Command (op_code = WRITE_SIMPLE_PAIRING_MODE) { + simple_pairing_mode : Enable, +} + +test WriteSimplePairingMode { + "\x56\x0c\x01\x01", +} + +packet WriteSimplePairingModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_MODE) { + status : ErrorCode, +} + +test WriteSimplePairingModeComplete { + "\x0e\x04\x01\x56\x0c\x00", +} + +packet ReadLocalOobData : Command (op_code = READ_LOCAL_OOB_DATA) { +} + +packet ReadLocalOobDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_DATA) { + status : ErrorCode, + c : 8[16], + r : 8[16], +} + +packet ReadInquiryResponseTransmitPowerLevel : Command (op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) { +} + +packet ReadInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) { + status : ErrorCode, + tx_power : 8, // (-70dBm to 20dBm) +} + +packet WriteInquiryTransmitPowerLevel : Command (op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) { + tx_power : 8, +} + +packet WriteInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) { + status : ErrorCode, +} + +enum KeypressNotificationType : 8 { + ENTRY_STARTED = 0, + DIGIT_ENTERED = 1, + DIGIT_ERASED = 2, + CLEARED = 3, + ENTRY_COMPLETED = 4, +} + +packet SendKeypressNotification : Command (op_code = SEND_KEYPRESS_NOTIFICATION) { + bd_addr : Address, + notification_type : KeypressNotificationType, +} + +packet SendKeypressNotificationComplete : CommandComplete (command_op_code = SEND_KEYPRESS_NOTIFICATION) { + status : ErrorCode, + bd_addr : Address, +} + +packet SetEventMaskPage2 : Command (op_code = SET_EVENT_MASK_PAGE_2) { + event_mask_page_2: 64, +} + +packet SetEventMaskPage2Complete : CommandComplete (command_op_code = SET_EVENT_MASK_PAGE_2) { + status: ErrorCode, +} + +packet ReadEnhancedTransmitPowerLevel : Command (op_code = READ_ENHANCED_TRANSMIT_POWER_LEVEL) { + connection_handle : 12, + _reserved_ : 4, + transmit_power_level_type : TransmitPowerLevelType, +} + +packet ReadEnhancedTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_ENHANCED_TRANSMIT_POWER_LEVEL) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + tx_power_level_gfsk : 8, + tx_power_level_dqpsk : 8, + tx_power_level_8dpsk : 8, +} + +packet ReadLeHostSupport : Command (op_code = READ_LE_HOST_SUPPORT) { +} + +packet ReadLeHostSupportComplete : CommandComplete (command_op_code = READ_LE_HOST_SUPPORT) { + status : ErrorCode, + le_supported_host : Enable, + _reserved_ : 8, // simultaneous_le_host reserved since 4.1 +} + +packet WriteLeHostSupport : Command (op_code = WRITE_LE_HOST_SUPPORT) { + le_supported_host : Enable, + simultaneous_le_host : Enable, // According to the spec, this should be 0x00 since 4.1 +} + +test WriteLeHostSupport { + "\x6d\x0c\x02\x01\x01", +} + +packet WriteLeHostSupportComplete : CommandComplete (command_op_code = WRITE_LE_HOST_SUPPORT) { + status : ErrorCode, +} + +test WriteLeHostSupportComplete { + "\x0e\x04\x01\x6d\x0c\x00", +} + +packet ReadSecureConnectionsHostSupport : Command (op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) { +} + +packet ReadSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) { + status : ErrorCode, + secure_connections_host_support : Enable, +} + +packet WriteSecureConnectionsHostSupport : Command (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) { + secure_connections_host_support : Enable, +} + +test WriteSecureConnectionsHostSupport { + "\x7a\x0c\x01\x01", +} + +packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) { + status : ErrorCode, +} + +test WriteSecureConnectionsHostSupportComplete { + "\x0e\x04\x01\x7a\x0c\x00", +} + +packet ReadLocalOobExtendedData : Command (op_code = READ_LOCAL_OOB_EXTENDED_DATA) { +} + +packet ReadLocalOobExtendedDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_EXTENDED_DATA) { + status : ErrorCode, + c_192 : 8[16], + r_192 : 8[16], + c_256 : 8[16], + r_256 : 8[16], +} + +packet SetEcosystemBaseInterval : Command (op_code = SET_ECOSYSTEM_BASE_INTERVAL) { + interval : 16, +} + +packet SetEcosystemBaseIntervalComplete : CommandComplete (command_op_code = SET_ECOSYSTEM_BASE_INTERVAL) { + status : ErrorCode, +} + +enum DataPathDirection : 8 { + INPUT = 0, + OUTPUT = 1, +} + +packet ConfigureDataPath : Command (op_code = CONFIGURE_DATA_PATH) { + data_path_direction : DataPathDirection, + data_path_id : 8, + _size_(vendor_specific_config) : 8, + vendor_specific_config : 8[], +} + +packet ConfigureDataPathComplete : CommandComplete (command_op_code = CONFIGURE_DATA_PATH) { + status : ErrorCode, +} + +packet SetMinEncryptionKeySize : Command (op_code = SET_MIN_ENCRYPTION_KEY_SIZE) { + min_encryption_key_size : 8, +} + +packet SetMinEncryptionKeySizeComplete : CommandComplete (command_op_code = SET_MIN_ENCRYPTION_KEY_SIZE) { + status : ErrorCode, +} + + + // INFORMATIONAL_PARAMETERS +packet ReadLocalVersionInformation : Command (op_code = READ_LOCAL_VERSION_INFORMATION) { +} + +test ReadLocalVersionInformation { + "\x01\x10\x00", +} + +enum HciVersion : 8 { + V_1_0B = 0x00, + V_1_1 = 0x01, + V_1_2 = 0x02, + V_2_0 = 0x03, // + EDR + V_2_1 = 0x04, // + EDR + V_3_0 = 0x05, // + HS + V_4_0 = 0x06, + V_4_1 = 0x07, + V_4_2 = 0x08, + V_5_0 = 0x09, + V_5_1 = 0x0a, + V_5_2 = 0x0b, + V_5_3 = 0x0c, + V_5_4 = 0x0d, +} + +enum LmpVersion : 8 { + V_1_0B = 0x00, // withdrawn + V_1_1 = 0x01, // withdrawn + V_1_2 = 0x02, // withdrawn + V_2_0 = 0x03, // + EDR + V_2_1 = 0x04, // + EDR + V_3_0 = 0x05, // + HS + V_4_0 = 0x06, + V_4_1 = 0x07, + V_4_2 = 0x08, + V_5_0 = 0x09, + V_5_1 = 0x0a, + V_5_2 = 0x0b, + V_5_3 = 0x0c, + V_5_4 = 0x0d, +} + +struct LocalVersionInformation { + hci_version : HciVersion, + hci_revision : 16, + lmp_version : LmpVersion, + manufacturer_name : 16, + lmp_subversion : 16, +} + +packet ReadLocalVersionInformationComplete : CommandComplete (command_op_code = READ_LOCAL_VERSION_INFORMATION) { + status : ErrorCode, + local_version_information : LocalVersionInformation, +} + +test ReadLocalVersionInformationComplete { + "\x0e\x0c\x01\x01\x10\x00\x09\x00\x00\x09\x1d\x00\xbe\x02", +} + +packet ReadLocalSupportedCommands : Command (op_code = READ_LOCAL_SUPPORTED_COMMANDS) { +} + +test ReadLocalSupportedCommands { + "\x02\x10\x00", +} + +packet ReadLocalSupportedCommandsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_COMMANDS) { + status : ErrorCode, + supported_commands : 8[64], +} + +test ReadLocalSupportedCommandsComplete { + "\x0e\x44\x01\x02\x10\x00\xff\xff\xff\x03\xce\xff\xef\xff\xff\xff\xff\x7f\xf2\x0f\xe8\xfe\x3f\xf7\x83\xff\x1c\x00\x00\x00\x61\xff\xff\xff\x7f\xbe\x20\xf5\xff\xf0\xff\xff\xff\xff\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", +} + +packet ReadLocalSupportedFeatures : Command (op_code = READ_LOCAL_SUPPORTED_FEATURES) { +} + +packet ReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_FEATURES) { + status : ErrorCode, + lmp_features : 64, +} + +packet ReadLocalExtendedFeatures : Command (op_code = READ_LOCAL_EXTENDED_FEATURES) { + page_number : 8, +} + +test ReadLocalExtendedFeatures { + "\x04\x10\x01\x00", + "\x04\x10\x01\x01", + "\x04\x10\x01\x02", +} + +enum LMPFeaturesPage0Bits: 64 { + // Byte 0 + LMP_3_SLOT_PACKETS = 0x01, + LMP_5_SLOT_PACKETS = 0x02, + ENCRYPTION = 0x04, + SLOT_OFFSET = 0x08, + TIMING_ACCURACY = 0x10, + ROLE_SWITCH = 0x20, + HOLD_MODE = 0x40, + SNIFF_MODE = 0x80, + + // Byte 1 + POWER_CONTROL_REQUESTS = 0x0200, + CHANNEL_QUALITY_DRIVEN_DATA_RATE = 0x0400, + SCO_LINK = 0x0800, + HV2_PACKETS = 0x1000, + HV3_PACKETS = 0x2000, + M_LAW_LOG_SYNCHRONOUS_DATA = 0x4000, + A_LAW_LOG_SYNCHRONOUS_DATA = 0x8000, + + // Byte 2 + CVSD_SYNCHRONOUS_DATA = 0x010000, + PAGING_PARAMETER_NEGOTIATION = 0x020000, + POWER_CONTROL = 0x040000, + TRANSPARENT_SYNCHRONOUS_DATA = 0x080000, + FLOW_CONTROL_LAG_LEAST_SIGNIFICANT_BIT = 0x100000, + FLOW_CONTROL_LAG_MIDDLE_BIT = 0x200000, + FLOW_CONTROL_LAG_MOST_SIGNIFICANT_BIT = 0x400000, + BROADCAST_ENCRYPTION = 0x800000, + + // Byte 3 + ENHANCED_DATA_RATE_ACL_2_MB_S_MODE = 0x02000000, + ENHANCED_DATA_RATE_ACL_3_MB_S_MODE = 0x04000000, + ENHANCED_INQUIRY_SCAN = 0x08000000, + INTERLACED_INQUIRY_SCAN = 0x10000000, + INTERLACED_PAGE_SCAN = 0x20000000, + RSSI_WITH_INQUIRY_RESULTS = 0x40000000, + EXTENDED_SCO_LINK = 0x80000000, + + // Byte 4 + EV4_PACKETS = 0x0100000000, + EV5_PACKETS = 0x0200000000, + AFH_CAPABLE_PERIPHERAL = 0x0800000000, + AFH_CLASSIFICATION_PERIPHERAL = 0x1000000000, + BR_EDR_NOT_SUPPORTED = 0x2000000000, + LE_SUPPORTED_CONTROLLER = 0x4000000000, + LMP_3_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS = 0x8000000000, + + // Byte 5 + LMP_5_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS = 0x010000000000, + SNIFF_SUBRATING = 0x020000000000, + PAUSE_ENCRYPTION = 0x040000000000, + AFH_CAPABLE_CENTRAL = 0x080000000000, + AFH_CLASSIFICATION_CENTRAL = 0x100000000000, + ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE = 0x200000000000, + ENHANCED_DATA_RATE_ESCO_3_MB_S_MODE = 0x400000000000, + LMP_3_SLOT_ENHANCED_DATA_RATE_ESCO_PACKETS = 0x800000000000, + + // Byte 6 + EXTENDED_INQUIRY_RESPONSE = 0x01000000000000, + SIMULTANEOUS_LE_AND_BR_CONTROLLER = 0x02000000000000, + SECURE_SIMPLE_PAIRING_CONTROLLER = 0x08000000000000, + ENCAPSULATED_PDU = 0x10000000000000, + ERRONEOUS_DATA_REPORTING = 0x20000000000000, + NON_FLUSHABLE_PACKET_BOUNDARY_FLAG = 0x40000000000000, + + // Byte 7 + HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT = 0x0100000000000000, + VARIABLE_INQUIRY_TX_POWER_LEVEL = 0x0200000000000000, + ENHANCED_POWER_CONTROL = 0x0400000000000000, + EXTENDED_FEATURES = 0x8000000000000000, +} + +enum LMPFeaturesPage1Bits: 64 { + // Byte 0 + SECURE_SIMPLE_PAIRING_HOST_SUPPORT = 0x01, + LE_SUPPORTED_HOST = 0x02, + SIMULTANEOUS_LE_AND_BR_HOST = 0x04, + SECURE_CONNECTIONS_HOST_SUPPORT = 0x08, +} + +enum LMPFeaturesPage2Bits: 64 { + // Byte 0 + CONNECTIONLESS_PERIPHERAL_BROADCAST_TRANSMITTER_OPERATION = 0x01, + CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVER_OPERATION = 0x02, + SYNCHRONIZATION_TRAIN = 0x04, + SYNCHRONIZATION_SCAN = 0x08, + HCI_INQUIRY_RESPONSE_NOTIFICATION_EVENT = 0x10, + GENERALIZED_INTERLACED_SCAN = 0x20, + COARSE_CLOCK_ADJUSTMENT = 0x40, + + // Byte 1 + SECURE_CONNECTIONS_CONTROLLER_SUPPORT = 0x0100, + PING = 0x0200, + SLOT_AVAILABILITY_MASK = 0x0400, + TRAIN_NUDGING = 0x0800, +} + +packet ReadLocalExtendedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_EXTENDED_FEATURES) { + status : ErrorCode, + page_number : 8, + maximum_page_number : 8, + extended_lmp_features : 64, +} + +test ReadLocalExtendedFeaturesComplete { + "\x0e\x0e\x01\x04\x10\x00\x00\x02\xff\xfe\x8f\xfe\xd8\x3f\x5b\x87", + "\x0e\x0e\x01\x04\x10\x00\x01\x02\x07\x00\x00\x00\x00\x00\x00\x00", + "\x0e\x0e\x01\x04\x10\x00\x02\x02\x45\x03\x00\x00\x00\x00\x00\x00", +} + +packet ReadBufferSize : Command (op_code = READ_BUFFER_SIZE) { +} + +test ReadBufferSize { + "\x05\x10\x00", +} + +packet ReadBufferSizeComplete : CommandComplete (command_op_code = READ_BUFFER_SIZE) { + status : ErrorCode, + acl_data_packet_length : 16, + synchronous_data_packet_length : 8, + total_num_acl_data_packets : 16, + total_num_synchronous_data_packets : 16, +} + +test ReadBufferSizeComplete { + "\x0e\x0b\x01\x05\x10\x00\x00\x04\x3c\x07\x00\x08\x00", +} + +packet ReadBdAddr : Command (op_code = READ_BD_ADDR) { +} + +test ReadBdAddr { + "\x09\x10\x00", +} + +packet ReadBdAddrComplete : CommandComplete (command_op_code = READ_BD_ADDR) { + status : ErrorCode, + bd_addr : Address, +} + +test ReadBdAddrComplete { + "\x0e\x0a\x01\x09\x10\x00\x14\x8e\x61\x5f\x36\x88", +} + +packet ReadDataBlockSize : Command (op_code = READ_DATA_BLOCK_SIZE) { +} + +packet ReadDataBlockSizeComplete : CommandComplete (command_op_code = READ_DATA_BLOCK_SIZE) { + status : ErrorCode, + max_acl_data_packet_length : 16, + data_block_length : 16, + total_num_data_blocks : 16, +} + +packet ReadLocalSupportedCodecsV1 : Command (op_code = READ_LOCAL_SUPPORTED_CODECS_V1) { +} + +packet ReadLocalSupportedCodecsV1Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V1) { + status : ErrorCode, + _count_(supported_codecs) : 8, + supported_codecs : 8[], + _count_(vendor_specific_codecs) : 8, + vendor_specific_codecs : 32[], +} + +packet ReadLocalSupportedCodecsV2 : Command (op_code = READ_LOCAL_SUPPORTED_CODECS_V2) { +} + +group CodecTransport { + br_edr : 1, + br_edr_sco_and_esco : 1, + le_cis : 1, + le_bis : 1, + _reserved_ : 4, +} + +struct CodecConfiguration { + codec_id : 8, + CodecTransport, +} + +struct VendorCodecConfiguration { + company_id : 16, + codec_vendor_id : 16, + CodecTransport, +} + +packet ReadLocalSupportedCodecsV2Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V2) { + status : ErrorCode, + _count_(supported_codecs) : 8, + supported_codecs : CodecConfiguration[], + _count_(vendor_specific_codecs) : 8, + vendor_specific_codecs : VendorCodecConfiguration[], +} + +packet ReadLocalSupportedCodecCapabilities : Command (op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) { + codec_id : 8, + company_id : 16, + codec_vendor_id : 16, + CodecTransport, + direction : DataPathDirection, +} + +struct CodecCapability { + _size_(capability) : 8, + capability : 8[], +} + +packet ReadLocalSupportedCodecCapabilitiesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) { + status : ErrorCode, + _count_(codec_capabilities) : 8, + codec_capabilities : CodecCapability[], +} + +packet ReadLocalSupportedControllerDelay : Command (op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) { + codec_id : 8, + company_id : 16, + codec_vendor_id : 16, + CodecTransport, + direction : DataPathDirection, + _size_(codec_configuration) : 8, + codec_configuration : 8[], +} + +packet ReadLocalSupportedControllerDelayComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) { + status : ErrorCode, + min_controller_delay : 24, + max_controller_delay : 24, +} + + + // STATUS_PARAMETERS +packet ReadFailedContactCounter : Command (op_code = READ_FAILED_CONTACT_COUNTER) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadFailedContactCounterComplete : CommandComplete (command_op_code = READ_FAILED_CONTACT_COUNTER) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + failed_contact_counter : 16, +} + +packet ResetFailedContactCounter : Command (op_code = RESET_FAILED_CONTACT_COUNTER) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ResetFailedContactCounterComplete : CommandComplete (command_op_code = RESET_FAILED_CONTACT_COUNTER) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadLinkQuality : Command (op_code = READ_LINK_QUALITY) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadLinkQualityComplete : CommandComplete (command_op_code = READ_LINK_QUALITY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + link_quality : 8, +} + +packet ReadRssi : Command (op_code = READ_RSSI) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadRssiComplete : CommandComplete (command_op_code = READ_RSSI) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + rssi : 8, +} + +packet ReadAfhChannelMap : Command (op_code = READ_AFH_CHANNEL_MAP) { + connection_handle : 12, + _reserved_ : 4, +} + +enum AfhMode : 8 { + AFH_DISABLED = 0x00, + AFH_ENABLED = 0x01, +} + +packet ReadAfhChannelMapComplete : CommandComplete (command_op_code = READ_AFH_CHANNEL_MAP) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + afh_mode : AfhMode, + afh_channel_map : 8[10], +} + + +enum WhichClock : 8 { + LOCAL = 0x00, + PICONET = 0x01, +} + +packet ReadClock : Command (op_code = READ_CLOCK) { + connection_handle : 12, + _reserved_ : 4, + which_clock : WhichClock, +} + +packet ReadClockComplete : CommandComplete (command_op_code = READ_CLOCK) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + clock : 28, + _reserved_ : 4, + accuracy : 16, +} + +packet ReadEncryptionKeySize : Command (op_code = READ_ENCRYPTION_KEY_SIZE) { + connection_handle : 12, + _reserved_ : 4, +} + +packet ReadEncryptionKeySizeComplete : CommandComplete (command_op_code = READ_ENCRYPTION_KEY_SIZE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + key_size : 8, +} + + // TESTING +enum LoopbackMode : 8 { + NO_LOOPBACK = 0x00, + ENABLE_LOCAL = 0x01, + ENABLE_REMOTE = 0x02, +} + +packet ReadLoopbackMode : Command (op_code = READ_LOOPBACK_MODE) { +} + +packet ReadLoopbackModeComplete : CommandComplete (command_op_code = READ_LOOPBACK_MODE) { + status : ErrorCode, + loopback_mode : LoopbackMode, +} + +packet WriteLoopbackMode : Command (op_code = WRITE_LOOPBACK_MODE) { + loopback_mode : LoopbackMode, +} + +packet WriteLoopbackModeComplete : CommandComplete (command_op_code = WRITE_LOOPBACK_MODE) { + status : ErrorCode, +} + +packet EnableDeviceUnderTestMode : Command (op_code = ENABLE_DEVICE_UNDER_TEST_MODE) { +} + +packet EnableDeviceUnderTestModeComplete : CommandComplete (command_op_code = ENABLE_DEVICE_UNDER_TEST_MODE) { + status : ErrorCode, +} + +packet WriteSimplePairingDebugMode : Command (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) { + simple_pairing_debug_mode : Enable, +} + +packet WriteSimplePairingDebugModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) { + status : ErrorCode, +} + +packet WriteSecureConnectionsTestMode : Command (op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) { + connection_handle : 12, + _reserved_ : 4, + dm1_aclu_mode : Enable, + esco_loopback_mode : Enable, +} + +packet WriteSecureConnectionsTestModeComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) { + status : ErrorCode, +} + + // LE_CONTROLLER +packet LeSetEventMask : Command (op_code = LE_SET_EVENT_MASK) { + le_event_mask : 64, +} + +packet LeSetEventMaskComplete : CommandComplete (command_op_code = LE_SET_EVENT_MASK) { + status : ErrorCode, +} + +packet LeReadBufferSizeV1 : Command (op_code = LE_READ_BUFFER_SIZE_V1) { +} + +struct LeBufferSize { + le_data_packet_length : 16, + total_num_le_packets : 8, +} + +test LeReadBufferSizeV1 { + "\x02\x20\x00", +} + +packet LeReadBufferSizeV1Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V1) { + status : ErrorCode, + le_buffer_size : LeBufferSize, +} + +test LeReadBufferSizeV1Complete { + "\x0e\x07\x01\x02\x20\x00\xfb\x00\x10", +} + +enum LLFeaturesBits : 64 { + // Byte 0 + LE_ENCRYPTION = 0x0000000000000001, + CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 0x0000000000000002, + EXTENDED_REJECT_INDICATION = 0x0000000000000004, + PERIPHERAL_INITIATED_FEATURES_EXCHANGE = 0x0000000000000008, + LE_PING = 0x0000000000000010, + LE_DATA_PACKET_LENGTH_EXTENSION = 0x0000000000000020, + LL_PRIVACY = 0x0000000000000040, + EXTENDED_SCANNER_FILTER_POLICIES = 0x0000000000000080, + + // Byte 1 + LE_2M_PHY = 0x0000000000000100, + STABLE_MODULATION_INDEX_TRANSMITTER = 0x0000000000000200, + STABLE_MODULATION_INDEX_RECEIVER = 0x0000000000000400, + LE_CODED_PHY = 0x0000000000000800, + LE_EXTENDED_ADVERTISING = 0x0000000000001000, + LE_PERIODIC_ADVERTISING = 0x0000000000002000, + CHANNEL_SELECTION_ALGORITHM_2 = 0x0000000000004000, + LE_POWER_CLASS_1 = 0x0000000000008000, + + // Byte 2 + MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE = 0x0000000000010000, + CONNECTION_CTE_REQUEST = 0x0000000000020000, + CONNECTION_CTE_RESPONSE = 0x0000000000040000, + CONNECTIONLESS_CTE_TRANSMITTER = 0x0000000000080000, + CONNECTIONLESS_CTE_RECEIVER = 0x0000000000100000, + ANTENNA_SWITCHING_DURING_CTE_TRANSMISSION = 0x0000000000200000, + ANTENNA_SWITCHING_DURING_CTE_RECEPTION = 0x0000000000400000, + RECEIVING_CONSTANT_TONE_EXTENSIONS = 0x0000000000800000, + + // Byte 3 + PERIODIC_ADVERTISING_SYNC_TRANSFER_SENDER = 0x0000000001000000, + PERIODIC_ADVERTISING_SYNC_TRANSFER_RECIPIENT = 0x0000000002000000, + SLEEP_CLOCK_ACCURACY_UPDATES = 0x0000000004000000, + REMOTE_PUBLIC_KEY_VALIDATION = 0x0000000008000000, + CONNECTED_ISOCHRONOUS_STREAM_CENTRAL = 0x0000000010000000, + CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL = 0x0000000020000000, + ISOCHRONOUS_BROADCASTER = 0x0000000040000000, + SYNCHRONIZED_RECEIVER = 0x0000000080000000, + + // Byte 4 + CONNECTED_ISOCHRONOUS_STREAM_HOST_SUPPORT = 0x0000000100000000, + LE_POWER_CONTROL_REQUEST = 0x0000000200000000, + LE_POWER_CONTROL_REQUEST_BIS = 0x0000000400000000, + LE_PATH_LOSS_MONITORING = 0x0000000800000000, + PERIODIC_ADVERTISING_ADI_SUPPORT = 0x0000001000000000, + CONNECTION_SUBRATING = 0x0000002000000000, + CONNECTION_SUBRATING_HOST_SUPPORT = 0x0000004000000000, + CHANNEL_CLASSIFICATION = 0x0000008000000000, +} + +packet LeReadLocalSupportedFeatures : Command (op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) { +} + +packet LeReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) { + status : ErrorCode, + le_features : 64, +} + +packet LeSetRandomAddress : Command (op_code = LE_SET_RANDOM_ADDRESS) { + random_address : Address, +} + +packet LeSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_RANDOM_ADDRESS) { + status : ErrorCode, +} + +enum AdvertisingFilterPolicy : 2 { + ALL_DEVICES = 0, // Default + LISTED_SCAN = 1, + LISTED_CONNECT = 2, + LISTED_SCAN_AND_CONNECT = 3, +} + +enum PeerAddressType : 8 { + PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00, + RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01, +} + +enum AdvertisingType : 8 { + ADV_IND = 0x00, + ADV_DIRECT_IND_HIGH = 0x01, + ADV_SCAN_IND = 0x02, + ADV_NONCONN_IND = 0x03, + ADV_DIRECT_IND_LOW = 0x04, +} + +enum AddressType : 8 { + PUBLIC_DEVICE_ADDRESS = 0x00, + RANDOM_DEVICE_ADDRESS = 0x01, + PUBLIC_IDENTITY_ADDRESS = 0x02, + RANDOM_IDENTITY_ADDRESS = 0x03, +} + +enum OwnAddressType : 8 { + PUBLIC_DEVICE_ADDRESS = 0x00, + RANDOM_DEVICE_ADDRESS = 0x01, + RESOLVABLE_OR_PUBLIC_ADDRESS = 0x02, + RESOLVABLE_OR_RANDOM_ADDRESS = 0x03, +} + +packet LeSetAdvertisingParameters : Command (op_code = LE_SET_ADVERTISING_PARAMETERS) { + advertising_interval_min : 16, + advertising_interval_max : 16, + advertising_type : AdvertisingType, + own_address_type : OwnAddressType, + peer_address_type : PeerAddressType, + peer_address : Address, + advertising_channel_map : 8, + advertising_filter_policy : AdvertisingFilterPolicy, + _reserved_ : 6, +} + +packet LeSetAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_PARAMETERS) { + status : ErrorCode, +} + +packet LeReadAdvertisingPhysicalChannelTxPower : Command (op_code = LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER) { +} + +packet LeReadAdvertisingPhysicalChannelTxPowerComplete : CommandComplete (command_op_code = LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER) { + status : ErrorCode, + transmit_power_level : 8, // (-127dBm to 20dBm) Accuracy: +/-4dB +} + +packet LeSetAdvertisingData : Command (op_code = LE_SET_ADVERTISING_DATA) { + _size_(advertising_data) : 8, + advertising_data : 8[], + _padding_[31], // Zero padding to 31 bytes of advertising_data +} + +packet LeSetAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_DATA) { + status : ErrorCode, +} + +packet LeSetScanResponseData : Command (op_code = LE_SET_SCAN_RESPONSE_DATA) { + _size_(advertising_data) : 8, + advertising_data : 8[], + _padding_[31], // Zero padding to 31 bytes of advertising_data +} + +packet LeSetScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_SCAN_RESPONSE_DATA) { + status : ErrorCode, +} + +packet LeSetAdvertisingEnable : Command (op_code = LE_SET_ADVERTISING_ENABLE) { + advertising_enable : Enable, // Default DISABLED +} + +packet LeSetAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_ENABLE) { + status : ErrorCode, +} + +enum LeScanType : 8 { + PASSIVE = 0x00, // Default + ACTIVE = 0x01, +} + +enum LeScanningFilterPolicy : 8 { + ACCEPT_ALL = 0x00, // Default + FILTER_ACCEPT_LIST_ONLY = 0x01, + CHECK_INITIATORS_IDENTITY = 0x02, + FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY = 0x03, +} + +packet LeSetScanParameters : Command (op_code = LE_SET_SCAN_PARAMETERS) { + le_scan_type : LeScanType, + le_scan_interval : 16, // 0x0004-0x4000 Default 0x10 (10ms) + le_scan_window : 16, // Default 0x10 (10ms) + own_address_type : OwnAddressType, + scanning_filter_policy : LeScanningFilterPolicy, +} + +test LeSetScanParameters { + "\x0b\x20\x07\x01\x12\x00\x12\x00\x01\x00", +} + +packet LeSetScanParametersComplete : CommandComplete (command_op_code = LE_SET_SCAN_PARAMETERS) { + status : ErrorCode, +} + +packet LeSetScanEnable : Command (op_code = LE_SET_SCAN_ENABLE) { + le_scan_enable : Enable, + filter_duplicates : Enable, +} + +test LeSetScanEnable { + "\x0c\x20\x02\x01\x00", +} + +packet LeSetScanEnableComplete : CommandComplete (command_op_code = LE_SET_SCAN_ENABLE) { + status : ErrorCode, +} + +enum InitiatorFilterPolicy : 8 { + USE_PEER_ADDRESS = 0x00, + USE_FILTER_ACCEPT_LIST = 0x01, +} + +packet LeCreateConnection : Command (op_code = LE_CREATE_CONNECTION) { + le_scan_interval : 16, // 0x0004-0x4000 + le_scan_window : 16, // < = LeScanInterval + initiator_filter_policy : InitiatorFilterPolicy, + peer_address_type : AddressType, + peer_address : Address, + own_address_type : OwnAddressType, + connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s) + connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s) + max_latency : 16, // 0x0006-0x01F3 + supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s) + min_ce_length : 16, // 0.625ms + max_ce_length : 16, // 0.625ms +} + +packet LeCreateConnectionStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION) { +} + +packet LeCreateConnectionCancel : Command (op_code = LE_CREATE_CONNECTION_CANCEL) { +} + +packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) { + status : ErrorCode, +} + +packet LeReadFilterAcceptListSize : Command (op_code = LE_READ_FILTER_ACCEPT_LIST_SIZE) { +} + +test LeReadFilterAcceptListSize { + "\x0f\x20\x00", +} + +packet LeReadFilterAcceptListSizeComplete : CommandComplete (command_op_code = LE_READ_FILTER_ACCEPT_LIST_SIZE) { + status : ErrorCode, + filter_accept_list_size : 8, +} + +test LeReadFilterAcceptListSizeComplete { + "\x0e\x05\x01\x0f\x20\x00\x80", +} + +packet LeClearFilterAcceptList : Command (op_code = LE_CLEAR_FILTER_ACCEPT_LIST) { +} + +packet LeClearFilterAcceptListComplete : CommandComplete (command_op_code = LE_CLEAR_FILTER_ACCEPT_LIST) { + status : ErrorCode, +} + +enum FilterAcceptListAddressType : 8 { + PUBLIC = 0x00, + RANDOM = 0x01, + ANONYMOUS_ADVERTISERS = 0xFF, +} + +packet LeAddDeviceToFilterAcceptList : Command (op_code = LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST) { + address_type : FilterAcceptListAddressType, + address : Address, +} + +packet LeAddDeviceToFilterAcceptListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST) { + status : ErrorCode, +} + +packet LeRemoveDeviceFromFilterAcceptList : Command (op_code = LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST) { + address_type : FilterAcceptListAddressType, + address : Address, +} + +packet LeRemoveDeviceFromFilterAcceptListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST) { + status : ErrorCode, +} + +packet LeConnectionUpdate : Command (op_code = LE_CONNECTION_UPDATE) { + connection_handle : 12, + _reserved_ : 4, + connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s) + connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s) + max_latency : 16, // 0x0006-0x01F3 + supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s) + min_ce_length : 16, // 0.625ms + max_ce_length : 16, // 0.625ms +} + +packet LeConnectionUpdateStatus : CommandStatus (command_op_code = LE_CONNECTION_UPDATE) { +} + +packet LeSetHostChannelClassification : Command (op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) { + channel_map : 8[5], +} + +packet LeSetHostChannelClassificationComplete : CommandComplete (command_op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) { + status : ErrorCode, +} + +packet LeReadChannelMap : Command (op_code = LE_READ_CHANNEL_MAP) { +} + +packet LeReadChannelMapComplete : CommandComplete (command_op_code = LE_READ_CHANNEL_MAP) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + channel_map : 8[5], +} + +packet LeReadRemoteFeatures : Command (op_code = LE_READ_REMOTE_FEATURES) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeReadRemoteFeaturesStatus : CommandStatus (command_op_code = LE_READ_REMOTE_FEATURES) { +} + +packet LeEncrypt : Command (op_code = LE_ENCRYPT) { + key : 8[16], + plaintext_data : 8[16], +} + +packet LeEncryptComplete : CommandComplete (command_op_code = LE_ENCRYPT) { + status : ErrorCode, + encrypted_data : 8[16], +} + +packet LeRand : Command (op_code = LE_RAND) { +} + +packet LeRandComplete : CommandComplete (command_op_code = LE_RAND) { + status : ErrorCode, + random_number : 64, +} + +packet LeStartEncryption : Command (op_code = LE_START_ENCRYPTION) { + connection_handle: 16, + rand: 8[8], + ediv: 16, + ltk: 8[16], +} + +packet LeStartEncryptionStatus : CommandStatus (command_op_code = LE_START_ENCRYPTION) { +} + +packet LeLongTermKeyRequestReply : Command (op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) { + connection_handle: 16, + long_term_key: 8[16], +} + +packet LeLongTermKeyRequestReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeLongTermKeyRequestNegativeReply : Command (op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeLongTermKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeReadSupportedStates : Command (op_code = LE_READ_SUPPORTED_STATES) { +} + +packet LeReadSupportedStatesComplete : CommandComplete (command_op_code = LE_READ_SUPPORTED_STATES) { + status : ErrorCode, + le_states : 64, +} + +packet LeReceiverTest : Command (op_code = LE_RECEIVER_TEST_V1) { + rx_channel : 8, +} + +packet LeReceiverTestComplete : CommandComplete (command_op_code = LE_RECEIVER_TEST_V1) { + status : ErrorCode, +} + +enum LeTestPayload : 8 { + PRBS9 = 0x00, + REPEATED_F0 = 0x01, + REPEATED_AA = 0x02, + PRBS15 = 0x03, + REPEATED_FF = 0x04, + REPEATED_00 = 0x05, + REPEATED_0F = 0x06, + REPEATED_55 = 0x07, +} + +packet LeTransmitterTest : Command (op_code = LE_TRANSMITTER_TEST_V1) { + tx_channel : 8, + test_data_length : 8, + packet_payload : LeTestPayload, +} + +packet LeTransmitterTestComplete : CommandComplete (command_op_code = LE_TRANSMITTER_TEST_V1) { + status : ErrorCode, +} + +packet LeTestEnd : Command (op_code = LE_TEST_END) { +} + +packet LeTestEndComplete : CommandComplete (command_op_code = LE_TEST_END) { + status : ErrorCode, +} + +packet LeRemoteConnectionParameterRequestReply : Command (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) { + connection_handle : 12, + _reserved_ : 4, + interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s) + interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s) + latency : 16, // 0x0006-0x01F3 + timeout : 16, // 0x00A to 0x0C80 (100ms to 32s) + minimum_ce_length : 16, // 0.625ms + maximum_ce_length : 16, // 0.625ms +} + +packet LeRemoteConnectionParameterRequestReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeRemoteConnectionParameterRequestNegativeReply : Command (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) { + connection_handle : 12, + _reserved_ : 4, + reason : ErrorCode, +} + +packet LeRemoteConnectionParameterRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeSetDataLength : Command (op_code = LE_SET_DATA_LENGTH) { + connection_handle : 12, + _reserved_ : 4, + tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB + tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290 +} + +packet LeSetDataLengthComplete : CommandComplete (command_op_code = LE_SET_DATA_LENGTH) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeReadSuggestedDefaultDataLength : Command (op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) { +} + +packet LeReadSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) { + status : ErrorCode, + tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB + tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290 +} + +packet LeWriteSuggestedDefaultDataLength : Command (op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) { + tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB + tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290 +} + +packet LeWriteSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) { + status : ErrorCode, +} + +packet LeReadLocalP256PublicKeyCommand : Command (op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY) { +} + +packet LeReadLocalP256PublicKeyCommandStatus : CommandStatus (command_op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY) { +} + +packet LeGenerateDhkeyV1Command : Command (op_code = LE_GENERATE_DHKEY_V1) { + remote_p_256_public_key : 8[64], +} + +packet LeGenerateDhkeyV1CommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_V1) { +} + +packet LeAddDeviceToResolvingList : Command (op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) { + peer_identity_address_type : PeerAddressType, + peer_identity_address : Address, + peer_irk : 8[16], + local_irk : 8[16], +} + +packet LeAddDeviceToResolvingListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) { + status : ErrorCode, +} + +packet LeRemoveDeviceFromResolvingList : Command (op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) { + peer_identity_address_type : PeerAddressType, + peer_identity_address : Address, +} + +packet LeRemoveDeviceFromResolvingListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) { + status : ErrorCode, +} + +packet LeClearResolvingList : Command (op_code = LE_CLEAR_RESOLVING_LIST) { +} + +packet LeClearResolvingListComplete : CommandComplete (command_op_code = LE_CLEAR_RESOLVING_LIST) { + status : ErrorCode, +} + +packet LeReadResolvingListSize : Command (op_code = LE_READ_RESOLVING_LIST_SIZE) { +} + +packet LeReadResolvingListSizeComplete : CommandComplete (command_op_code = LE_READ_RESOLVING_LIST_SIZE) { + status : ErrorCode, + resolving_list_size : 8, +} + +packet LeReadPeerResolvableAddress : Command (op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) { + peer_identity_address_type : PeerAddressType, + peer_identity_address : Address, +} + +packet LeReadPeerResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) { + status : ErrorCode, + peer_resolvable_address : Address, +} + +packet LeReadLocalResolvableAddress : Command (op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) { + peer_identity_address_type : PeerAddressType, + peer_identity_address : Address, +} + +packet LeReadLocalResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) { + status : ErrorCode, + local_resolvable_address : Address, +} + +packet LeSetAddressResolutionEnable : Command (op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) { + address_resolution_enable : Enable, +} + +packet LeSetAddressResolutionEnableComplete : CommandComplete (command_op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) { + status : ErrorCode, +} + +packet LeSetResolvablePrivateAddressTimeout : Command (op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) { + rpa_timeout : 16, // RPA_Timeout measured in seconds 0x0001 to 0xA1B8 1s to 11.5 hours +} + +packet LeSetResolvablePrivateAddressTimeoutComplete : CommandComplete (command_op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) { + status : ErrorCode, +} + +packet LeReadMaximumDataLength : Command (op_code = LE_READ_MAXIMUM_DATA_LENGTH) { +} + +struct LeMaximumDataLength { + supported_max_tx_octets : 16, + supported_max_tx_time: 16, + supported_max_rx_octets : 16, + supported_max_rx_time: 16, +} + +packet LeReadMaximumDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_DATA_LENGTH) { + status : ErrorCode, + le_maximum_data_length : LeMaximumDataLength, +} + +packet LeReadPhy : Command (op_code = LE_READ_PHY) { + connection_handle : 12, + _reserved_ : 4, +} + +enum PhyType : 8 { + LE_1M = 0x01, + LE_2M = 0x02, + LE_CODED = 0x03, +} + +packet LeReadPhyComplete : CommandComplete (command_op_code = LE_READ_PHY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + tx_phy : PhyType, + rx_phy : PhyType, +} + +packet LeSetDefaultPhy : Command (op_code = LE_SET_DEFAULT_PHY) { + all_phys_no_transmit_preference : 1, + all_phys_no_receive_preference : 1, + _reserved_ : 6, + tx_phys : 3, + _reserved_ : 5, + rx_phys : 3, + _reserved_ : 5, +} + +packet LeSetDefaultPhyComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_PHY) { + status : ErrorCode, +} + +enum PhyOptions : 8 { + NO_PREFERENCE = 0x00, + S_2 = 0x01, + S_8 = 0x02, +} + +packet LeSetPhy : Command (op_code = LE_SET_PHY) { + connection_handle : 12, + _reserved_ : 4, + all_phys_no_transmit_preference : 1, + all_phys_no_receive_preference : 1, + _reserved_ : 6, + tx_phys : 3, + _reserved_ : 5, + rx_phys : 3, + _reserved_ : 5, + phy_options : PhyOptions, +} + +packet LeSetPhyStatus : CommandStatus (command_op_code = LE_SET_PHY) { +} + +enum ModulationIndex : 8 { + STANDARD = 0x00, + STABLE = 0x01, +} + +packet LeEnhancedReceiverTest : Command (op_code = LE_RECEIVER_TEST_V2) { + rx_channel : 8, + phy : PhyType, + modulation_index : ModulationIndex, +} + +packet LeEnhancedReceiverTestComplete : CommandComplete (command_op_code = LE_RECEIVER_TEST_V2) { + status : ErrorCode, +} + +packet LeEnhancedTransmitterTest : Command (op_code = LE_TRANSMITTER_TEST_V2) { + tx_channel : 8, + test_data_length : 8, + packet_payload : LeTestPayload, + phy : PhyType, +} + +packet LeEnhancedTransmitterTestComplete : CommandComplete (command_op_code = LE_TRANSMITTER_TEST_V2) { + status : ErrorCode, +} + +packet LeSetAdvertisingSetRandomAddress : Command (op_code = LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) { + advertising_handle : 8, + random_address : Address, +} + +test LeSetAdvertisingSetRandomAddress { + "\x35\x20\x07\x00\x77\x58\xeb\xd3\x1c\x6e", +} + +packet LeSetAdvertisingSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) { + status : ErrorCode, +} + +test LeSetAdvertisingSetRandomAddressComplete { + "\x0e\x04\x01\x35\x20\x00", +} + +// The lower 4 bits of the advertising event properties +enum LegacyAdvertisingEventProperties : 4 { + ADV_IND = 0x3, + ADV_DIRECT_IND_LOW = 0x5, + ADV_DIRECT_IND_HIGH = 0xD, + ADV_SCAN_IND = 0x2, + ADV_NONCONN_IND = 0, +} + +enum PrimaryPhyType : 8 { + LE_1M = 0x01, + LE_CODED = 0x03, +} + +enum SecondaryPhyType : 8 { + NO_PACKETS = 0x00, + LE_1M = 0x01, + LE_2M = 0x02, + LE_CODED = 0x03, +} + +packet LeSetExtendedAdvertisingParametersLegacy : Command (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) { + advertising_handle : 8, + legacy_advertising_event_properties : LegacyAdvertisingEventProperties, + _fixed_ = 0x1 : 1, // legacy bit set + _reserved_ : 11, // advertising_event_properties reserved bits + primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms + primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms + primary_advertising_channel_map : 3, // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39 + _reserved_ : 5, + own_address_type : OwnAddressType, + peer_address_type : PeerAddressType, + peer_address : Address, + advertising_filter_policy : AdvertisingFilterPolicy, + _reserved_ : 6, + advertising_tx_power : 8, // -127 to +20, 0x7F - no preference + _fixed_ = 0x1 : 8, // PrimaryPhyType LE_1M + _reserved_ : 8, // secondary_advertising_max_skip + _fixed_ = 0x1 : 8, // secondary_advertising_phy LE_1M + advertising_sid : 8, // SID subfield from the ADI field of the PDU + scan_request_notification_enable : Enable, +} + +test LeSetExtendedAdvertisingParametersLegacy { + "\x36\x20\x19\x00\x13\x00\x90\x01\x00\xc2\x01\x00\x07\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x01\x00\x01\x01\x00", + "\x36\x20\x19\x01\x13\x00\x90\x01\x00\xc2\x01\x00\x07\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x01\x00\x01\x01\x00", +} + +struct AdvertisingEventProperties { + connectable : 1, + scannable : 1, + directed : 1, + high_duty_cycle : 1, + legacy : 1, + anonymous : 1, + tx_power : 1, + _reserved_ : 9, +} + +packet LeSetExtendedAdvertisingParameters : Command (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) { + advertising_handle : 8, + advertising_event_properties : AdvertisingEventProperties, + primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms + primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms + primary_advertising_channel_map : 3, // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39 + _reserved_ : 5, + own_address_type : OwnAddressType, + peer_address_type : PeerAddressType, + peer_address : Address, + advertising_filter_policy : AdvertisingFilterPolicy, + _reserved_ : 6, + advertising_tx_power : 8, // -127 to +20, 0x7F - no preference + primary_advertising_phy : PrimaryPhyType, + secondary_advertising_max_skip : 8, // 1 to 255, 0x00 - AUX_ADV_IND sent before next advertising event + secondary_advertising_phy : SecondaryPhyType, + advertising_sid : 8, // SID subfield from the ADI field of the PDU + scan_request_notification_enable : Enable, +} + +packet LeSetExtendedAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) { + status : ErrorCode, + selected_tx_power : 8, // -127 to +20 +} + +enum Operation : 3 { + INTERMEDIATE_FRAGMENT = 0, + FIRST_FRAGMENT = 1, + LAST_FRAGMENT = 2, + COMPLETE_ADVERTISEMENT = 3, + UNCHANGED_DATA = 4, +} + +enum FragmentPreference : 1 { + CONTROLLER_MAY_FRAGMENT = 0, + CONTROLLER_SHOULD_NOT = 1, +} + +packet LeSetExtendedAdvertisingData : Command (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) { + advertising_handle : 8, + operation : Operation, + _reserved_ : 5, + fragment_preference : FragmentPreference, + _reserved_ : 7, + _size_(advertising_data) : 8, + advertising_data : 8[], +} + +test LeSetExtendedAdvertisingData { + "\x37\x20\x12\x00\x03\x01\x0e\x02\x01\x02\x0a\x09\x50\x69\x78\x65\x6c\x20\x33\x20\x58", +} + +packet LeSetExtendedAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_DATA) { + status : ErrorCode, +} + +test LeSetExtendedAdvertisingDataComplete { + "\x0e\x04\x01\x37\x20\x00", +} + +packet LeSetExtendedScanResponseData : Command (op_code = LE_SET_EXTENDED_SCAN_RESPONSE_DATA) { + advertising_handle : 8, + operation : Operation, + _reserved_ : 5, + fragment_preference : FragmentPreference, + _reserved_ : 7, + _size_(scan_response_data) : 8, + scan_response_data : 8[], +} + +packet LeSetExtendedScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_RESPONSE_DATA) { + status : ErrorCode, +} + +packet LeSetExtendedAdvertisingEnableDisableAll : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) { + _fixed_ = 0x00 : 8, // Enable::DISABLED + _fixed_ = 0x00 : 8, // Disable all sets +} + +struct EnabledSet { + advertising_handle : 8, + duration : 16, + max_extended_advertising_events : 8, +} + +struct DisabledSet { + advertising_handle : 8, + _fixed_ = 0x00 : 16, // duration + _fixed_ = 0x00 : 8, // max_extended_advertising_events +} + +packet LeSetExtendedAdvertisingDisable : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) { + _fixed_ = 0x00 : 8, // Enable::DISABLED + _count_(disabled_sets) : 8, + disabled_sets : DisabledSet[], +} + +packet LeSetExtendedAdvertisingEnable : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) { + enable : Enable, + _count_(enabled_sets) : 8, + enabled_sets : EnabledSet[], +} + +test LeSetExtendedAdvertisingEnable { + "\x39\x20\x06\x01\x01\x01\x00\x00\x00", +} + +test LeSetExtendedAdvertisingDisable { + "\x39\x20\x06\x00\x01\x01\x00\x00\x00", +} + +packet LeSetExtendedAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) { + status : ErrorCode, +} + +test LeSetExtendedAdvertisingEnableComplete { + "\x0e\x04\x01\x39\x20\x00", +} + +packet LeReadMaximumAdvertisingDataLength : Command (op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) { +} + +packet LeReadMaximumAdvertisingDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) { + status : ErrorCode, + maximum_advertising_data_length : 16, +} + +packet LeReadNumberOfSupportedAdvertisingSets : Command (op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) { +} + +packet LeReadNumberOfSupportedAdvertisingSetsComplete : CommandComplete (command_op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) { + status : ErrorCode, + number_supported_advertising_sets : 8, +} + +packet LeRemoveAdvertisingSet : Command (op_code = LE_REMOVE_ADVERTISING_SET) { + advertising_handle : 8, +} + +test LeRemoveAdvertisingSet { + "\x3c\x20\x01\x01", +} + +packet LeRemoveAdvertisingSetComplete : CommandComplete (command_op_code = LE_REMOVE_ADVERTISING_SET) { + status : ErrorCode, +} + +test LeRemoveAdvertisingSetComplete { + "\x0e\x04\x01\x3c\x20\x00", +} + +packet LeClearAdvertisingSets : Command (op_code = LE_CLEAR_ADVERTISING_SETS) { +} + +packet LeClearAdvertisingSetsComplete : CommandComplete (command_op_code = LE_CLEAR_ADVERTISING_SETS) { + status : ErrorCode, +} + +packet LeSetPeriodicAdvertisingParameters : Command (op_code = LE_SET_PERIODIC_ADVERTISING_PARAMETERS) { + advertising_handle : 8, + periodic_advertising_interval_min : 16, // 0x006 to 0xFFFF (7.5 ms to 82s) + periodic_advertising_interval_max : 16, // 0x006 to 0xFFFF (7.5 ms to 82s) + _reserved_ : 6, + include_tx_power : 1, + _reserved_ : 9, +} + +packet LeSetPeriodicAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_PARAMETERS) { + status : ErrorCode, +} + +packet LeSetPeriodicAdvertisingData : Command (op_code = LE_SET_PERIODIC_ADVERTISING_DATA) { + advertising_handle : 8, + operation : Operation, + _reserved_ : 5, + _size_(advertising_data) : 8, + advertising_data : 8[], +} + +packet LeSetPeriodicAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_DATA) { + status : ErrorCode, +} + +packet LeSetPeriodicAdvertisingEnable : Command (op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) { + enable : 1, + include_adi: 1, + _reserved_: 6, + advertising_handle : 8, +} + +packet LeSetPeriodicAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) { + status : ErrorCode, +} + +struct ScanningPhyParameters { + le_scan_type : LeScanType, + le_scan_interval : 16, // 0x0004-0xFFFF Default 0x10 (10ms) + le_scan_window : 16, // 0x004-0xFFFF Default 0x10 (10ms) +} + +packet LeSetExtendedScanParameters : Command (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) { + own_address_type : OwnAddressType, + scanning_filter_policy : LeScanningFilterPolicy, + scanning_phys : 8, + scanning_phy_parameters : ScanningPhyParameters[], +} + +test LeSetExtendedScanParameters { + "\x41\x20\x08\x01\x00\x01\x01\x12\x00\x12\x00", + "\x41\x20\x08\x01\x00\x01\x01\x99\x19\x99\x19", +} + +packet LeSetExtendedScanParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) { + status : ErrorCode, +} + +test LeSetExtendedScanParametersComplete { + "\x0e\x04\x01\x41\x20\x00", +} + +enum FilterDuplicates : 8 { + DISABLED = 0, + ENABLED = 1, + RESET_EACH_PERIOD = 2, +} + +packet LeSetExtendedScanEnable : Command (op_code = LE_SET_EXTENDED_SCAN_ENABLE) { + enable : Enable, + filter_duplicates : FilterDuplicates, + duration : 16, // 0 - Scan continuously, N * 10 ms + period : 16, // 0 - Scan continuously, N * 1.28 sec +} + +test LeSetExtendedScanEnable { + "\x42\x20\x06\x01\x00\x00\x00\x00\x00", + "\x42\x20\x06\x00\x01\x00\x00\x00\x00", +} + +packet LeSetExtendedScanEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_ENABLE) { + status : ErrorCode, +} + +test LeSetExtendedScanEnableComplete { + "\x0e\x04\x01\x42\x20\x00", +} + +struct InitiatingPhyParameters { + scan_interval : 16, // 0x0004-0xFFFF + scan_window : 16, // < = LeScanInterval + connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s) + connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s) + max_latency : 16, // 0x0006-0x01F3 + supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s) + min_ce_length : 16, // 0.625ms + max_ce_length : 16, // 0.625ms +} + +packet LeExtendedCreateConnection : Command (op_code = LE_EXTENDED_CREATE_CONNECTION) { + initiator_filter_policy : InitiatorFilterPolicy, + own_address_type : OwnAddressType, + peer_address_type : PeerAddressType, + peer_address : Address, + initiating_phys : 8, + initiating_phy_parameters : InitiatingPhyParameters[], +} + +test LeExtendedCreateConnection { + "\x43\x20\x2a\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x08\x30\x00\x18\x00\x28\x00\x00\x00\xf4\x01\x00\x00\x00\x00\x00\x08\x30\x00\x18\x00\x28\x00\x00\x00\xf4\x01\x00\x00\x00\x00", +} + +packet LeExtendedCreateConnectionStatus : CommandStatus (command_op_code = LE_EXTENDED_CREATE_CONNECTION) { +} + +enum PeriodicSyncCteType : 8 { + AVOID_AOA_CONSTANT_TONE_EXTENSION = 0x01, + AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS = 0x02, + AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_TWO_US_SLOTS = 0x04, + AVOID_TYPE_THREE_CONSTANT_TONE_EXTENSION = 0x08, + AVOID_NO_CONSTANT_TONE_EXTENSION = 0x10, +} + +struct PeriodicAdvertisingOptions { + use_periodic_advertiser_list: 1, + disable_reporting: 1, + enable_duplicate_filtering: 1, + _reserved_: 5, +} + +enum AdvertiserAddressType : 8 { + PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00, + RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01, +} + +packet LePeriodicAdvertisingCreateSync : Command (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) { + options : PeriodicAdvertisingOptions, + advertising_sid : 8, + advertiser_address_type : AdvertiserAddressType, + advertiser_address : Address, + skip : 16, + sync_timeout : 16, + sync_cte_type : 8, +} + +packet LePeriodicAdvertisingCreateSyncStatus : CommandStatus (command_op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) { +} + +packet LePeriodicAdvertisingCreateSyncCancel : Command (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) { +} + +packet LePeriodicAdvertisingCreateSyncCancelComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) { + status : ErrorCode, +} + +packet LePeriodicAdvertisingTerminateSync : Command (op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) { + sync_handle : 12, + _reserved_ : 4, +} + +packet LePeriodicAdvertisingTerminateSyncComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) { + status : ErrorCode, +} + +packet LeAddDeviceToPeriodicAdvertiserList : Command (op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST) { + advertiser_address_type : AdvertiserAddressType, + advertiser_address : Address, + advertising_sid : 8, +} + +packet LeAddDeviceToPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST) { + status : ErrorCode, +} + +packet LeRemoveDeviceFromPeriodicAdvertiserList : Command (op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST) { + advertiser_address_type : AdvertiserAddressType, + advertiser_address : Address, + advertising_sid : 8, +} + +packet LeRemoveDeviceFromPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST) { + status : ErrorCode, +} + +packet LeClearPeriodicAdvertiserList : Command (op_code = LE_CLEAR_PERIODIC_ADVERTISER_LIST) { +} + +packet LeClearPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_CLEAR_PERIODIC_ADVERTISER_LIST) { + status : ErrorCode, +} + +packet LeReadPeriodicAdvertiserListSize : Command (op_code = LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) { +} + +packet LeReadPeriodicAdvertiserListSizeComplete : CommandComplete (command_op_code = LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) { + status : ErrorCode, + periodic_advertiser_list_size : 8, +} + +packet LeReadTransmitPower : Command (op_code = LE_READ_TRANSMIT_POWER) { +} + +packet LeReadTransmitPowerComplete : CommandComplete (command_op_code = LE_READ_TRANSMIT_POWER) { + status : ErrorCode, + min_tx_power_dbm : 8, + max_tx_power_dbm : 8, +} + +packet LeReadRfPathCompensationPower : Command (op_code = LE_READ_RF_PATH_COMPENSATION_POWER) { +} + +packet LeReadRfPathCompensationPowerComplete : CommandComplete (command_op_code = LE_READ_RF_PATH_COMPENSATION_POWER) { + status : ErrorCode, + rf_tx_path_compensation_tenths_db : 16, + rf_rx_path_compensation_tenths_db : 16, +} + +packet LeWriteRfPathCompensationPower : Command (op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) { + rf_tx_path_compensation_tenths_db : 16, + rf_rx_path_compensation_tenths_db : 16, +} + +packet LeWriteRfPathCompensationPowerComplete : CommandComplete (command_op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) { + status : ErrorCode, +} + +enum PrivacyMode : 8 { + NETWORK = 0, + DEVICE = 1, +} + +packet LeSetPrivacyMode : Command (op_code = LE_SET_PRIVACY_MODE) { + peer_identity_address_type : PeerAddressType, + peer_identity_address : Address, + privacy_mode : PrivacyMode, +} + +packet LeSetPrivacyModeComplete : CommandComplete (command_op_code = LE_SET_PRIVACY_MODE) { + status : ErrorCode, +} + +packet LeSetPeriodicAdvertisingReceiveEnable : Command (op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) { + sync_handle : 12, + _reserved_ : 4, + enable : 8, +} + +packet LeSetPeriodicAdvertisingReceiveEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) { + status : ErrorCode, +} + +packet LePeriodicAdvertisingSyncTransfer : Command (op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) { + connection_handle : 12, + _reserved_ : 4, + service_data : 16, + sync_handle: 12, + _reserved_ : 4, +} + +packet LePeriodicAdvertisingSyncTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LePeriodicAdvertisingSetInfoTransfer : Command (op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) { + connection_handle : 12, + _reserved_ : 4, + service_data : 16, + advertising_handle: 8, +} + +packet LePeriodicAdvertisingSetInfoTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +enum SyncTransferMode : 8 { + NO_SYNC = 0, + SEND_SYNC_RECEIVED_DISABLE_REPORTS = 1, + SEND_SYNC_RECEIVED_SEND_REPORTS = 2, +} + +enum CteType : 8 { + AOA_CONSTANT_TONE_EXTENSION = 0x00, + AOD_CONSTANT_TONE_EXTENSION_ONE_US_SLOTS = 0x01, + AOD_CONSTANT_TONE_EXTENSION_TWO_US_SLOTS = 0x02, + NO_CONSTANT_TONE_EXTENSION = 0xFF, +} + +packet LeSetPeriodicAdvertisingSyncTransferParameters : Command (op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) { + connection_handle : 12, + _reserved_ : 4, + mode : SyncTransferMode, + skip: 16, + sync_timeout : 16, + cte_type : CteType, +} + +packet LeSetPeriodicAdvertisingSyncTransferParametersComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeSetDefaultPeriodicAdvertisingSyncTransferParameters : Command (op_code = LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) { + mode : SyncTransferMode, + skip: 16, + sync_timeout : 16, + cte_type : CteType, +} + +packet LeSetDefaultPeriodicAdvertisingSyncTransferParametersComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) { + status : ErrorCode, +} + +enum UseDebugKey : 8 { + USE_GENERATED_KEY = 0, + USE_DEBUG_KEY = 1, +} + +packet LeGenerateDhkeyCommand : Command (op_code = LE_GENERATE_DHKEY_V2) { + remote_p_256_public_key : 8[64], + key_type : UseDebugKey, +} + +packet LeGenerateDhkeyCommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_V2) { +} + +enum ScaAction : 8 { + MORE_ACCURATE_CLOCK = 0, + LESS_ACCURATE_CLOCK = 1, +} + +packet LeModifySleepClockAccuracy : Command (op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) { + action : ScaAction, +} + +packet LeModifySleepClockAccuracyComplete : CommandComplete (command_op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) { + status : ErrorCode, +} + +packet LeReadBufferSizeV2 : Command (op_code = LE_READ_BUFFER_SIZE_V2) { +} + +packet LeReadBufferSizeV2Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V2) { + status : ErrorCode, + le_buffer_size : LeBufferSize, + iso_buffer_size : LeBufferSize, +} + +packet LeReadIsoTxSync : Command (op_code = LE_READ_ISO_TX_SYNC) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeReadIsoTxSyncComplete : CommandComplete (command_op_code = LE_READ_ISO_TX_SYNC) { + connection_handle : 12, + _reserved_ : 4, + packet_sequence_number : 16, + timestamp : 32, + time_offset : 24, +} + +struct CisParametersConfig { + cis_id : 8, + max_sdu_c_to_p : 12, + _reserved_ : 4, + max_sdu_p_to_c : 12, + _reserved_ : 4, + phy_c_to_p : 3, + _reserved_ : 5, + phy_p_to_c : 3, + _reserved_ : 5, + rtn_c_to_p : 4, + _reserved_ : 4, + rtn_p_to_c : 4, + _reserved_ : 4, +} + +enum Packing : 8 { + SEQUENTIAL = 0, + INTERLEAVED = 1, +} + +enum ClockAccuracy : 8 { + PPM_500 = 0x00, + PPM_250 = 0x01, + PPM_150 = 0x02, + PPM_100 = 0x03, + PPM_75 = 0x04, + PPM_50 = 0x05, + PPM_30 = 0x06, + PPM_20 = 0x07, +} + +packet LeSetCigParameters : Command (op_code = LE_SET_CIG_PARAMETERS) { + cig_id : 8, + sdu_interval_c_to_p : 24, + sdu_interval_p_to_c : 24, + worst_case_sca: ClockAccuracy, + packing : Packing, + framing : Enable, + max_transport_latency_c_to_p : 16, + max_transport_latency_p_to_c : 16, + _count_(cis_config) : 8, + cis_config : CisParametersConfig[], +} + +packet LeSetCigParametersComplete : CommandComplete (command_op_code = LE_SET_CIG_PARAMETERS) { + status : ErrorCode, + cig_id : 8, + _count_(connection_handle) : 8, + connection_handle : 16[], +} + +struct LeCisParametersTestConfig { + cis_id : 8, + nse : 8, + max_sdu_c_to_p : 16, + max_sdu_p_to_c : 16, + max_pdu_c_to_p : 16, + max_pdu_p_to_c : 16, + phy_c_to_p : 8, + phy_p_to_c : 8, + bn_c_to_p : 8, + bn_p_to_c : 8, +} + +packet LeSetCigParametersTest : Command (op_code = LE_SET_CIG_PARAMETERS_TEST) { + cig_id : 8, + sdu_interval_c_to_p : 24, + sdu_interval_p_to_c : 24, + ft_c_to_p : 8, + ft_p_to_c : 8, + iso_interval : 16, + worst_case_sca: ClockAccuracy, + packing : Packing, + framing : Enable, + _count_(cis_config) : 8, + cis_config : LeCisParametersTestConfig[], +} + +packet LeSetCigParametersTestComplete : CommandComplete (command_op_code = LE_SET_CIG_PARAMETERS_TEST) { + status : ErrorCode, + cig_id : 8, + _count_(connection_handle) : 8, + connection_handle : 16[], +} + +struct LeCreateCisConfig { + cis_connection_handle : 12, + _reserved_ : 4, + acl_connection_handle : 12, + _reserved_ : 4, +} + +packet LeCreateCis : Command (op_code = LE_CREATE_CIS) { + _count_(cis_config) : 8, + cis_config : LeCreateCisConfig[], +} + +packet LeCreateCisStatus : CommandStatus (command_op_code = LE_CREATE_CIS) { +} + +packet LeRemoveCig : Command (op_code = LE_REMOVE_CIG) { + cig_id : 8, +} + +packet LeRemoveCigComplete : CommandComplete (command_op_code = LE_REMOVE_CIG) { + status : ErrorCode, + cig_id : 8, +} + +packet LeAcceptCisRequest : Command (op_code = LE_ACCEPT_CIS_REQUEST) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeAcceptCisRequestStatus : CommandStatus (command_op_code = LE_ACCEPT_CIS_REQUEST) { +} + +packet LeRejectCisRequest : Command (op_code = LE_REJECT_CIS_REQUEST) { + connection_handle : 12, + _reserved_ : 4, + reason : ErrorCode, +} + +packet LeRejectCisRequestComplete : CommandComplete (command_op_code = LE_REJECT_CIS_REQUEST) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeCreateBig : Command (op_code = LE_CREATE_BIG) { + big_handle : 8, + advertising_handle : 8, + num_bis : 8, + sdu_interval : 24, + max_sdu : 16, + max_transport_latency : 16, + rtn : 4, + _reserved_ : 4, + phy : SecondaryPhyType, + packing : Packing, + framing : Enable, + encryption : Enable, + broadcast_code: 8[16], +} + +packet LeCreateBigStatus : CommandStatus (command_op_code = LE_CREATE_BIG) { +} + +packet LeTerminateBig : Command (op_code = LE_TERMINATE_BIG) { + big_handle : 8, + reason : ErrorCode, +} + +packet LeTerminateBigStatus : CommandStatus (command_op_code = LE_TERMINATE_BIG) { +} + +packet LeBigCreateSync : Command (op_code = LE_BIG_CREATE_SYNC) { + big_handle : 8, + sync_handle : 12, + _reserved_ : 4, + encryption : Enable, + broadcast_code : 8[16], + mse : 5, + _reserved_ : 3, + big_sync_timeout : 16, + _count_(bis) : 8, + bis : 8[], +} + +packet LeBigCreateSyncStatus : CommandStatus (command_op_code = LE_BIG_CREATE_SYNC) { +} + +packet LeBigTerminateSync : Command (op_code = LE_BIG_TERMINATE_SYNC) { + big_handle : 8, +} + +packet LeBigTerminateSyncComplete : CommandComplete (command_op_code = LE_BIG_TERMINATE_SYNC) { + status : ErrorCode, + big_handle : 8, +} + +packet LeRequestPeerSca : Command (op_code = LE_REQUEST_PEER_SCA) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeRequestPeerScaStatus : CommandStatus (command_op_code = LE_REQUEST_PEER_SCA) { +} + +packet LeSetupIsoDataPath : Command (op_code = LE_SETUP_ISO_DATA_PATH) { + connection_handle : 12, + _reserved_ : 4, + data_path_direction : DataPathDirection, + data_path_id : 8, + codec_id : 40, + controller_delay : 24, + _count_(codec_configuration) : 8, + codec_configuration : 8[], +} + +packet LeSetupIsoDataPathComplete : CommandComplete (command_op_code = LE_SETUP_ISO_DATA_PATH) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +enum RemoveDataPathDirection : 8 { + INPUT = 1, + OUTPUT = 2, + INPUT_AND_OUTPUT = 3, +} + +packet LeRemoveIsoDataPath : Command (op_code = LE_REMOVE_ISO_DATA_PATH) { + connection_handle : 12, + _reserved_ : 4, + remove_data_path_direction : RemoveDataPathDirection, +} + +packet LeRemoveIsoDataPathComplete : CommandComplete (command_op_code = LE_REMOVE_ISO_DATA_PATH) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +enum LeHostFeatureBits : 8 { + CONNECTED_ISO_STREAM_HOST_SUPPORT = 32, + CONNECTION_SUBRATING_HOST_SUPPORT = 38, +} + +packet LeSetHostFeature : Command (op_code = LE_SET_HOST_FEATURE) { + bit_number : LeHostFeatureBits, + bit_value: Enable, +} + +packet LeSetHostFeatureComplete : CommandComplete (command_op_code = LE_SET_HOST_FEATURE) { + status : ErrorCode, +} + +packet LeReadIsoLinkQuality : Command (op_code = LE_READ_ISO_LINK_QUALITY) { + connection_handle : 12, + _reserved_ : 4, +} + +packet LeReadIsoLinkQualityComplete : CommandComplete (command_op_code = LE_READ_ISO_LINK_QUALITY) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + tx_unacked_packets : 32, + tx_flushed_packets : 32, + tx_last_subevent_packets : 32, + retransmitted_packets : 32, + crc_error_packets : 32, + rx_unreceived_packets : 32, + duplicate_packets : 32, +} + +packet LeEnhancedReadTransmitPowerLevel : Command (op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) { + connection_handle : 12, + _reserved_ : 4, + phy : 8, +} + +enum PhyWithCodedSpecified : 8 { + LE_1M = 1, + LE_2M = 2, + LE_CODED_S_8 = 3, + LE_CODED_S_2 = 4, +} + +packet LeEnhancedReadTransmitPowerLevelComplete : CommandComplete (command_op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + phy : PhyWithCodedSpecified, + current_transmit_power_level : 8, + max_transmit_power_level : 8, +} + +packet LeReadRemoteTransmitPowerLevel : Command (op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) { + connection_handle : 12, + _reserved_ : 4, + phy : 8, +} + +packet LeReadRemoteTransmitPowerLevelStatus : CommandStatus (command_op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) { +} + +packet LeSetPathLossReportingParameters : Command (op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) { + connection_handle : 12, + _reserved_ : 4, + high_threshold : 8, + high_hysteresis : 8, + low_threshold : 8, + low_hysteresis : 8, + min_time_spent : 16, +} + +packet LeSetPathLossReportingParametersComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeSetPathLossReportingEnable : Command (op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) { + connection_handle : 12, + _reserved_ : 4, + enable : 8, +} + +packet LeSetPathLossReportingEnableComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeSetTransmitPowerReportingEnable : Command (op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) { + connection_handle : 12, + _reserved_ : 4, + local_enable : 8, + remote_enable : 8, +} + +packet LeSetTransmitPowerReportingEnableComplete : CommandComplete (command_op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet LeSetDataRelatedAddressChanges : Command (op_code = LE_SET_DATA_RELATED_ADDRESS_CHANGES) { + advertising_handle : 8, + change_reasons : 8, +} + +packet LeSetDataRelatedAddressChangesComplete : CommandComplete (command_op_code = LE_SET_DATA_RELATED_ADDRESS_CHANGES) { + status : ErrorCode, +} + +packet LeSetDefaultSubrate : Command (op_code = LE_SET_DEFAULT_SUBRATE) { + subrate_min : 9, + _reserved_ : 7, + subrate_max : 9, + _reserved_ : 7, + max_latency : 9, + _reserved_ : 7, + continuation_number : 9, + _reserved_ : 7, + supervision_timeout: 12, + _reserved_ : 4, +} + +packet LeSetDefaultSubrateComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_SUBRATE) { + status : ErrorCode, +} + +packet LeSubrateRequest : Command (op_code = LE_SUBRATE_REQUEST) { + connection_handle : 12, + _reserved_ : 4, + subrate_min : 9, + _reserved_ : 7, + subrate_max : 9, + _reserved_ : 7, + max_latency : 9, + _reserved_ : 7, + continuation_number : 9, + _reserved_ : 7, + supervision_timeout: 12, + _reserved_ : 4, +} + +packet LeSubrateRequestStatus : CommandStatus (command_op_code = LE_SUBRATE_REQUEST) { +} + +// HCI Event Packets + +packet InquiryComplete : Event (event_code = INQUIRY_COMPLETE) { + status : ErrorCode, +} + +struct InquiryResponse { + bd_addr : Address, + page_scan_repetition_mode : PageScanRepetitionMode, + _reserved_ : 8, + _reserved_ : 8, + class_of_device : 24, + clock_offset : 15, + _reserved_ : 1, +} + +packet InquiryResult : Event (event_code = INQUIRY_RESULT) { + _count_(responses) : 8, + responses : InquiryResponse[], +} + +enum LinkType : 8 { + SCO = 0x00, + ACL = 0x01, +} + +packet ConnectionComplete : Event (event_code = CONNECTION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + bd_addr : Address, + link_type : LinkType, + encryption_enabled : Enable, +} + +enum ConnectionRequestLinkType : 8 { + UNKNOWN = 0xFF, + SCO = 0x00, + ACL = 0x01, + ESCO = 0x02, +} + +packet ConnectionRequest : Event (event_code = CONNECTION_REQUEST) { + bd_addr : Address, + class_of_device : 24, + link_type : ConnectionRequestLinkType, +} + +packet DisconnectionComplete : Event (event_code = DISCONNECTION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + reason : ErrorCode, +} + +packet AuthenticationComplete : Event (event_code = AUTHENTICATION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet RemoteNameRequestComplete : Event (event_code = REMOTE_NAME_REQUEST_COMPLETE) { + status : ErrorCode, + bd_addr : Address, + remote_name : 8[248], // UTF-8 encoded user-friendly descriptive name +} + +enum EncryptionEnabled : 8 { + OFF = 0x00, + ON = 0x01, // E0 for BR/EDR and AES-CCM for LE + BR_EDR_AES_CCM = 0x02, +} + +packet EncryptionChange : Event (event_code = ENCRYPTION_CHANGE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + encryption_enabled : EncryptionEnabled, +} + +packet ChangeConnectionLinkKeyComplete : Event (event_code = CHANGE_CONNECTION_LINK_KEY_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet CentralLinkKeyComplete : Event (event_code = CENTRAL_LINK_KEY_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + key_flag : KeyFlag, +} + +packet ReadRemoteSupportedFeaturesComplete : Event (event_code = READ_REMOTE_SUPPORTED_FEATURES_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + lmp_features : 64, +} + +packet ReadRemoteVersionInformationComplete : Event (event_code = READ_REMOTE_VERSION_INFORMATION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + version : 8, + manufacturer_name : 16, + sub_version : 16, +} + +packet QosSetupComplete : Event (event_code = QOS_SETUP_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + _reserved_ : 8, + service_type : ServiceType, + token_rate : 32, // Octets/s + peak_bandwidth : 32, // Octets/s + latency : 32, // Octets/s + delay_variation : 32, // microseconds +} + +// Command Complete and Command Status Events are implemented above Commands. + +packet HardwareError : Event (event_code = HARDWARE_ERROR) { + hardware_code : 8, +} + +packet FlushOccurred : Event (event_code = FLUSH_OCCURRED) { + connection_handle : 12, + _reserved_ : 4, +} + +packet RoleChange : Event (event_code = ROLE_CHANGE) { + status : ErrorCode, + bd_addr : Address, + new_role : Role, +} + +packet NumberOfCompletedPackets : Event (event_code = NUMBER_OF_COMPLETED_PACKETS) { + _count_(completed_packets) : 8, + completed_packets : CompletedPackets[], +} + +enum Mode : 8 { + ACTIVE = 0x00, + HOLD = 0x01, + SNIFF = 0x02, +} + +packet ModeChange : Event (event_code = MODE_CHANGE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + current_mode : Mode, + interval : 16, // 0x002 - 0xFFFE (1.25ms - 40.9s) +} + +struct ZeroKeyAndAddress { + address : Address, + _fixed_ = 0 : 64, + _fixed_ = 0 : 64, +} + +packet ReturnLinkKeys : Event (event_code = RETURN_LINK_KEYS) { + _count_(keys) : 8, + keys : ZeroKeyAndAddress[], +} + +packet PinCodeRequest : Event (event_code = PIN_CODE_REQUEST) { + bd_addr : Address, +} + +packet LinkKeyRequest : Event (event_code = LINK_KEY_REQUEST) { + bd_addr : Address, +} + +enum KeyType : 8 { + COMBINATION = 0x00, + DEBUG_COMBINATION = 0x03, + UNAUTHENTICATED_P192 = 0x04, + AUTHENTICATED_P192 = 0x05, + CHANGED = 0x06, + UNAUTHENTICATED_P256 = 0x07, + AUTHENTICATED_P256 = 0x08, +} + +packet LinkKeyNotification : Event (event_code = LINK_KEY_NOTIFICATION) { + bd_addr : Address, + link_key : 8[16], + key_type : KeyType, +} + +packet LoopbackCommand : Event (event_code = LOOPBACK_COMMAND) { + _payload_, // Command packet, truncated if it was longer than 252 bytes +} + +packet DataBufferOverflow : Event (event_code = DATA_BUFFER_OVERFLOW) { + link_type : LinkType, +} + +packet MaxSlotsChange : Event (event_code = MAX_SLOTS_CHANGE) { + connection_handle : 12, + _reserved_ : 4, + lmp_max_slots : 8, +} + +packet ReadClockOffsetComplete : Event (event_code = READ_CLOCK_OFFSET_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + clock_offset : 15, + _reserved_ : 1, +} + +packet ConnectionPacketTypeChanged : Event (event_code = CONNECTION_PACKET_TYPE_CHANGED) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + packet_type : 16, +} + +packet QosViolation : Event (event_code = QOS_VIOLATION) { + connection_handle : 12, + _reserved_ : 4, +} + +packet PageScanRepetitionModeChange : Event (event_code = PAGE_SCAN_REPETITION_MODE_CHANGE) { + bd_addr : Address, + page_scan_repetition_mode : PageScanRepetitionMode, +} + +packet FlowSpecificationComplete : Event (event_code = FLOW_SPECIFICATION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + _reserved_ : 8, + flow_direction : FlowDirection, + service_type : ServiceType, + token_rate : 32, // Octets/s + token_bucket_size : 32, + peak_bandwidth : 32, // Octets/s + access_latency : 32, // Octets/s +} + +struct InquiryResponseWithRssi { + address : Address, + page_scan_repetition_mode : PageScanRepetitionMode, + _reserved_ : 8, + class_of_device : 24, + clock_offset : 15, + _reserved_ : 1, + rssi : 8, +} + +packet InquiryResultWithRssi : Event (event_code = INQUIRY_RESULT_WITH_RSSI) { + _count_(responses) : 8, + responses : InquiryResponseWithRssi[], +} + +packet ReadRemoteExtendedFeaturesComplete : Event (event_code = READ_REMOTE_EXTENDED_FEATURES_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + page_number : 8, + maximum_page_number : 8, + extended_lmp_features : 64, +} + +enum ScoLinkType : 8 { + SCO = 0x00, + ESCO = 0x02, +} + +enum ScoAirMode : 8 { + ULAW_LOG = 0x00, + ALAW_LOG = 0x01, + CVSD = 0x02, + TRANSPARENT = 0x03, +} + +packet SynchronousConnectionComplete : Event (event_code = SYNCHRONOUS_CONNECTION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + bd_addr : Address, + link_type : ScoLinkType, + // Time between two consecutive eSCO instants measured in slots. + // eSCO only, Shall be zero for SCO links. + transmission_interval_slots : 8, + // The size of the retransmission window measured in slots. + // eSCO only. Shall be zero for SCO links. + retransmission_window_slots : 8, + // Length in bytes of the eSCO payload in the receive direction. + // eSCO only. Shall be zero for SCO links. + rx_packet_length : 16, + // Length in bytes of the eSCO payload in the transmit direction. + // eSCO only. Shall be zero for SCO links. + tx_packet_length : 16, + air_mode : ScoAirMode, +} + +test SynchronousConnectionComplete { + "\x2c\x11\x00\x03\x00\x1d\xdf\xed\x2b\x1a\xf8\x02\x0c\x04\x3c\x00\x3c\x00\x03", +} + +packet SynchronousConnectionChanged : Event (event_code = SYNCHRONOUS_CONNECTION_CHANGED) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + // Time between two consecutive eSCO instants measured in slots. + // eSCO only, Shall be zero for SCO links. + transmission_interval_slots : 8, + // Time between two consecutive SCO/eSCO instants measured in slots. + retransmission_window_slots : 8, + // Length in bytes of the SCO/eSCO payload in the receive direction. + rx_packet_length : 16, + // Length in bytes of the SCO/eSCO payload in the transmit direction. + tx_packet_length : 16, +} + +packet SniffSubratingEvent : Event (event_code = SNIFF_SUBRATING) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + maximum_transmit_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s) + maximum_receive_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s) + minimum_remote_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s) + minimum_local_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s) +} + +packet ExtendedInquiryResult : Event (event_code = EXTENDED_INQUIRY_RESULT) { + _fixed_ = 0x01 : 8, + address : Address, + page_scan_repetition_mode : PageScanRepetitionMode, + _reserved_ : 8, + class_of_device : 24, + clock_offset : 15, + _reserved_ : 1, + rssi : 8, + // Extended inquiry Result is always 255 bytes long; + // the gap data is padded with zeros as necessary. + // Refer to BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part C Section 8 on page 1340 + extended_inquiry_response : 8[240], +} + +packet EncryptionKeyRefreshComplete : Event (event_code = ENCRYPTION_KEY_REFRESH_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, +} + +packet IoCapabilityRequest : Event (event_code = IO_CAPABILITY_REQUEST) { + bd_addr : Address, +} + +packet IoCapabilityResponse : Event (event_code = IO_CAPABILITY_RESPONSE) { + bd_addr : Address, + io_capability : IoCapability, + oob_data_present : OobDataPresent, + authentication_requirements : AuthenticationRequirements, +} + +packet UserConfirmationRequest : Event (event_code = USER_CONFIRMATION_REQUEST) { + bd_addr : Address, + numeric_value : 20, // 0x00000-0xF423F (000000 - 999999) + _reserved_ : 12, +} + +packet UserPasskeyRequest : Event (event_code = USER_PASSKEY_REQUEST) { + bd_addr : Address, +} + +packet RemoteOobDataRequest : Event (event_code = REMOTE_OOB_DATA_REQUEST) { + bd_addr : Address, +} + +packet SimplePairingComplete : Event (event_code = SIMPLE_PAIRING_COMPLETE) { + status : ErrorCode, + bd_addr : Address, +} + +packet LinkSupervisionTimeoutChanged : Event (event_code = LINK_SUPERVISION_TIMEOUT_CHANGED) { + connection_handle : 12, + _reserved_ : 4, + link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s) +} + +enum FlushablePacketType : 8 { + AUTOMATICALLY_FLUSHABLE_ONLY = 0, +} + +packet EnhancedFlush : Command (op_code = ENHANCED_FLUSH) { + connection_handle : 12, + _reserved_ : 4, + packet_type : FlushablePacketType, +} + +test EnhancedFlush { + "\x5f\x0c\x03\x02\x00\x00", +} + +packet EnhancedFlushStatus : CommandStatus (command_op_code = ENHANCED_FLUSH) { +} + +packet EnhancedFlushComplete : Event (event_code = ENHANCED_FLUSH_COMPLETE) { + connection_handle : 12, + _reserved_ : 4, +} + +test EnhancedFlushComplete { + "\x39\x02\x02\x00", +} + +packet UserPasskeyNotification : Event (event_code = USER_PASSKEY_NOTIFICATION) { + bd_addr : Address, + passkey : 20, // 0x00000-0xF423F (000000 - 999999) + _reserved_ : 12, +} + +packet KeypressNotification : Event (event_code = KEYPRESS_NOTIFICATION) { + bd_addr : Address, + notification_type : KeypressNotificationType, +} + +packet RemoteHostSupportedFeaturesNotification : Event (event_code = REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION) { + bd_addr : Address, + host_supported_features : 64, +} + +packet LeMetaEvent : Event (event_code = LE_META_EVENT) { + subevent_code : SubeventCode, + _body_, +} + +packet NumberOfCompletedDataBlocks : Event (event_code = NUMBER_OF_COMPLETED_DATA_BLOCKS) { + total_num_data_blocks : 16, + _payload_, // placeholder (unimplemented) +} + +// LE Events +packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + role : Role, + peer_address_type : AddressType, + peer_address : Address, + connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms) + peripheral_latency : 16, + supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s) + central_clock_accuracy : ClockAccuracy, +} + +enum AdvertisingEventType : 8 { + ADV_IND = 0x00, + ADV_DIRECT_IND = 0x01, + ADV_SCAN_IND = 0x02, + ADV_NONCONN_IND = 0x03, + SCAN_RESPONSE = 0x04, +} + +struct LeAdvertisingResponse { + event_type : AdvertisingEventType, + address_type : AddressType, + address : Address, + _size_(advertising_data) : 8, + advertising_data : 8[], + rssi : 8, +} + +packet LeAdvertisingReport : LeMetaEvent (subevent_code = ADVERTISING_REPORT) { + _count_(responses) : 8, + responses : LeAdvertisingResponse[], +} + +packet LeConnectionUpdateComplete : LeMetaEvent (subevent_code = CONNECTION_UPDATE_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms) + peripheral_latency : 16, // Number of connection events + supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s) +} + +packet LeReadRemoteFeaturesComplete : LeMetaEvent (subevent_code = READ_REMOTE_FEATURES_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + le_features : 64, +} + +packet LeLongTermKeyRequest : LeMetaEvent (subevent_code = LONG_TERM_KEY_REQUEST) { + connection_handle : 12, + _reserved_ : 4, + random_number : 8[8], + encrypted_diversifier : 16, +} + +packet LeRemoteConnectionParameterRequest : LeMetaEvent (subevent_code = REMOTE_CONNECTION_PARAMETER_REQUEST) { + connection_handle : 12, + _reserved_ : 4, + interval_min : 16, // 0x006 - 0x0C80 (7.5ms - 4s) + interval_max : 16, // 0x006 - 0x0C80 (7.5ms - 4s) + latency : 16, // Number of connection events (0x0000 to 0x01f3 (499) + timeout : 16, // 0x000A to 0x0C80 (100ms to 32s) +} + +packet LeDataLengthChange : LeMetaEvent (subevent_code = DATA_LENGTH_CHANGE) { + connection_handle : 12, + _reserved_ : 4, + max_tx_octets : 16, // 0x001B - 0x00FB + max_tx_time : 16, // 0x0148 - 0x4290 + max_rx_octets : 16, // 0x001B - 0x00FB + max_rx_time : 16, // 0x0148 - 0x4290 +} + +packet ReadLocalP256PublicKeyComplete : LeMetaEvent (subevent_code = READ_LOCAL_P256_PUBLIC_KEY_COMPLETE) { + status : ErrorCode, + local_p_256_public_key : 8[64], +} + +packet GenerateDhKeyComplete : LeMetaEvent (subevent_code = GENERATE_DHKEY_COMPLETE) { + status : ErrorCode, + dh_key : 8[32], +} + +packet LeEnhancedConnectionComplete : LeMetaEvent (subevent_code = ENHANCED_CONNECTION_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + role : Role, + peer_address_type : AddressType, + peer_address : Address, + local_resolvable_private_address : Address, + peer_resolvable_private_address : Address, + connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms) + peripheral_latency : 16, + supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s) + central_clock_accuracy : ClockAccuracy, +} + +enum DirectAdvertisingAddressType : 8 { + PUBLIC_DEVICE_ADDRESS = 0x00, + RANDOM_DEVICE_ADDRESS = 0x01, + PUBLIC_IDENTITY_ADDRESS = 0x02, + RANDOM_IDENTITY_ADDRESS = 0x03, + CONTROLLER_UNABLE_TO_RESOLVE = 0xFE, + NO_ADDRESS_PROVIDED = 0xFF, +} + +enum DirectAdvertisingEventType : 8 { + ADV_DIRECT_IND = 0x01, +} + +enum DirectAddressType : 8 { + RANDOM_DEVICE_ADDRESS = 0x01, +} + +struct LeDirectedAdvertisingResponse { + event_type : DirectAdvertisingEventType, + address_type : DirectAdvertisingAddressType, + address : Address, + direct_address_type : DirectAddressType, + direct_address : Address, + rssi : 8, +} + +packet LeDirectedAdvertisingReport : LeMetaEvent (subevent_code = DIRECTED_ADVERTISING_REPORT) { + _count_(responses) : 8, + responses : LeDirectedAdvertisingResponse[], +} + +packet LePhyUpdateComplete : LeMetaEvent (subevent_code = PHY_UPDATE_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + tx_phy : PhyType, + rx_phy : PhyType, +} + +enum DataStatus : 2 { + COMPLETE = 0x0, + CONTINUING = 0x1, + TRUNCATED = 0x2, + RESERVED = 0x3, +} + +struct LeExtendedAdvertisingResponse { + connectable : 1, + scannable : 1, + directed : 1, + scan_response : 1, + legacy : 1, + data_status : DataStatus, + _reserved_ : 9, + address_type : DirectAdvertisingAddressType, + address : Address, + primary_phy : PrimaryPhyType, + secondary_phy : SecondaryPhyType, + advertising_sid : 8, // SID subfield in the ADI field + tx_power : 8, + rssi : 8, // -127 to +20 (0x7F means not available) + periodic_advertising_interval : 16, // 0x006 to 0xFFFF (7.5 ms to 82s) + direct_address_type : DirectAdvertisingAddressType, + direct_address : Address, + _size_(advertising_data) : 8, + advertising_data: 8[], +} + +packet LeExtendedAdvertisingReport : LeMetaEvent (subevent_code = EXTENDED_ADVERTISING_REPORT) { + _count_(responses) : 8, + responses : LeExtendedAdvertisingResponse[], +} + +packet LePeriodicAdvertisingSyncEstablished : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_ESTABLISHED) { + status : ErrorCode, + sync_handle : 12, + _reserved_ : 4, + advertising_sid : 8, + advertiser_address_type : AddressType, + advertiser_address : Address, + advertiser_phy : SecondaryPhyType, + periodic_advertising_interval : 16, + advertiser_clock_accuracy : ClockAccuracy, +} + +packet LePeriodicAdvertisingReport : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_REPORT) { + sync_handle : 12, + _reserved_ : 4, + tx_power : 8, + rssi : 8, + cte_type : CteType, + data_status : DataStatus, + _reserved_: 6, + _size_(data) : 8, + data : 8[], +} + +packet LePeriodicAdvertisingSyncLost : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_LOST) { + sync_handle : 12, + _reserved_ : 4, +} + +packet LeScanTimeout : LeMetaEvent (subevent_code = SCAN_TIMEOUT) { +} + +packet LeAdvertisingSetTerminated : LeMetaEvent (subevent_code = ADVERTISING_SET_TERMINATED) { + status : ErrorCode, + advertising_handle : 8, + connection_handle : 12, + _reserved_ : 4, + num_completed_extended_advertising_events : 8, +} + +packet LeScanRequestReceived : LeMetaEvent (subevent_code = SCAN_REQUEST_RECEIVED) { + advertising_handle : 8, + scanner_address_type : AddressType, + scanner_address : Address, +} + +enum ChannelSelectionAlgorithm : 8 { + ALGORITHM_1 = 0, + ALGORITHM_2 = 1, +} + +packet LeChannelSelectionAlgorithm : LeMetaEvent (subevent_code = CHANNEL_SELECTION_ALGORITHM) { + connection_handle : 12, + _reserved_ : 4, + channel_selection_algorithm : ChannelSelectionAlgorithm, +} + +packet LeConnectionlessIqReport : LeMetaEvent (subevent_code = CONNECTIONLESS_IQ_REPORT) { + _payload_, // placeholder (unimplemented) +} + +packet LeConnectionIqReport : LeMetaEvent (subevent_code = CONNECTION_IQ_REPORT) { + _payload_, // placeholder (unimplemented) +} + +packet LeCteRequestFailed : LeMetaEvent (subevent_code = CTE_REQUEST_FAILED) { + _payload_, // placeholder (unimplemented) +} + +packet LePeriodicAdvertisingSyncTransferReceived : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + service_data : 16, + sync_handle : 12, + _reserved_ : 4, + advertising_sid : 4, + _reserved_ : 4, + advertiser_address_type : AddressType, + advertiser_address : Address, + advertiser_phy : SecondaryPhyType, + periodic_advertising_interval : 16, + advertiser_clock_accuracy : ClockAccuracy, +} + +packet LeCisEstablished : LeMetaEvent (subevent_code = CIS_ESTABLISHED) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + cig_sync_delay : 24, + cis_sync_delay : 24, + transport_latency_c_to_p : 24, + transport_latency_p_to_c : 24, + phy_c_to_p : SecondaryPhyType, + phy_p_to_c : SecondaryPhyType, + nse : 8, + bn_c_to_p : 4, + _reserved_ : 4, + bn_p_to_c : 4, + _reserved_ : 4, + ft_c_to_p : 8, + ft_p_to_c : 8, + max_pdu_c_to_p : 8, + _reserved_ : 8, + max_pdu_p_to_c : 8, + _reserved_ : 8, + iso_interval : 16, +} + +packet LeCisRequest : LeMetaEvent (subevent_code = CIS_REQUEST) { + acl_connection_handle : 12, + _reserved_ : 4, + cis_connection_handle : 12, + _reserved_ : 4, + cig_id : 8, + cis_id : 8, +} + +packet LeCreateBigComplete : LeMetaEvent (subevent_code = CREATE_BIG_COMPLETE) { + status : ErrorCode, + big_handle : 8, + big_sync_delay : 24, + transport_latency_big: 24, + phy : SecondaryPhyType, + nse : 8, + bn : 8, + pto : 8, + irc : 8, + max_pdu : 16, + iso_interval : 16, + _size_(connection_handle) : 8, + connection_handle : 16[], +} + +packet LeTerminateBigComplete : LeMetaEvent (subevent_code = TERMINATE_BIG_COMPLETE) { + big_handle : 8, + reason : ErrorCode, +} + +packet LeBigSyncEstablished : LeMetaEvent (subevent_code = BIG_SYNC_ESTABLISHED) { + status : ErrorCode, + big_handle : 8, + transport_latency_big : 24, + nse : 8, + bn : 8, + pto : 8, + irc : 8, + max_pdu : 16, + iso_interval : 16, + _size_(connection_handle) : 8, + connection_handle : 16[], +} + +packet LeBigSyncLost : LeMetaEvent (subevent_code = BIG_SYNC_LOST) { + big_handle : 8, + reason : ErrorCode, +} + +packet LeRequestPeerScaComplete : LeMetaEvent (subevent_code = REQUEST_PEER_SCA_COMPLETE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + peer_clock_accuracy : ClockAccuracy, +} + +enum PathLossZone : 8 { + LOW = 0, + MID = 1, + HIGH = 2, +} + +packet LePathLossThreshold : LeMetaEvent (subevent_code = PATH_LOSS_THRESHOLD) { + connection_handle : 12, + _reserved_ : 4, + current_path_loss : 8, + zone_entered : PathLossZone, +} + +enum ReportingReason : 8 { + LOCAL_TRANSMIT_POWER_CHANGED = 0x00, + REMOTE_TRANSMIT_POWER_CHANGED = 0x01, + READ_COMMAND_COMPLETE = 0x02, +} + +packet LeTransmitPowerReporting : LeMetaEvent (subevent_code = TRANSMIT_POWER_REPORTING) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + reason : ReportingReason, + phy : 8, + transmit_power_level : 8, + transmit_power_level_flag : 8, + delta : 8, +} + +packet LeBigInfoAdvertisingReport : LeMetaEvent (subevent_code = BIG_INFO_ADVERTISING_REPORT) { + sync_handle : 12, + _reserved_ : 4, + num_bis : 8, + nse : 8, + iso_interval : 16, + bn : 8, + pto : 8, + irc : 8, + max_pdu : 16, + sdu_interval : 24, + max_sdu : 16, + phy : SecondaryPhyType, + framing : Enable, + encryption : Enable, +} + +packet LeSubrateChange : LeMetaEvent (subevent_code = LE_SUBRATE_CHANGE) { + status : ErrorCode, + connection_handle : 12, + _reserved_ : 4, + subrate_factor : 9, + _reserved_ : 7, + peripheral_latency : 9, + _reserved_ : 7, + continuation_number : 9, + _reserved_ : 7, + supervision_timeout: 12, + _reserved_ : 4, +} + +enum IsoPacketBoundaryFlag : 2 { + FIRST_FRAGMENT = 0, + CONTINUATION_FRAGMENT = 1, + COMPLETE_SDU = 2, + LAST_FRAGMENT = 3, +} + +enum TimeStampFlag : 1 { + NOT_PRESENT = 0, + PRESENT = 1, +} + +packet Iso { + connection_handle : 12, + pb_flag : IsoPacketBoundaryFlag, + ts_flag : TimeStampFlag, + _reserved_ : 1, + _size_(_payload_) : 14, + _reserved_ : 2, + _payload_, +} + +enum IsoPacketStatusFlag : 2 { + VALID = 0, + POSSIBLY_INVALID = 1, + LOST_DATA = 2, +} + +packet IsoWithTimestamp : Iso (ts_flag = PRESENT) { + time_stamp : 32, + packet_sequence_number : 16, + iso_sdu_length : 12, + _reserved_ : 2, + packet_status_flag : IsoPacketStatusFlag, + _payload_, +} + +packet IsoWithoutTimestamp : Iso (ts_flag = NOT_PRESENT) { + packet_sequence_number : 16, + iso_sdu_length : 12, + _reserved_ : 2, + packet_status_flag : IsoPacketStatusFlag, + _payload_, +} + +// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile +enum GapDataType : 8 { + INVALID = 0x00, + FLAGS = 0x01, + INCOMPLETE_LIST_16_BIT_UUIDS = 0x02, + COMPLETE_LIST_16_BIT_UUIDS = 0x03, + INCOMPLETE_LIST_32_BIT_UUIDS = 0x04, + COMPLETE_LIST_32_BIT_UUIDS = 0x05, + INCOMPLETE_LIST_128_BIT_UUIDS = 0x06, + COMPLETE_LIST_128_BIT_UUIDS = 0x07, + SHORTENED_LOCAL_NAME = 0x08, + COMPLETE_LOCAL_NAME = 0x09, + TX_POWER_LEVEL = 0x0A, + CLASS_OF_DEVICE = 0x0D, + SIMPLE_PAIRING_HASH_C = 0x0E, + SIMPLE_PAIRING_RANDOMIZER_R = 0x0F, + DEVICE_ID = 0x10, + SECURITY_MANAGER_OOB_FLAGS = 0x11, + SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, + LIST_16BIT_SERVICE_SOLICITATION_UUIDS = 0x14, + LIST_128BIT_SERVICE_SOLICITATION_UUIDS = 0x15, + SERVICE_DATA_16_BIT_UUIDS = 0x16, + PUBLIC_TARGET_ADDRESS = 0x17, + RANDOM_TARGET_ADDRESS = 0x18, + APPEARANCE = 0x19, + ADVERTISING_INTERVAL = 0x1A, + LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B, + LE_ROLE = 0x1C, + SIMPLE_PAIRING_HASH_C_256 = 0x1D, + SIMPLE_PAIRING_RANDOMIZER_R_256 = 0x1E, + LIST_32BIT_SERVICE_SOLICITATION_UUIDS = 0x1F, + SERVICE_DATA_32_BIT_UUIDS = 0x20, + SERVICE_DATA_128_BIT_UUIDS = 0x21, + LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = 0x22, + LE_SECURE_CONNECTIONS_RANDOM_VALUE = 0x23, + URI = 0x24, + INDOOR_POSITIONING = 0x25, + TRANSPORT_DISCOVERY_DATA = 0x26, + LE_SUPPORTED_FEATURES = 0x27, + CHANNEL_MAP_UPDATE_INDICATION = 0x28, + MESH_PB_ADV = 0x29, + MESH_MESSAGE = 0x2A, + MESH_BEACON = 0x2B, + BIG_INFO = 0x2C, + BROADCAST_CODE = 0x2D, + THREE_D_INFORMATION_DATA = 0x3D, + MANUFACTURER_SPECIFIC_DATA = 0xFF, +} + +// ----------------------------------------------------------------------------- +// LE Get Vendor Capabilities Command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities +// ----------------------------------------------------------------------------- + +packet LeGetVendorCapabilities : Command (op_code = LE_GET_VENDOR_CAPABILITIES) { +} + +test LeGetVendorCapabilities { + "\x53\xfd\x00", +} + +struct BaseVendorCapabilities { + max_advt_instances: 8, + offloaded_resolution_of_private_address : 8, + total_scan_results_storage: 16, + max_irk_list_sz: 8, + filtering_support: 8, + max_filter: 8, + activity_energy_info_support: 8, +} + +packet LeGetVendorCapabilitiesComplete : CommandComplete (command_op_code = LE_GET_VENDOR_CAPABILITIES) { + status : ErrorCode, + base_vendor_capabilities : BaseVendorCapabilities, + _payload_, +} + +packet LeGetVendorCapabilitiesComplete095 : LeGetVendorCapabilitiesComplete { + version_supported: 16, + total_num_of_advt_tracked: 16, + extended_scan_support: 8, + debug_logging_supported: 8, + _payload_, +} + +packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 { + le_address_generation_offloading_support: 8, + _payload_, +} + +packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 { + a2dp_source_offload_capability_mask: 32, + bluetooth_quality_report_support: 8 +} + +// ----------------------------------------------------------------------------- +// LE Batch Scan Command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_batch_scan_command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#storage-threshold-breach-subevent +// ----------------------------------------------------------------------------- + +enum BatchScanOpcode : 8 { + ENABLE = 0x01, + SET_STORAGE_PARAMETERS = 0x02, + SET_SCAN_PARAMETERS = 0x03, + READ_RESULT_PARAMETERS = 0x04, +} + +packet LeBatchScan : Command (op_code = LE_BATCH_SCAN) { + batch_scan_opcode : BatchScanOpcode, + _body_, +} + +packet LeBatchScanComplete : CommandComplete (command_op_code = LE_BATCH_SCAN) { + status : ErrorCode, + batch_scan_opcode : BatchScanOpcode, + _body_, +} + +packet LeBatchScanEnable : LeBatchScan (batch_scan_opcode = ENABLE) { + enable : Enable, +} + +packet LeBatchScanEnableComplete : LeBatchScanComplete (batch_scan_opcode = ENABLE) { +} + +packet LeBatchScanSetStorageParameters : LeBatchScan (batch_scan_opcode = SET_STORAGE_PARAMETERS) { + batch_scan_full_max_percentage : 8, + batch_scan_truncated_max_percentage : 8, + batch_scan_notify_threshold_percentage : 8, +} + +packet LeBatchScanSetStorageParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_STORAGE_PARAMETERS) { +} + +enum BatchScanDiscardRule : 8 { + OLDEST = 0x00, + WEAKEST_RSSI = 0x01, +} + +packet LeBatchScanSetScanParameters : LeBatchScan (batch_scan_opcode = SET_SCAN_PARAMETERS) { + truncated_mode_enabled : 1, + full_mode_enabled : 1, + _reserved_ : 6, + duty_cycle_scan_window_slots : 32, + duty_cycle_scan_interval_slots : 32, + own_address_type : PeerAddressType, + batch_scan_discard_rule : BatchScanDiscardRule, +} + +packet LeBatchScanSetScanParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_SCAN_PARAMETERS) { +} + +enum BatchScanDataRead : 8 { + TRUNCATED_MODE_DATA = 0x01, + FULL_MODE_DATA = 0x02, +} + +packet LeBatchScanReadResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) { + batch_scan_data_read : BatchScanDataRead, +} + +packet LeBatchScanReadResultParametersCompleteRaw : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) { + batch_scan_data_read : BatchScanDataRead, + num_of_records : 8, + raw_data : 8[], +} + +packet LeBatchScanReadResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) { + batch_scan_data_read : BatchScanDataRead, + _body_, +} + +struct TruncatedResult { + bd_addr : Address, + address_type : AddressType, + tx_power : 8, + rssi : 8, + timestamp : 16, +} + +packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = TRUNCATED_MODE_DATA) { + _count_(results) : 8, + results : TruncatedResult[], +} + +struct FullResult { + bd_addr : Address, + address_type : AddressType, + tx_power : 8, + rssi : 8, + timestamp : 16, + _size_(adv_packet) : 8, + adv_packet : 8[], + _size_(scan_response) : 8, + scan_response : 8[], +} + +packet LeBatchScanReadFullResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = FULL_MODE_DATA) { + _count_(results) : 8, + results : FullResult[], +} + +packet StorageThresholdBreachEvent : VendorSpecificEvent (subevent_code = STORAGE_THRESHOLD_BREACH) { +} + +// ----------------------------------------------------------------------------- +// Advertising Packet Content Filter (APCF) Command. +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#advertising-packet-content-filter +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-advertisement-tracking-sub-event +// ----------------------------------------------------------------------------- + +enum ApcfOpcode : 8 { + ENABLE = 0x00, + SET_FILTERING_PARAMETERS = 0x01, + BROADCASTER_ADDRESS = 0x02, + SERVICE_UUID = 0x03, + SERVICE_SOLICITATION_UUID = 0x04, + LOCAL_NAME = 0x05, + MANUFACTURER_DATA = 0x06, + SERVICE_DATA = 0x07, + TRANSPORT_DISCOVERY_SERVICE = 0x08, + AD_TYPE_FILTER = 0x09, + READ_EXTENDED_FEATURES = 0xFF, +} + +packet LeApcf : Command (op_code = LE_APCF) { + apcf_opcode : ApcfOpcode, + _payload_, +} + +packet LeApcfComplete : CommandComplete (command_op_code = LE_APCF) { + status : ErrorCode, + apcf_opcode : ApcfOpcode, + _payload_ +} + +packet LeApcfEnable : LeApcf (apcf_opcode = ENABLE) { + apcf_enable : Enable, +} + +packet LeApcfEnableComplete : LeApcfComplete (apcf_opcode = ENABLE) { + apcf_enable : Enable, +} + +enum ApcfAction : 8 { + ADD = 0x00, + DELETE = 0x01, + CLEAR = 0x02, +} + +enum DeliveryMode : 8 { + IMMEDIATE = 0x00, + ONFOUND = 0x01, + BATCHED = 0x02, +} + +// Bit mask for the APCF Feature Selection field. +enum ApcfFeatureSelection : 8 { + BROADCASTER_ADDRESS = 0x00, + SERVICE_DATA_CHANGE = 0x01, + SERVICE_UUID = 0x02, + SERVICE_SOLICITATION_UUID = 0x03, + LOCAL_NAME = 0x04, + MANUFACTURER_DATA = 0x05, + SERVICE_DATA = 0x06, + TRANSPORT_DISCOVERY_DATA = 0x07, + AD_TYPE = 0x08, +} + +packet LeApcfSetFilteringParameters : LeApcf (apcf_opcode = SET_FILTERING_PARAMETERS) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_feature_selection : 16, + apcf_list_logic_type : 16, + apcf_filter_logic_type : 8, + rssi_high_thresh : 8, + delivery_mode : DeliveryMode, + onfound_timeout : 16, + onfound_timeout_cnt : 8, + rssi_low_thresh : 8, + onlost_timeout : 16, + num_of_tracking_entries : 16, +} + +packet LeApcfSetFilteringParametersComplete : LeApcfComplete (apcf_opcode = SET_FILTERING_PARAMETERS) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +enum ApcfApplicationAddressType : 8 { + PUBLIC = 0x00, + RANDOM = 0x01, + NOT_APPLICABLE = 0x02, +} + +packet LeApcfBroadcasterAddress : LeApcf (apcf_opcode = BROADCASTER_ADDRESS) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_broadcaster_address : Address, + apcf_application_address_type : ApcfApplicationAddressType, +} + +packet LeApcfBroadcasterAddressComplete : LeApcfComplete (apcf_opcode = BROADCASTER_ADDRESS) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfServiceUuid : LeApcf (apcf_opcode = SERVICE_UUID) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + acpf_uuid_data : 8[], +} + +packet LeApcfServiceUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_UUID) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfSolicitationUuid : LeApcf (apcf_opcode = SERVICE_SOLICITATION_UUID) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + acpf_uuid_data : 8[], +} + +packet LeApcfSolicitationUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_SOLICITATION_UUID) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfLocalName : LeApcf (apcf_opcode = LOCAL_NAME) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_local_name : 8[], +} + +packet LeApcfLocalNameComplete : LeApcfComplete (apcf_opcode = LOCAL_NAME) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfManufacturerData : LeApcf (apcf_opcode = MANUFACTURER_DATA) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_manufacturer_data : 8[], +} + +packet LeApcfManufacturerDataComplete : LeApcfComplete (apcf_opcode = MANUFACTURER_DATA) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfServiceData : LeApcf (apcf_opcode = SERVICE_DATA) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_service_data : 8[], +} + +packet LeApcfServiceDataComplete : LeApcfComplete (apcf_opcode = SERVICE_DATA) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfADType : LeApcf (apcf_opcode = AD_TYPE_FILTER) { + apcf_action : ApcfAction, + apcf_filter_index : 8, + apcf_ad_type_data : 8[], +} + +packet LeApcfADTypeComplete : LeApcfComplete (apcf_opcode = AD_TYPE_FILTER) { + apcf_action : ApcfAction, + apcf_available_spaces : 8, +} + +packet LeApcfReadExtendedFeatures : LeApcf (apcf_opcode = READ_EXTENDED_FEATURES) { +} + +test LeApcfReadExtendedFeatures { + "\x57\xfd\x01\xff", +} + +packet LeApcfReadExtendedFeaturesComplete : LeApcfComplete (apcf_opcode = READ_EXTENDED_FEATURES) { + transport_discovery_data_filter : 1, + ad_type_filter : 1, + _reserved_ : 14, +} + +test LeApcfReadExtendedFeaturesComplete { + "\x0e\x07\x01\x57\xfd\x00\xff\x03\x00", +} + +enum AdvertiserState : 8 { + ADVERTISER_FOUND = 0x0, + ADVERTISER_LOST = 0x1, +} + +enum AdvtInfoPresent : 8 { + ADVT_INFO_PRESENT = 0x0, + ADVT_INFO_NOT_PRESENT = 0x1, +} + +struct AdvtInfo { + tx_power : 8, + rssi : 8, + timestamp : 16, + _size_(adv_packet) : 8, + adv_packet : 8[], + _size_(scan_data_resp) : 8, + scan_data_resp : 8[], +} + +packet LeAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = LE_ADVERTISEMENT_TRACKING) { + apcf_filter_index : 8, + advertiser_state : AdvertiserState, + advt_info_present : AdvtInfoPresent, + advertiser_address : Address, + advertiser_address_type : PeerAddressType, + advt_info : AdvtInfo[], +} + +// ----------------------------------------------------------------------------- +// LE Get Controller Activity Energy Info Command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_get_controller_activity_energy_info +// ----------------------------------------------------------------------------- + +packet LeGetControllerActivityEnergyInfo : Command (op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) { +} + +packet LeGetControllerActivityEnergyInfoComplete : CommandComplete (command_op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) { + status : ErrorCode, + total_tx_time_ms : 32, + total_rx_time_ms : 32, + total_idle_time_ms : 32, + total_energy_used : 32, +} + +// ----------------------------------------------------------------------------- +// LE Extended Set Scan Parameters Command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-extended-set-scan-parameters-command +// ----------------------------------------------------------------------------- + +enum LeExScanType : 8 { + PASSIVE = 0x0, + ACTIVE = 0x1, +} + +enum LeExScanFilterPolicy : 8 { + ACCEPT_ALL = 0x0, + FILTER_ACCEPT_LIST_ONLY = 0x01, +} + +packet LeExSetScanParameters : Command (op_code = LE_EX_SET_SCAN_PARAMETERS) { + le_ex_scan_type : LeExScanType, + le_ex_scan_interval : 32, + le_ex_scan_window : 32, + own_address_type : OwnAddressType, + le_ex_scan_filter_policy : LeExScanFilterPolicy, +} + +packet LeExSetScanParametersComplete : CommandComplete (command_op_code = LE_EX_SET_SCAN_PARAMETERS) { + status : ErrorCode, +} + +// ----------------------------------------------------------------------------- +// Get Controller Debug Info Command +// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#get-controller-debug-info-command +// ----------------------------------------------------------------------------- + +packet GetControllerDebugInfo : Command (op_code = GET_CONTROLLER_DEBUG_INFO) { +} + +packet GetControllerDebugInfoComplete : CommandComplete (command_op_code = GET_CONTROLLER_DEBUG_INFO) { + status : ErrorCode, +} + +packet ControllerDebugInfoEvent : VendorSpecificEvent (subevent_code = CONTROLLER_DEBUG_INFO) { + debug_block_byte_offset_start : 16, + last_block : 8, + _size_(debug_data) : 16, + debug_data : 8[], +} + +// ----------------------------------------------------------------------------- +// Microsoft Commands +// https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events +// ----------------------------------------------------------------------------- + +enum MsftSubcommandOpcode : 8 { + MSFT_READ_SUPPORTED_FEATURES = 0x00, + MSFT_MONITOR_RSSI = 0x01, + MSFT_CANCEL_MONITOR_RSSI = 0x02, + MSFT_LE_MONITOR_ADV = 0x03, + MSFT_LE_CANCEL_MONITOR_ADV = 0x04, + MSFT_LE_SET_ADV_FILTER_ENABLE = 0x05, + MSFT_READ_ABSOLUTE_RSSI = 0x06, +} + +// MSFT Commands do not have a constant opcode, so leave `op_code` undefined. +packet MsftCommand : Command { + subcommand_opcode: MsftSubcommandOpcode, + _payload_, +} + +packet MsftReadSupportedFeatures : MsftCommand (subcommand_opcode = MSFT_READ_SUPPORTED_FEATURES) {} + +enum MsftLeMonitorAdvConditionType : 8 { + MSFT_CONDITION_TYPE_PATTERNS = 0x01, + MSFT_CONDITION_TYPE_UUID = 0x02, + MSFT_CONDITION_TYPE_IRK_RESOLUTION = 0x03, + MSFT_CONDITION_TYPE_ADDRESS = 0x04, +} + +enum MsftLeMonitorAdvConditionUuidType : 8 { + MSFT_CONDITION_UUID_TYPE_16_BIT = 0x01, + MSFT_CONDITION_UUID_TYPE_32_BIT = 0x02, + MSFT_CONDITION_UUID_TYPE_128_BIT = 0x03, +} + +packet MsftLeMonitorAdv : MsftCommand (subcommand_opcode = MSFT_LE_MONITOR_ADV) { + rssi_threshold_high : 8, + rssi_threshold_low : 8, + rssi_threshold_low_time_interval : 8, + rssi_sampling_period : 8, + condition_type: MsftLeMonitorAdvConditionType, + _payload_, +} + +struct MsftLeMonitorAdvConditionPattern { + _size_(pattern) : 8, // including one byte for ad_type and one byte for start_of_pattern + ad_type: 8, + start_of_pattern: 8, + pattern: 8[+2], +} + +packet MsftLeMonitorAdvConditionPatterns : MsftLeMonitorAdv (condition_type = MSFT_CONDITION_TYPE_PATTERNS) { + _count_(patterns): 8, + patterns: MsftLeMonitorAdvConditionPattern[], +} + +test MsftLeMonitorAdvConditionPatterns { + "\x1e\xfc\x0e\x03\x10\x05\x04\xaa\x01\x01\x06\x03\x00\x80\x81\x82\x83", // 1 pattern + "\x70\xfd\x13\x03\x15\x04\x02\xbb\x01\x02\x04\x03\x00\x80\x81\x06\x0f\x00\x90\x91\x92\x93", // 2 patterns +} + +packet MsftLeMonitorAdvConditionUuid : MsftLeMonitorAdv (condition_type = MSFT_CONDITION_TYPE_UUID) { + uuid_type: MsftLeMonitorAdvConditionUuidType, + _payload_, +} + +packet MsftLeMonitorAdvConditionUuid2 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_16_BIT) { + uuid2: 8[2], +} + +test MsftLeMonitorAdvConditionUuid2 { + "\x1e\xfc\x09\x03\x10\x11\x12\x13\x02\x01\x70\x71", // opcode = fc1e for Intel + "\x70\xfd\x09\x03\x10\x11\x12\x13\x02\x01\x70\x71", // opcode = fd70 for Qualcomm +} + +packet MsftLeMonitorAdvConditionUuid4 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_32_BIT) { + uuid4: 8[4], +} + +test MsftLeMonitorAdvConditionUuid4 { + "\x1e\xfc\x0b\x03\x10\x11\x12\x13\x02\x02\x70\x71\x72\x73", + "\x70\xfd\x0b\x03\x10\x11\x12\x13\x02\x02\x70\x71\x72\x73", +} + +packet MsftLeMonitorAdvConditionUuid16 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_128_BIT) { + uuid16: 8[16], +} + +test MsftLeMonitorAdvConditionUuid16 { + "\x1e\xfc\x17\x03\x10\x11\x12\x13\x02\x03\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f", + "\x70\xfd\x17\x03\x10\x11\x12\x13\x02\x03\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f", +} + +packet MsftLeCancelMonitorAdv: MsftCommand (subcommand_opcode = MSFT_LE_CANCEL_MONITOR_ADV) { + monitor_handle: 8, +} + +test MsftLeCancelMonitorAdv { + "\x1e\xfc\x02\x04\x01", // cancel handle 0x01 +} + +packet MsftLeSetAdvFilterEnable : MsftCommand (subcommand_opcode = MSFT_LE_SET_ADV_FILTER_ENABLE) { + enable: 8, +} + +test MsftLeSetAdvFilterEnable { + "\x1e\xfc\x02\x05\x01", // disable + "\x70\xfd\x02\x05\x01", // enable +} + +packet MsftCommandComplete : CommandComplete { + status: ErrorCode, + subcommand_opcode: MsftSubcommandOpcode, + _payload_, +} + +packet MsftReadSupportedFeaturesCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_READ_SUPPORTED_FEATURES) { + supported_features: 64, + _size_(prefix) : 8, + prefix: 8[], +} + +test MsftReadSupportedFeaturesCommandComplete { + "\x0e\x10\x01\x1e\xfc\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x02\x87\x80", // Msft opcode by Intel + "\x0e\x12\x01\x70\xfd\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x04\x4d\x53\x46\x54", // Msft opcode by Qualcomm +} + +packet MsftLeMonitorAdvCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_MONITOR_ADV) { + monitor_handle: 8, +} + +test MsftLeMonitorAdvCommandComplete { + "\x0e\x06\x01\x1e\xfc\x00\x03\x05", // succeeded + "\x0e\x06\x01\x70\xfd\x01\x03\x06", // failed +} + +packet MsftLeCancelMonitorAdvCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_CANCEL_MONITOR_ADV) {} + +packet MsftLeSetAdvFilterEnableCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_SET_ADV_FILTER_ENABLE) {} + +enum MsftEventCode : 8 { + MSFT_RSSI_EVENT = 0x01, + MSFT_LE_MONITOR_DEVICE_EVENT = 0x02, +} + +enum MsftEventStatus : 8 { + MSFT_EVENT_STATUS_SUCCESS = 0x00, + MSFT_EVENT_STATUS_FAILURE = 0x01, +} + +// It is not possible to define MSFT Event packet by deriving `Event` packet +// because it starts with variable-length event prefix which can only be determined +// at run-time (after receiving return of MSFT Read Supported Features). +// Therefore we only define the payload which is located after the event prefix. +packet MsftEventPayload { + msft_event_code : MsftEventCode, + _payload_, +} + +packet MsftRssiEventPayload : MsftEventPayload (msft_event_code = MSFT_RSSI_EVENT) { + status: MsftEventStatus, + connection_handle: 16, + rssi: 8, +} + +test MsftRssiEventPayload { + "\x01\x00\x01\x10\xf0", // MSFT_RSSI_EVENT succeeded + "\x01\x01\x02\x02\x08", // MSFT_RSSI_EVENT failed +} + +packet MsftLeMonitorDeviceEventPayload : MsftEventPayload (msft_event_code = MSFT_LE_MONITOR_DEVICE_EVENT) { + address_type: 8, + bd_addr: Address, + monitor_handle: 8, + monitor_state: 8, +} + +test MsftLeMonitorDeviceEventPayload { + "\x02\x01\x00\x01\x02\x03\x04\x05\x10\x00", + "\x02\x02\xf0\xf1\xf2\xf3\xf4\xf5\xaa\x02", +} \ No newline at end of file diff --git a/rust/src/internal/hci/tests.rs b/rust/src/internal/hci/tests.rs new file mode 100644 index 0000000..7962c88 --- /dev/null +++ b/rust/src/internal/hci/tests.rs @@ -0,0 +1,94 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::internal::hci::{ + packets::{Event, EventBuilder, EventCode, Sco}, + parse_with_expected_packet_type, prepend_packet_type, Error, Packet, PacketType, + PacketTypeParseError, WithPacketType, +}; +use bytes::Bytes; + +#[test] +fn prepends_packet_type() { + let packet_type = PacketType::Event; + let packet_bytes = vec![0x00, 0x00, 0x00, 0x00]; + let actual = prepend_packet_type(packet_type, packet_bytes); + assert_eq!(vec![0x04, 0x00, 0x00, 0x00, 0x00], actual); +} + +#[test] +fn parse_empty_slice_should_error() { + let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Event, &[]); + assert_eq!(Err(PacketTypeParseError::EmptySlice), actual); +} + +#[test] +fn parse_invalid_packet_type_should_error() { + let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Event, &[0xFF]); + assert_eq!( + Err(PacketTypeParseError::InvalidPacketType { value: 0xFF }), + actual + ); +} + +#[test] +fn parse_mismatched_packet_type_should_error() { + let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Acl, &[0x01]); + assert_eq!( + Err(PacketTypeParseError::PacketTypeMismatch { + expected: PacketType::Acl, + actual: PacketType::Command + }), + actual + ); +} + +#[test] +fn parse_invalid_packet_should_error() { + let actual = parse_with_expected_packet_type(Sco::parse, PacketType::Sco, &[0x03]); + assert!(actual.is_err()); +} + +#[test] +fn test_packet_roundtrip_with_type() { + let event_packet = EventBuilder { + event_code: EventCode::InquiryComplete, + payload: None, + } + .build(); + let event_packet_bytes = event_packet.clone().to_vec_with_packet_type(); + let actual = + parse_with_expected_packet_type(Event::parse, PacketType::Event, &event_packet_bytes) + .unwrap(); + assert_eq!(event_packet, actual); +} + +#[derive(Debug, PartialEq)] +struct FakePacket; + +impl FakePacket { + fn parse(_bytes: &[u8]) -> Result { + Ok(Self) + } +} + +impl Packet for FakePacket { + fn to_bytes(self) -> Bytes { + Bytes::new() + } + + fn to_vec(self) -> Vec { + Vec::new() + } +} diff --git a/rust/src/internal/mod.rs b/rust/src/internal/mod.rs index f474c2d..9514ed6 100644 --- a/rust/src/internal/mod.rs +++ b/rust/src/internal/mod.rs @@ -18,3 +18,4 @@ //! to discover. pub(crate) mod drivers; +pub(crate) mod hci; diff --git a/rust/src/wrapper/common.rs b/rust/src/wrapper/common.rs new file mode 100644 index 0000000..4947560 --- /dev/null +++ b/rust/src/wrapper/common.rs @@ -0,0 +1,34 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared resources found under bumble's common.py +use pyo3::{PyObject, Python, ToPyObject}; + +/// Represents the sink for some transport mechanism +pub struct TransportSink(pub(crate) PyObject); + +impl ToPyObject for TransportSink { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.0.clone() + } +} + +/// Represents the source for some transport mechanism +pub struct TransportSource(pub(crate) PyObject); + +impl ToPyObject for TransportSource { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.0.clone() + } +} diff --git a/rust/src/wrapper/controller.rs b/rust/src/wrapper/controller.rs new file mode 100644 index 0000000..4f19dd6 --- /dev/null +++ b/rust/src/wrapper/controller.rs @@ -0,0 +1,66 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Controller components +use crate::wrapper::{ + common::{TransportSink, TransportSource}, + hci::Address, + link::Link, + wrap_python_async, PyDictExt, +}; +use pyo3::{ + intern, + types::{PyDict, PyModule}, + PyObject, PyResult, Python, +}; +use pyo3_asyncio::tokio::into_future; + +/// A controller that can send and receive HCI frames via some link +#[derive(Clone)] +pub struct Controller(pub(crate) PyObject); + +impl Controller { + /// Creates a new [Controller] object. When optional arguments are not specified, the Python + /// module specifies the defaults. Must be called from a thread with a Python event loop, which + /// should be true on `tokio::main` and `async_std::main`. + /// + /// For more info, see https://awestlake87.github.io/pyo3-asyncio/master/doc/pyo3_asyncio/#event-loop-references-and-contextvars. + pub async fn new( + name: &str, + host_source: Option, + host_sink: Option, + link: Option, + public_address: Option
, + ) -> PyResult { + Python::with_gil(|py| { + let controller_ctr = PyModule::import(py, intern!(py, "bumble.controller"))? + .getattr(intern!(py, "Controller"))?; + + let kwargs = PyDict::new(py); + kwargs.set_item("name", name)?; + kwargs.set_opt_item("host_source", host_source)?; + kwargs.set_opt_item("host_sink", host_sink)?; + kwargs.set_opt_item("link", link)?; + kwargs.set_opt_item("public_address", public_address)?; + + // Controller constructor (`__init__`) is not (and can't be) marked async, but calls + // `get_running_loop`, and thus needs wrapped in an async function. + wrap_python_async(py, controller_ctr)? + .call((), Some(kwargs)) + .and_then(into_future) + })? + .await + .map(Self) + } +} diff --git a/rust/src/wrapper/device.rs b/rust/src/wrapper/device.rs index be5e4fa..6bf958a 100644 --- a/rust/src/wrapper/device.rs +++ b/rust/src/wrapper/device.rs @@ -14,12 +14,16 @@ //! Devices and connections to them +use crate::internal::hci::WithPacketType; use crate::{ adv::AdvertisementDataBuilder, wrapper::{ core::AdvertisingData, gatt_client::{ProfileServiceProxy, ServiceProxy}, - hci::{Address, HciErrorCode}, + hci::{ + packets::{Command, ErrorCode, Event}, + Address, HciCommandWrapper, + }, host::Host, l2cap::LeConnectionOrientedChannel, transport::{Sink, Source}, @@ -27,18 +31,73 @@ use crate::{ }, }; use pyo3::{ + exceptions::PyException, intern, types::{PyDict, PyModule}, - IntoPy, PyObject, PyResult, Python, ToPyObject, + IntoPy, PyErr, PyObject, PyResult, Python, ToPyObject, }; use pyo3_asyncio::tokio::into_future; use std::path; +/// Represents the various properties of some device +pub struct DeviceConfiguration(PyObject); + +impl DeviceConfiguration { + /// Creates a new configuration, letting the internal Python object set all the defaults + pub fn new() -> PyResult { + Python::with_gil(|py| { + PyModule::import(py, intern!(py, "bumble.device"))? + .getattr(intern!(py, "DeviceConfiguration"))? + .call0() + .map(|any| Self(any.into())) + }) + } + + /// Creates a new configuration from the specified file + pub fn load_from_file(&mut self, device_config: &path::Path) -> PyResult<()> { + Python::with_gil(|py| { + self.0 + .call_method1(py, intern!(py, "load_from_file"), (device_config,)) + }) + .map(|_| ()) + } +} + +impl ToPyObject for DeviceConfiguration { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.0.clone() + } +} + /// A device that can send/receive HCI frames. #[derive(Clone)] pub struct Device(PyObject); impl Device { + /// Creates a Device. When optional arguments are not specified, the Python object specifies the + /// defaults. + pub fn new( + name: Option<&str>, + address: Option
, + config: Option, + host: Option, + generic_access_service: Option, + ) -> PyResult { + Python::with_gil(|py| { + let kwargs = PyDict::new(py); + kwargs.set_opt_item("name", name)?; + kwargs.set_opt_item("address", address)?; + kwargs.set_opt_item("config", config)?; + kwargs.set_opt_item("host", host)?; + kwargs.set_opt_item("generic_access_service", generic_access_service)?; + + PyModule::import(py, intern!(py, "bumble.device"))? + .getattr(intern!(py, "Device"))? + .call((), Some(kwargs)) + .map(|any| Self(any.into())) + }) + } + /// Create a Device per the provided file configured to communicate with a controller through an HCI source/sink pub fn from_config_file_with_hci( device_config: &path::Path, @@ -66,6 +125,29 @@ impl Device { }) } + /// Sends an HCI command on this Device, returning the command's event result. + pub async fn send_command(&self, command: &Command, check_result: bool) -> PyResult { + Python::with_gil(|py| { + self.0 + .call_method1( + py, + intern!(py, "send_command"), + (HciCommandWrapper(command.clone()), check_result), + ) + .and_then(|coroutine| into_future(coroutine.as_ref(py))) + })? + .await + .and_then(|event| { + Python::with_gil(|py| { + let py_bytes = event.call_method0(py, intern!(py, "__bytes__"))?; + let bytes: &[u8] = py_bytes.extract(py)?; + let event = Event::parse_with_packet_type(bytes) + .map_err(|e| PyErr::new::(e.to_string()))?; + Ok(event) + }) + }) + } + /// Turn the device on pub async fn power_on(&self) -> PyResult<()> { Python::with_gil(|py| { @@ -236,7 +318,7 @@ impl Connection { kwargs.set_opt_item("mps", mps)?; self.0 .call_method(py, intern!(py, "open_l2cap_channel"), (), Some(kwargs)) - .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py))) + .and_then(|coroutine| into_future(coroutine.as_ref(py))) })? .await .map(LeConnectionOrientedChannel::from) @@ -244,13 +326,13 @@ impl Connection { /// Disconnect from device with provided reason. When optional arguments are not specified, the /// Python module specifies the defaults. - pub async fn disconnect(&mut self, reason: Option) -> PyResult<()> { + pub async fn disconnect(&mut self, reason: Option) -> PyResult<()> { Python::with_gil(|py| { let kwargs = PyDict::new(py); kwargs.set_opt_item("reason", reason)?; self.0 .call_method(py, intern!(py, "disconnect"), (), Some(kwargs)) - .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py))) + .and_then(|coroutine| into_future(coroutine.as_ref(py))) })? .await .map(|_| ()) @@ -259,7 +341,7 @@ impl Connection { /// Register a callback to be called on disconnection. pub fn on_disconnection( &mut self, - callback: impl Fn(Python, HciErrorCode) -> PyResult<()> + Send + 'static, + callback: impl Fn(Python, ErrorCode) -> PyResult<()> + Send + 'static, ) -> PyResult<()> { let boxed = ClosureCallback::new(move |py, args, _kwargs| { callback(py, args.get_item(0)?.extract()?) diff --git a/rust/src/wrapper/hci.rs b/rust/src/wrapper/hci.rs index 41dcbf3..b029a65 100644 --- a/rust/src/wrapper/hci.rs +++ b/rust/src/wrapper/hci.rs @@ -14,84 +14,62 @@ //! HCI +pub use crate::internal::hci::{packets, Error, Packet}; + +use crate::{ + internal::hci::WithPacketType, + wrapper::hci::packets::{AddressType, Command, ErrorCode}, +}; use itertools::Itertools as _; use pyo3::{ - exceptions::PyException, intern, types::PyModule, FromPyObject, PyAny, PyErr, PyObject, - PyResult, Python, ToPyObject, + exceptions::PyException, + intern, pyclass, pymethods, + types::{PyBytes, PyModule}, + FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject, }; -/// HCI error code. -pub struct HciErrorCode(u8); - -impl<'source> FromPyObject<'source> for HciErrorCode { - fn extract(ob: &'source PyAny) -> PyResult { - Ok(HciErrorCode(ob.extract()?)) - } -} - -impl ToPyObject for HciErrorCode { - fn to_object(&self, py: Python<'_>) -> PyObject { - self.0.to_object(py) - } -} - /// Provides helpers for interacting with HCI pub struct HciConstant; impl HciConstant { /// Human-readable error name - pub fn error_name(status: HciErrorCode) -> PyResult { + pub fn error_name(status: ErrorCode) -> PyResult { Python::with_gil(|py| { PyModule::import(py, intern!(py, "bumble.hci"))? .getattr(intern!(py, "HCI_Constant"))? - .call_method1(intern!(py, "error_name"), (status.0,))? + .call_method1(intern!(py, "error_name"), (status.to_object(py),))? .extract() }) } } /// A Bluetooth address +#[derive(Clone)] pub struct Address(pub(crate) PyObject); impl Address { + /// Creates a new [Address] object + pub fn new(address: &str, address_type: &AddressType) -> PyResult { + Python::with_gil(|py| { + PyModule::import(py, intern!(py, "bumble.device"))? + .getattr(intern!(py, "Address"))? + .call1((address, address_type.to_object(py))) + .map(|any| Self(any.into())) + }) + } + /// The type of address pub fn address_type(&self) -> PyResult { Python::with_gil(|py| { - let addr_type = self - .0 + self.0 .getattr(py, intern!(py, "address_type"))? - .extract::(py)?; - - let module = PyModule::import(py, intern!(py, "bumble.hci"))?; - let klass = module.getattr(intern!(py, "Address"))?; - - if addr_type - == klass - .getattr(intern!(py, "PUBLIC_DEVICE_ADDRESS"))? - .extract::()? - { - Ok(AddressType::PublicDevice) - } else if addr_type - == klass - .getattr(intern!(py, "RANDOM_DEVICE_ADDRESS"))? - .extract::()? - { - Ok(AddressType::RandomDevice) - } else if addr_type - == klass - .getattr(intern!(py, "PUBLIC_IDENTITY_ADDRESS"))? - .extract::()? - { - Ok(AddressType::PublicIdentity) - } else if addr_type - == klass - .getattr(intern!(py, "RANDOM_IDENTITY_ADDRESS"))? - .extract::()? - { - Ok(AddressType::RandomIdentity) - } else { - Err(PyErr::new::("Invalid address type")) - } + .extract::(py)? + .try_into() + .map_err(|addr_type| { + PyErr::new::(format!( + "Failed to convert {addr_type} to AddressType" + )) + }) }) } @@ -134,12 +112,45 @@ impl Address { } } -/// BT address types -#[allow(missing_docs)] -#[derive(PartialEq, Eq, Debug)] -pub enum AddressType { - PublicDevice, - RandomDevice, - PublicIdentity, - RandomIdentity, +impl ToPyObject for Address { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.0.clone() + } +} + +/// Implements minimum necessary interface to be treated as bumble's [HCI_Command]. +/// While pyo3's macros do not support generics, this could probably be refactored to allow multiple +/// implementations of the HCI_Command methods in the future, if needed. +#[pyclass] +pub(crate) struct HciCommandWrapper(pub(crate) Command); + +#[pymethods] +impl HciCommandWrapper { + fn __bytes__(&self, py: Python) -> PyResult { + let bytes = PyBytes::new(py, &self.0.clone().to_vec_with_packet_type()); + Ok(bytes.into_py(py)) + } + + #[getter] + fn op_code(&self) -> u16 { + self.0.get_op_code().into() + } +} + +impl ToPyObject for AddressType { + fn to_object(&self, py: Python<'_>) -> PyObject { + u8::from(self).to_object(py) + } +} + +impl<'source> FromPyObject<'source> for ErrorCode { + fn extract(ob: &'source PyAny) -> PyResult { + ob.extract() + } +} + +impl ToPyObject for ErrorCode { + fn to_object(&self, py: Python<'_>) -> PyObject { + u8::from(self).to_object(py) + } } diff --git a/rust/src/wrapper/host.rs b/rust/src/wrapper/host.rs index ab81450..8295664 100644 --- a/rust/src/wrapper/host.rs +++ b/rust/src/wrapper/host.rs @@ -14,8 +14,12 @@ //! Host-side types -use crate::wrapper::transport::{Sink, Source}; -use pyo3::{intern, prelude::PyModule, types::PyDict, PyObject, PyResult, Python}; +use crate::wrapper::{ + transport::{Sink, Source}, + wrap_python_async, +}; +use pyo3::{intern, prelude::PyModule, types::PyDict, PyObject, PyResult, Python, ToPyObject}; +use pyo3_asyncio::tokio::into_future; /// Host HCI commands pub struct Host { @@ -29,13 +33,23 @@ impl Host { } /// Create a new Host - pub fn new(source: Source, sink: Sink) -> PyResult { + pub async fn new(source: Source, sink: Sink) -> PyResult { Python::with_gil(|py| { - PyModule::import(py, intern!(py, "bumble.host"))? - .getattr(intern!(py, "Host"))? - .call((source.0, sink.0), None) - .map(|any| Self { obj: any.into() }) - }) + let host_ctr = + PyModule::import(py, intern!(py, "bumble.host"))?.getattr(intern!(py, "Host"))?; + + let kwargs = PyDict::new(py); + kwargs.set_item("controller_source", source.0)?; + kwargs.set_item("controller_sink", sink.0)?; + + // Needed for Python 3.8-3.9, in which the Semaphore object, when constructed, calls + // `get_event_loop`. + wrap_python_async(py, host_ctr)? + .call((), Some(kwargs)) + .and_then(into_future) + })? + .await + .map(|any| Self { obj: any }) } /// Send a reset command and perform other reset tasks. @@ -61,6 +75,12 @@ impl Host { } } +impl ToPyObject for Host { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.obj.clone() + } +} + /// Driver factory to use when initializing a host #[derive(Debug, Clone)] pub enum DriverFactory { diff --git a/rust/src/wrapper/link.rs b/rust/src/wrapper/link.rs new file mode 100644 index 0000000..7169ef5 --- /dev/null +++ b/rust/src/wrapper/link.rs @@ -0,0 +1,38 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Link components +use pyo3::{intern, types::PyModule, PyObject, PyResult, Python, ToPyObject}; + +/// Link bus for controllers to communicate with each other +#[derive(Clone)] +pub struct Link(pub(crate) PyObject); + +impl Link { + /// Creates a [Link] object that transports messages locally + pub fn new_local_link() -> PyResult { + Python::with_gil(|py| { + PyModule::import(py, intern!(py, "bumble.link"))? + .getattr(intern!(py, "LocalLink"))? + .call0() + .map(|any| Self(any.into())) + }) + } +} + +impl ToPyObject for Link { + fn to_object(&self, _py: Python<'_>) -> PyObject { + self.0.clone() + } +} diff --git a/rust/src/wrapper/mod.rs b/rust/src/wrapper/mod.rs index 94ac15a..27b86d9 100644 --- a/rust/src/wrapper/mod.rs +++ b/rust/src/wrapper/mod.rs @@ -22,13 +22,17 @@ // Re-exported to make it easy for users to depend on the same `PyObject`, etc pub use pyo3; +pub use pyo3_asyncio; + use pyo3::{ + intern, prelude::*, types::{PyDict, PyTuple}, }; -pub use pyo3_asyncio; pub mod assigned_numbers; +pub mod common; +pub mod controller; pub mod core; pub mod device; pub mod drivers; @@ -36,6 +40,7 @@ pub mod gatt_client; pub mod hci; pub mod host; pub mod l2cap; +pub mod link; pub mod logging; pub mod profile; pub mod transport; @@ -119,3 +124,11 @@ impl ClosureCallback { (self.callback)(py, args, kwargs).map(|_| py.None()) } } + +/// Wraps the Python function in a Python async function. `pyo3_asyncio` needs functions to be +/// marked async to properly inject a running loop. +pub(crate) fn wrap_python_async<'a>(py: Python<'a>, function: &'a PyAny) -> PyResult<&'a PyAny> { + PyModule::import(py, intern!(py, "bumble.utils"))? + .getattr(intern!(py, "wrap_async"))? + .call1((function,)) +} diff --git a/rust/src/wrapper/transport.rs b/rust/src/wrapper/transport.rs index 6c9468d..a7ec9e9 100644 --- a/rust/src/wrapper/transport.rs +++ b/rust/src/wrapper/transport.rs @@ -14,6 +14,7 @@ //! HCI packet transport +use crate::wrapper::controller::Controller; use pyo3::{intern, types::PyModule, PyObject, PyResult, Python}; /// A source/sink pair for HCI packet I/O. @@ -67,6 +68,18 @@ impl Drop for Transport { #[derive(Clone)] pub struct Source(pub(crate) PyObject); +impl From for Source { + fn from(value: Controller) -> Self { + Self(value.0) + } +} + /// The sink side of a [Transport]. #[derive(Clone)] pub struct Sink(pub(crate) PyObject); + +impl From for Sink { + fn from(value: Controller) -> Self { + Self(value.0) + } +}