├── clippy.toml ├── .gitignore ├── src ├── types │ ├── mod.rs │ ├── expected_connection_length.rs │ ├── advertisement.rs │ ├── scan_window.rs │ ├── common.rs │ ├── advertising_interval.rs │ └── connection_interval.rs ├── host │ ├── cmd_link.rs │ ├── event_link.rs │ └── uart.rs ├── opcode.rs ├── bitflag_array.rs └── lib.rs ├── .travis.yml ├── Cargo.toml ├── LICENSE-MIT ├── tests ├── expected_connection_length.rs ├── advertisement.rs ├── scan_window.rs ├── vendor │ └── mod.rs ├── advertising_interval.rs ├── connection_interval.rs ├── event.rs ├── host.rs └── command_complete.rs ├── README.md └── LICENSE-APACHE /clippy.toml: -------------------------------------------------------------------------------- 1 | doc-valid-idents = ["STMicro", "BlueNRG"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common types for Bluetooth commands and events. 2 | 3 | mod advertisement; 4 | mod advertising_interval; 5 | mod common; 6 | mod connection_interval; 7 | mod expected_connection_length; 8 | mod scan_window; 9 | 10 | pub use self::advertisement::*; 11 | pub use self::advertising_interval::*; 12 | pub use self::common::*; 13 | pub use self::connection_interval::*; 14 | pub use self::expected_connection_length::*; 15 | pub use self::scan_window::*; 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | install: rustup target add thumbv7em-none-eabihf 7 | script: 8 | - cargo build --verbose --all --target=thumbv7em-none-eabihf 9 | - cargo test --verbose --all 10 | - cargo test --verbose --all --no-default-features --features="version-4-1" 11 | - cargo test --verbose --all --no-default-features --features="version-4-2" 12 | - cargo test --verbose --all --no-default-features --features="version-5-0" 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | name = "bluetooth-hci" 4 | version = "0.1.1" 5 | authors = ["Daniel Gallagher "] 6 | categories = ["embedded", "hardware-support", "no-std"] 7 | description = "Implementation of the Bluetooth HCI" 8 | keywords = ["ble", "bluetooth"] 9 | license = "MIT/Apache-2.0" 10 | repository = "https://github.com/danielgallagher0/bluetooth-hci" 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | nb = "0.1.2" 15 | bitflags = "1.2" 16 | 17 | [dependencies.byteorder] 18 | version = "1" 19 | default-features = false 20 | 21 | [features] 22 | default = ["version-4-1"] 23 | version-4-1 = [] 24 | version-4-2 = [] 25 | version-5-0 = [] 26 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2020 Daniel Gallagher 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/host/cmd_link.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the HCI that includes the packet ID byte in the header. 2 | //! 3 | //! This was originally written just based on wording from the Bluetooth spec (version 5.0, Vol 4, 4 | //! Part A, section 2), emphasis added: 5 | //! 6 | //! > Therefore, *if* the HCI packets are sent via a common physical interface, a HCI 7 | //! > packet indicator has to be added according to Table 2.1 below. 8 | //! 9 | //! However, there don't seem to be any implementations where the HCI packets are _not_ sent "via a 10 | //! common physical interface", so this module may be unnecessary. 11 | 12 | use byteorder::{ByteOrder, LittleEndian}; 13 | 14 | /// Header for HCI Commands. 15 | pub struct Header { 16 | opcode: crate::opcode::Opcode, 17 | param_len: u8, 18 | } 19 | 20 | impl super::HciHeader for Header { 21 | const HEADER_LENGTH: usize = 3; 22 | 23 | fn new(opcode: crate::opcode::Opcode, param_len: usize) -> Header { 24 | Header { 25 | opcode, 26 | param_len: param_len as u8, 27 | } 28 | } 29 | 30 | fn copy_into_slice(&self, buffer: &mut [u8]) { 31 | LittleEndian::write_u16(buffer, self.opcode.0); 32 | buffer[2] = self.param_len; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/expected_connection_length.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | use hci::types::{ExpectedConnectionLength, ExpectedConnectionLengthError}; 4 | use std::time::Duration; 5 | 6 | #[test] 7 | fn valid() { 8 | let range = 9 | ExpectedConnectionLength::new(Duration::from_millis(200), Duration::from_millis(500)) 10 | .unwrap(); 11 | let mut bytes = [0; 4]; 12 | range.copy_into_slice(&mut bytes); 13 | assert_eq!(bytes, [0x40, 0x01, 0x20, 0x03]); 14 | } 15 | 16 | #[test] 17 | fn interval_too_long() { 18 | let err = ExpectedConnectionLength::new( 19 | Duration::from_millis(200), 20 | Duration::from_micros(40_959_376), 21 | ) 22 | .err() 23 | .unwrap(); 24 | assert_eq!( 25 | err, 26 | ExpectedConnectionLengthError::TooLong(Duration::from_micros(40_959_376)) 27 | ); 28 | } 29 | 30 | #[test] 31 | fn inverted() { 32 | let err = ExpectedConnectionLength::new(Duration::from_millis(400), Duration::from_millis(399)) 33 | .err() 34 | .unwrap(); 35 | assert_eq!( 36 | err, 37 | ExpectedConnectionLengthError::Inverted( 38 | Duration::from_millis(400), 39 | Duration::from_millis(399) 40 | ) 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /tests/advertisement.rs: -------------------------------------------------------------------------------- 1 | use hci::types::Advertisement; 2 | 3 | extern crate bluetooth_hci as hci; 4 | 5 | #[test] 6 | fn complete_name() { 7 | // Example advertising data - complete local name 8 | let expected = [ 9 | 0x0a, 0x09, 0x50, 0x65, 0x64, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 10 | ]; 11 | let adv = Advertisement::CompleteLocalName("Pedometer"); 12 | 13 | let mut o = [0; 31]; 14 | let l = adv.copy_into_slice(&mut o); 15 | assert_eq!(expected.len(), l); 16 | assert_eq!(expected, o[..l]); 17 | } 18 | 19 | #[test] 20 | fn ibeacon() { 21 | let expected = [ 22 | 0x1a, 0xff, 0x4c, 0x0, 0x2, 0x15, 0xfb, 0xb, 0x57, 0xa2, 0x82, 0x28, 0x44, 0xcd, 0x91, 23 | 0x3a, 0x94, 0xa1, 0x22, 0xba, 0x12, 0x6, 0x0, 0x1, 0x0, 0x2, 0xd1, 24 | ]; 25 | let payload = [ 26 | 0x2, 0x15, 0xfb, 0xb, 0x57, 0xa2, 0x82, 0x28, 0x44, 0xcd, 0x91, 0x3a, 0x94, 0xa1, 0x22, 27 | 0xba, 0x12, 0x6, 0x0, 0x1, 0x0, 0x2, 0xd1, 28 | ]; 29 | let adv = Advertisement::ManufacturerSpecificData(0x4c, &payload); 30 | 31 | let mut o = [0; 31]; 32 | let l = adv.copy_into_slice(&mut o); 33 | assert_eq!(expected.len(), l); 34 | assert_eq!(expected, o[..l]); 35 | } 36 | 37 | #[test] 38 | fn eddystone() { 39 | let expected = [ 40 | 0x10, 0x16, 0xaa, 0xfe, 0x10, 0x0, 0x01, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 41 | 0x67, 0x01, 42 | ]; 43 | // URL(0x10), 0, https://www. (0x01), "rust-lang", .org/ (0x01) 44 | let payload = b"\x10\x00\x01rust-lang\x01"; 45 | let adv = Advertisement::ServiceData16BitUuid(0xfeaa, payload); 46 | let mut o = [0; 31]; 47 | let l = adv.copy_into_slice(&mut o); 48 | assert_eq!(expected.len(), l); 49 | assert_eq!(expected, o[..l]); 50 | } 51 | -------------------------------------------------------------------------------- /tests/scan_window.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | use hci::types::{ScanWindow, ScanWindowError}; 4 | use std::time::Duration; 5 | 6 | #[test] 7 | fn valid() { 8 | let scan_window = ScanWindow::start_every(Duration::from_millis(10)) 9 | .unwrap() 10 | .open_for(Duration::from_millis(5)) 11 | .unwrap(); 12 | assert_eq!(scan_window.interval(), Duration::from_millis(10)); 13 | assert_eq!(scan_window.window(), Duration::from_millis(5)); 14 | } 15 | 16 | #[test] 17 | fn interval_too_short() { 18 | let err = ScanWindow::start_every(Duration::from_millis(2)) 19 | .err() 20 | .unwrap(); 21 | assert_eq!(err, ScanWindowError::TooShort(Duration::from_millis(2))); 22 | } 23 | 24 | #[test] 25 | fn interval_too_long() { 26 | let err = ScanWindow::start_every(Duration::from_millis(10241)) 27 | .err() 28 | .unwrap(); 29 | assert_eq!(err, ScanWindowError::TooLong(Duration::from_millis(10241))); 30 | } 31 | 32 | #[test] 33 | fn window_too_short() { 34 | let err = ScanWindow::start_every(Duration::from_millis(10)) 35 | .unwrap() 36 | .open_for(Duration::from_millis(2)) 37 | .err() 38 | .unwrap(); 39 | assert_eq!(err, ScanWindowError::TooShort(Duration::from_millis(2))); 40 | } 41 | 42 | #[test] 43 | fn inverted() { 44 | let err = ScanWindow::start_every(Duration::from_millis(100)) 45 | .unwrap() 46 | .open_for(Duration::from_millis(101)) 47 | .err() 48 | .unwrap(); 49 | assert_eq!( 50 | err, 51 | ScanWindowError::Inverted { 52 | interval: Duration::from_millis(100), 53 | window: Duration::from_millis(101), 54 | } 55 | ); 56 | } 57 | 58 | #[test] 59 | fn inverted_and_window_too_long() { 60 | let err = ScanWindow::start_every(Duration::from_millis(100)) 61 | .unwrap() 62 | .open_for(Duration::from_millis(10241)) 63 | .err() 64 | .unwrap(); 65 | assert_eq!( 66 | err, 67 | ScanWindowError::Inverted { 68 | interval: Duration::from_millis(100), 69 | window: Duration::from_millis(10241), 70 | } 71 | ); 72 | } 73 | 74 | #[test] 75 | fn copy_into_slice() { 76 | let scan_window = ScanWindow::start_every(Duration::from_millis(10)) 77 | .unwrap() 78 | .open_for(Duration::from_millis(5)) 79 | .unwrap(); 80 | let mut bytes = [0; 4]; 81 | scan_window.copy_into_slice(&mut bytes); 82 | assert_eq!(bytes, [0x10, 0x00, 0x08, 0x00]); 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth HCI 2 | 3 | [![Build 4 | Status](https://travis-ci.org/danielgallagher0/bluetooth-hci.svg?branch=master)](https://travis-ci.org/danielgallagher0/bluetooth-hci) 5 | 6 | This crate defines a pure Rust implementation of the Bluetooth 7 | Host-Controller Interface for bare metal devices. It defines commands 8 | and events from the specification, and requires specific chips to 9 | define vendor-specific commands and events. 10 | 11 | ## Version 12 | 13 | This crate can support versions 4.1, 4.2, and 5.0 of the Bluetooth 14 | specification. By default, it supports version 4.1. To enable another 15 | version, add the following to your `Cargo.toml`: 16 | 17 | [dependencies.bluetooth-hci] 18 | features = "version-4-2" 19 | 20 | or 21 | 22 | [dependencies.bluetooth-hci] 23 | features = "version-5-0" 24 | 25 | ## Implementation 26 | 27 | This crate defines a trait (`Controller`) that should be implemented 28 | for a specific BLE chip. Any implementor can then be used as a 29 | `host::uart::Hci` to read and write to the chip. 30 | 31 | impl bluetooth_hci::Controller for MyController { 32 | type Error = BusError; 33 | type Header = bluetooth_hci::host::uart::CommandHeader; 34 | fn write(&mut self, header: &[u8], payload: &[u8]) -> 35 | nb::Result<(), Self::Error> { 36 | // implementation... 37 | } 38 | fn read_into(&mut self, buffer: &mut [u8]) -> 39 | nb::Result<(), Self::Error> { 40 | // implementation... 41 | } 42 | fn peek(&mut self, n: usize) -> nb::Result { 43 | // implementation... 44 | } 45 | } 46 | 47 | The entire Bluetooth HCI is implemented in terms of these functions 48 | that handle the low-level I/O. To read events, you can use the 49 | `host::uart::Hci` trait, which defines a `read` function. The easiest 50 | way to specify the vendor-specific event type is via type inference: 51 | 52 | fn process_event(e: hci::event::Event) { 53 | // do stuff with e 54 | } 55 | // elsewhere... 56 | process_event(controller.read()?) 57 | 58 | ## Supported Commands and Events 59 | 60 | This crate contains only partial support for commands and events right 61 | now. The only commands and events (as of September 2018) are those 62 | used by the [BlueNRG](https://github.com/danielgallagher0/bluenrg) 63 | chip. Support for HCI ACL Data Packets and HCI Synchronous Data 64 | Packets still needs to be determined. 65 | 66 | See the [Bluetooth 67 | Specification](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=421043) 68 | for more (many, many more) details on what this crate should 69 | eventually support. Volume 2, Part E, section 7 is the most relevant 70 | portion for this crate. 71 | -------------------------------------------------------------------------------- /src/types/expected_connection_length.rs: -------------------------------------------------------------------------------- 1 | //! Types related to the expected connection length range. 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use core::time::Duration; 5 | 6 | /// Define an expected connection length range 7 | /// 8 | /// There is no minimum. The maximum is bounded by what is representable as a u16 at T = N * 0.625 9 | /// ms, so max = 65535 * 0.625 ms = 40.959375 seconds. 10 | #[derive(Clone, Debug)] 11 | pub struct ExpectedConnectionLength { 12 | range: (Duration, Duration), 13 | } 14 | 15 | impl ExpectedConnectionLength { 16 | /// Creates a new ExpectedConnectionLength, or returns an error if the duration is invalid. 17 | /// 18 | /// # Errors 19 | /// 20 | /// - [Inverted](ExpectedConnectionLengthError::Inverted) if `min` is greater than `max` 21 | /// - [TooLong](ExpectedConnectionLengthError::TooLong) if `max` is longer than 40.959375 22 | /// seconds. 23 | pub fn new( 24 | min: Duration, 25 | max: Duration, 26 | ) -> Result { 27 | if min > max { 28 | return Err(ExpectedConnectionLengthError::Inverted(min, max)); 29 | } 30 | 31 | const ABSOLUTE_MAX: Duration = Duration::from_micros(40_959_375); 32 | assert_eq!(Self::duration_as_u16(ABSOLUTE_MAX), 0xFFFF); 33 | if max > ABSOLUTE_MAX { 34 | return Err(ExpectedConnectionLengthError::TooLong(max)); 35 | } 36 | 37 | Ok(ExpectedConnectionLength { range: (min, max) }) 38 | } 39 | 40 | /// Serializes the expected connection length range into the given byte buffer. 41 | /// 42 | /// # Panics 43 | /// 44 | /// The buffer must be at least 4 bytes long. 45 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 46 | assert!(bytes.len() >= 4); 47 | 48 | LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.range.0)); 49 | LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.range.1)); 50 | } 51 | 52 | fn duration_as_u16(d: Duration) -> u16 { 53 | // T = 0.625 ms * N 54 | // so N = T / 0.625 ms 55 | // = T / 625 us 56 | // 57 | // Note: 1600 = 1_000_000 / 625 58 | (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16 59 | } 60 | } 61 | 62 | /// Types of errors that can occure when creating a [`ExpectedConnectionLength`]. 63 | #[derive(Copy, Clone, Debug, PartialEq)] 64 | pub enum ExpectedConnectionLengthError { 65 | /// The maximum expected length is too long. The maximum is 40.959375, because nothing higher 66 | /// can be represented as a u16. 67 | TooLong(Duration), 68 | /// The min is greater than the max. Returns the min and max, respectively. 69 | Inverted(Duration, Duration), 70 | } 71 | -------------------------------------------------------------------------------- /tests/vendor/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | extern crate bluetooth_hci as hci; 4 | extern crate nb; 5 | 6 | use hci::host::*; 7 | use std; 8 | 9 | pub struct RecordingSink { 10 | pub written_data: Vec, 11 | } 12 | 13 | #[derive(Debug, PartialEq)] 14 | pub struct RecordingSinkError; 15 | 16 | #[derive(Debug)] 17 | pub struct VendorEvent; 18 | #[derive(Debug)] 19 | pub struct VendorError; 20 | #[derive(Clone, Debug)] 21 | pub struct VendorReturnParameters; 22 | #[derive(Copy, Clone, Debug, PartialEq)] 23 | pub enum VendorStatus { 24 | FourFive, 25 | FiveZero, 26 | } 27 | 28 | pub struct MockVendor; 29 | impl hci::Vendor for MockVendor { 30 | type Status = VendorStatus; 31 | type Event = VendorEvent; 32 | } 33 | 34 | impl std::convert::TryFrom for VendorStatus { 35 | type Error = hci::BadStatusError; 36 | 37 | fn try_from(value: u8) -> Result { 38 | match value { 39 | 0x45 => Ok(VendorStatus::FourFive), 40 | 0x50 => Ok(VendorStatus::FiveZero), 41 | _ => Err(hci::BadStatusError::BadValue(value)), 42 | } 43 | } 44 | } 45 | 46 | impl std::convert::Into for VendorStatus { 47 | fn into(self) -> u8 { 48 | match self { 49 | VendorStatus::FourFive => 0x45, 50 | VendorStatus::FiveZero => 0x50, 51 | } 52 | } 53 | } 54 | 55 | impl hci::event::VendorEvent for VendorEvent { 56 | type Error = VendorError; 57 | type ReturnParameters = VendorReturnParameters; 58 | type Status = VendorStatus; 59 | 60 | fn new(_buffer: &[u8]) -> Result> { 61 | Err(hci::event::Error::Vendor(VendorError)) 62 | } 63 | } 64 | 65 | impl hci::event::VendorReturnParameters for VendorReturnParameters { 66 | type Error = VendorError; 67 | 68 | fn new(_buffer: &[u8]) -> Result> { 69 | Err(hci::event::Error::Vendor(VendorError)) 70 | } 71 | } 72 | 73 | impl hci::Controller for RecordingSink { 74 | type Error = RecordingSinkError; 75 | type Header = uart::CommandHeader; 76 | type Vendor = MockVendor; 77 | 78 | fn write(&mut self, header: &[u8], payload: &[u8]) -> nb::Result<(), Self::Error> { 79 | self.written_data.resize(header.len() + payload.len(), 0); 80 | { 81 | let (h, p) = self.written_data.split_at_mut(header.len()); 82 | h.copy_from_slice(header); 83 | p.copy_from_slice(payload); 84 | } 85 | Ok(()) 86 | } 87 | 88 | fn read_into(&mut self, _buffer: &mut [u8]) -> nb::Result<(), Self::Error> { 89 | Err(nb::Error::Other(RecordingSinkError {})) 90 | } 91 | 92 | fn peek(&mut self, _n: usize) -> nb::Result { 93 | Err(nb::Error::Other(RecordingSinkError {})) 94 | } 95 | } 96 | 97 | impl RecordingSink { 98 | pub fn new() -> RecordingSink { 99 | RecordingSink { 100 | written_data: Vec::new(), 101 | } 102 | } 103 | 104 | pub fn as_controller(&mut self) -> &mut dyn Hci { 105 | self as &mut dyn Hci 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/opcode.rs: -------------------------------------------------------------------------------- 1 | /// Newtype wrapper for a Bluetooth Opcode. Opcodes are used to indicate which command to send to 2 | /// the Controller as well as which command results are returned by the Command Complete and Command 3 | /// Status events. 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 | pub struct Opcode(pub u16); 6 | 7 | impl Opcode { 8 | /// Create an opcode from the OGF (Opcode group field) and OCF (Opcode command field). 9 | pub const fn new(ogf: u16, ocf: u16) -> Opcode { 10 | Opcode((ogf << 10) | (ocf & 0x03ff)) 11 | } 12 | 13 | /// Return the OGF (Opcode group field) of the opcode. 14 | pub fn ogf(&self) -> u16 { 15 | self.0 >> 10 16 | } 17 | 18 | /// Return the OCF (Opcode command field) of the opcode. 19 | pub fn ocf(&self) -> u16 { 20 | self.0 & 0x03ff 21 | } 22 | } 23 | 24 | macro_rules! opcodes { 25 | ( 26 | $( 27 | $_ogf_comment:ident = $ogf:expr; 28 | { 29 | $(pub const $var:ident = $ocf:expr;)+ 30 | } 31 | )+ 32 | ) => { 33 | $($( 34 | pub const $var: Opcode = Opcode::new($ogf, $ocf); 35 | )+)+ 36 | } 37 | } 38 | 39 | opcodes! { 40 | LinkControl = 0x0001; 41 | { 42 | pub const DISCONNECT = 0x0006; 43 | pub const READ_REMOTE_VERSION_INFO = 0x001D; 44 | } 45 | 46 | ControllerOrBaseband = 0x0003; 47 | { 48 | pub const SET_EVENT_MASK = 0x0001; 49 | pub const RESET = 0x0003; 50 | pub const READ_TX_POWER_LEVEL = 0x002D; 51 | } 52 | 53 | InfoParam = 0x0004; 54 | { 55 | pub const READ_LOCAL_VERSION_INFO = 0x0001; 56 | pub const READ_LOCAL_SUPPORTED_COMMANDS = 0x0002; 57 | pub const READ_LOCAL_SUPPORTED_FEATURES = 0x0003; 58 | pub const READ_BD_ADDR = 0x0009; 59 | } 60 | 61 | StatusParam = 0x0005; 62 | { 63 | pub const READ_RSSI = 0x0005; 64 | } 65 | 66 | LeCommands = 0x0008; 67 | { 68 | pub const LE_SET_EVENT_MASK = 0x0001; 69 | pub const LE_READ_BUFFER_SIZE = 0x0002; 70 | pub const LE_READ_LOCAL_SUPPORTED_FEATURES = 0x0003; 71 | pub const LE_SET_RANDOM_ADDRESS = 0x0005; 72 | pub const LE_SET_ADVERTISING_PARAMETERS = 0x0006; 73 | pub const LE_READ_ADVERTISING_CHANNEL_TX_POWER = 0x0007; 74 | pub const LE_SET_ADVERTISING_DATA = 0x0008; 75 | pub const LE_SET_SCAN_RESPONSE_DATA = 0x0009; 76 | pub const LE_SET_ADVERTISE_ENABLE = 0x000A; 77 | pub const LE_SET_SCAN_PARAMETERS = 0x000B; 78 | pub const LE_SET_SCAN_ENABLE = 0x000C; 79 | pub const LE_CREATE_CONNECTION = 0x000D; 80 | pub const LE_CREATE_CONNECTION_CANCEL = 0x000E; 81 | pub const LE_READ_WHITE_LIST_SIZE = 0x000F; 82 | pub const LE_CLEAR_WHITE_LIST = 0x0010; 83 | pub const LE_ADD_DEVICE_TO_WHITE_LIST = 0x0011; 84 | pub const LE_REMOVE_DEVICE_FROM_WHITE_LIST = 0x0012; 85 | pub const LE_CONNECTION_UPDATE = 0x0013; 86 | pub const LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x0014; 87 | pub const LE_READ_CHANNEL_MAP = 0x0015; 88 | pub const LE_READ_REMOTE_USED_FEATURES = 0x0016; 89 | pub const LE_ENCRYPT = 0x0017; 90 | pub const LE_RAND = 0x0018; 91 | pub const LE_START_ENCRYPTION = 0x0019; 92 | pub const LE_LTK_REQUEST_REPLY = 0x001A; 93 | pub const LE_LTK_REQUEST_NEGATIVE_REPLY = 0x001B; 94 | pub const LE_READ_STATES = 0x001C; 95 | pub const LE_RECEIVER_TEST = 0x001D; 96 | pub const LE_TRANSMITTER_TEST = 0x001E; 97 | pub const LE_TEST_END = 0x001F; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/types/advertisement.rs: -------------------------------------------------------------------------------- 1 | //! Types for LE advertisements 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | 5 | use super::CommonDataType; 6 | 7 | /// LE Advertisement Type 8 | pub enum Advertisement<'a> { 9 | /// Complete local name of the device. 10 | CompleteLocalName(&'a str), 11 | /// Service data with 16-bit UUID. 12 | /// 13 | /// The first parameter is the UUID, the second parameter is the payload. 14 | /// 15 | /// The payload may be up to 27 bytes for legacy advertising mode. 16 | ServiceData16BitUuid(u16, &'a [u8]), 17 | /// Service data with 32-bit UUID 18 | /// 19 | /// The first parameter is the UUID, the second parameter is the payload. 20 | /// 21 | /// The payload may be up to 25 bytes for legacy advertising mode. 22 | ServiceData32BitUuid(u32, &'a [u8]), 23 | /// Service data with 128-bit UUID 24 | /// 25 | /// The first parameter is the UUID, the second parameter is the payload. 26 | /// 27 | /// The payload may be up to 13 bytes for legacy advertising mode. 28 | ServiceData128BitUuid(u128, &'a [u8]), 29 | /// Manufacturer-specific data 30 | /// 31 | /// The first parameter is the manufacturer ID, the second parameter is the 32 | /// payload. 33 | /// 34 | /// The payload may be up to 27 bytes for legacy advertising mode. 35 | ManufacturerSpecificData(u16, &'a [u8]), 36 | } 37 | 38 | impl Advertisement<'_> { 39 | /// Gets the length of the advertisement payload, in bytes. 40 | /// 41 | /// This includes the length byte itself. 42 | pub fn len(&self) -> usize { 43 | use Advertisement::*; 44 | 2 + match self { 45 | CompleteLocalName(n) => n.len(), 46 | ServiceData16BitUuid(_, b) | ManufacturerSpecificData(_, b) => 2 + b.len(), 47 | ServiceData32BitUuid(_, b) => 4 + b.len(), 48 | ServiceData128BitUuid(_, b) => 16 + b.len(), 49 | } 50 | } 51 | 52 | /// Gets the [CommonDataType] for this advertisement. 53 | const fn get_type(&self) -> CommonDataType { 54 | use Advertisement::*; 55 | match self { 56 | CompleteLocalName(_) => CommonDataType::CompleteLocalName, 57 | ServiceData16BitUuid(_, _) => CommonDataType::ServiceData16BitUuid, 58 | ServiceData32BitUuid(_, _) => CommonDataType::ServiceData32BitUuid, 59 | ServiceData128BitUuid(_, _) => CommonDataType::ServiceData128BitUuid, 60 | ManufacturerSpecificData(_, _) => CommonDataType::ManufacturerSpecificData, 61 | } 62 | } 63 | 64 | /// Serialize the advertisement into the given buffer, and return the number 65 | /// of bytes written. 66 | /// 67 | /// The maximum length of advertisements in legacy mode is 31 bytes. 68 | /// 69 | /// `bytes` must be at least [Self::len()] bytes. 70 | pub fn copy_into_slice(&self, bytes: &mut [u8]) -> usize { 71 | use Advertisement::*; 72 | let len = self.len(); 73 | // Don't count the length byte. 74 | bytes[0] = (len - 1) as u8; 75 | bytes[1] = self.get_type() as u8; 76 | match self { 77 | CompleteLocalName(n) => { 78 | bytes[2..2 + n.len()].copy_from_slice(n.as_bytes()); 79 | } 80 | ServiceData16BitUuid(u, b) | ManufacturerSpecificData(u, b) => { 81 | LittleEndian::write_u16(&mut bytes[2..], *u); 82 | bytes[4..4 + b.len()].copy_from_slice(b); 83 | } 84 | ServiceData32BitUuid(u, b) => { 85 | LittleEndian::write_u32(&mut bytes[2..], *u); 86 | bytes[6..6 + b.len()].copy_from_slice(b); 87 | } 88 | ServiceData128BitUuid(u, b) => { 89 | LittleEndian::write_u128(&mut bytes[2..], *u); 90 | bytes[18..18 + b.len()].copy_from_slice(b); 91 | } 92 | } 93 | len 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/advertising_interval.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | use hci::types::{AdvertisingInterval, AdvertisingIntervalError, AdvertisingType}; 4 | use std::time::Duration; 5 | 6 | #[test] 7 | fn valid() { 8 | let interval = AdvertisingInterval::for_type(AdvertisingType::ConnectableUndirected) 9 | .with_range(Duration::from_millis(21), Duration::from_millis(1000)) 10 | .unwrap(); 11 | let mut bytes = [0; 5]; 12 | interval.copy_into_slice(&mut bytes); 13 | 14 | // 21 ms / 0.625 ms = 33 = 0x0021 15 | // 1000 ms / 0.625 ms = 1600 = 0x0640 16 | assert_eq!(bytes, [0x21, 0x00, 0x40, 0x06, 0x00]); 17 | } 18 | 19 | #[cfg(not(feature = "version-5-0"))] 20 | #[test] 21 | fn higher_min_scannable_undirected() { 22 | let err = AdvertisingInterval::for_type(AdvertisingType::ScannableUndirected) 23 | .with_range(Duration::from_millis(99), Duration::from_millis(1000)) 24 | .err() 25 | .unwrap(); 26 | assert_eq!( 27 | err, 28 | AdvertisingIntervalError::TooShort(Duration::from_millis(99)) 29 | ); 30 | } 31 | 32 | #[cfg(feature = "version-5-0")] 33 | #[test] 34 | fn normal_min_scannable_undirected() { 35 | let interval = AdvertisingInterval::for_type(AdvertisingType::ScannableUndirected) 36 | .with_range(Duration::from_millis(99), Duration::from_millis(1000)) 37 | .unwrap(); 38 | let mut bytes = [0; 5]; 39 | interval.copy_into_slice(&mut bytes); 40 | 41 | // 99 ms / 0.625 ms = 158 = 0x009E 42 | // 1000 ms / 0.625 ms = 1600 = 0x0640 43 | assert_eq!(bytes, [0x9E, 0x00, 0x40, 0x06, 0x02]); 44 | } 45 | 46 | #[test] 47 | fn connectable_directed_high_duty_cycle_without_range() { 48 | let interval = AdvertisingInterval::for_type(AdvertisingType::ConnectableDirectedHighDutyCycle) 49 | .build() 50 | .unwrap(); 51 | let mut bytes = [0; 5]; 52 | interval.copy_into_slice(&mut bytes); 53 | assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x01]); 54 | } 55 | 56 | #[test] 57 | fn connectable_directed_high_duty_cycle_with_range() { 58 | let interval = AdvertisingInterval::for_type(AdvertisingType::ConnectableDirectedHighDutyCycle) 59 | .with_range(Duration::from_millis(99), Duration::from_millis(1000)) 60 | .unwrap(); 61 | let mut bytes = [0; 5]; 62 | interval.copy_into_slice(&mut bytes); 63 | 64 | // Interval is ignored for this advertising type. 65 | assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x01]); 66 | } 67 | 68 | #[test] 69 | fn other_type_without_range() { 70 | let err = AdvertisingInterval::for_type(AdvertisingType::ScannableUndirected) 71 | .build() 72 | .err() 73 | .unwrap(); 74 | assert_eq!(err, AdvertisingIntervalError::NoRange); 75 | } 76 | 77 | #[test] 78 | fn interval_too_short() { 79 | let err = AdvertisingInterval::for_type(AdvertisingType::ConnectableUndirected) 80 | .with_range(Duration::from_millis(19), Duration::from_millis(1000)) 81 | .err() 82 | .unwrap(); 83 | assert_eq!( 84 | err, 85 | AdvertisingIntervalError::TooShort(Duration::from_millis(19)) 86 | ); 87 | } 88 | 89 | #[test] 90 | fn interval_too_long() { 91 | let err = AdvertisingInterval::for_type(AdvertisingType::ConnectableUndirected) 92 | .with_range(Duration::from_millis(100), Duration::from_millis(10241)) 93 | .err() 94 | .unwrap(); 95 | assert_eq!( 96 | err, 97 | AdvertisingIntervalError::TooLong(Duration::from_millis(10241)) 98 | ); 99 | } 100 | 101 | #[test] 102 | fn inverted() { 103 | let err = AdvertisingInterval::for_type(AdvertisingType::ConnectableUndirected) 104 | .with_range(Duration::from_millis(500), Duration::from_millis(499)) 105 | .err() 106 | .unwrap(); 107 | assert_eq!( 108 | err, 109 | AdvertisingIntervalError::Inverted(Duration::from_millis(500), Duration::from_millis(499)) 110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /src/host/event_link.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the HCI that only supports reading events from the controller. 2 | //! 3 | //! This was originally written just based on wording from the Bluetooth spec (version 5.0, Vol 4, 4 | //! Part A, section 2), emphasis added: 5 | //! 6 | //! > Therefore, *if* the HCI packets are sent via a common physical interface, a HCI 7 | //! > packet indicator has to be added according to Table 2.1 below. 8 | //! 9 | //! However, there don't seem to be any implementations where the HCI packets are _not_ sent "via a 10 | //! common physical interface", so this module may be unnecessary. 11 | 12 | extern crate nb; 13 | 14 | /// Potential errors from reading events from the controller. 15 | #[derive(Copy, Clone, Debug)] 16 | pub enum Error { 17 | /// There was an error deserializing an event. Contains the underlying error. 18 | BLE(crate::event::Error), 19 | /// There was a communication error. Contains the underlying error. 20 | Comm(E), 21 | } 22 | 23 | /// Dummy struct used to specialize [`super::Hci`]. Since the [`Hci`] does not support sending 24 | /// commands, we do not need a real header struct. 25 | pub struct NoCommands; 26 | 27 | /// Trait for reading events from the controller. Since this trait should only be used when events 28 | /// are sent by a different physical link than commands, it does not need to implement 29 | /// [`crate::host::Hci`]. 30 | /// 31 | /// Must be specialized for communication errors (`E`), vendor-specific events (`Vendor`), and 32 | /// vendor-specific errors (`VE`). 33 | /// 34 | /// Peeks ahead 2 bytes into the stream to read the length of the parameters for the next event. 35 | /// 36 | /// # Errors 37 | /// 38 | /// - Returns [`nb::Error::WouldBlock`] if the controller does not have enough bytes to read an 39 | /// event. 40 | /// - Returns [`nb::Error::Other`]`(`[`Error::BLE`]`)` if there is an error deserializing the packet 41 | /// (such as a mismatch between the packet length and the expected length of the event). See 42 | /// [`crate::event::Error`] for possible values of `e`. 43 | /// - Returns [`nb::Error::Other`]`(`[`Error::Comm`]`)` if there is an error reading from the 44 | /// controller. 45 | pub trait Hci { 46 | /// Reads and returns an event from the controller. Consumes exactly enough bytes to read the 47 | /// next event including its header. 48 | /// 49 | /// # Errors 50 | /// 51 | /// - Returns [`nb::Error::WouldBlock`] if the controller does not have enough bytes available 52 | /// to read the full event right now. 53 | /// - Returns [`nb::Error::Other`]`(`[`Error::BLE`]`)` if there is an error deserializing the 54 | /// packet (such as a mismatch between the packet length and the expected length of the 55 | /// event). See [`crate::event::Error`] for possible values of `e`. 56 | /// - Returns [`nb::Error::Other`]`(`[`Error::Comm`]`)` if there is an error reading from the 57 | /// controller. 58 | fn read(&mut self) -> nb::Result, Error> 59 | where 60 | Vendor: crate::event::VendorEvent; 61 | } 62 | 63 | impl super::HciHeader for NoCommands { 64 | const HEADER_LENGTH: usize = 3; 65 | 66 | fn new(_opcode: crate::opcode::Opcode, _param_len: usize) -> NoCommands { 67 | NoCommands 68 | } 69 | 70 | fn copy_into_slice(&self, _buffer: &mut [u8]) {} 71 | } 72 | 73 | fn rewrap_error(e: nb::Error) -> nb::Error> { 74 | match e { 75 | nb::Error::WouldBlock => nb::Error::WouldBlock, 76 | nb::Error::Other(err) => nb::Error::Other(Error::Comm(err)), 77 | } 78 | } 79 | 80 | impl Hci for T 81 | where 82 | T: crate::Controller, 83 | { 84 | fn read(&mut self) -> nb::Result, Error> 85 | where 86 | Vendor: crate::event::VendorEvent, 87 | { 88 | const MAX_EVENT_LENGTH: usize = 255; 89 | const EVENT_HEADER_LENGTH: usize = 2; 90 | const PARAM_LEN_BYTE: usize = 1; 91 | 92 | let param_len = self.peek(PARAM_LEN_BYTE).map_err(rewrap_error)? as usize; 93 | 94 | let mut buf = [0; MAX_EVENT_LENGTH + EVENT_HEADER_LENGTH]; 95 | self.read_into(&mut buf[..EVENT_HEADER_LENGTH + param_len]) 96 | .map_err(rewrap_error)?; 97 | 98 | crate::Event::new(crate::event::Packet( 99 | &buf[..EVENT_HEADER_LENGTH + param_len], 100 | )) 101 | .map_err(|e| nb::Error::Other(Error::BLE(e))) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/types/scan_window.rs: -------------------------------------------------------------------------------- 1 | //! Types related to the LE scanning window. 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use core::time::Duration; 5 | 6 | /// Define a scanning window. 7 | /// 8 | /// The controller runs LE scans every [`interval`](ScanWindow::interval), with scanning active 9 | /// during the [`window`](ScanWindow::window) in every interval. 10 | /// 11 | /// The minimum time range is 2.5 ms, and the maximum is 10.24 s. The window must be shorter than or 12 | /// equal to the interval. 13 | #[derive(Clone, Debug, PartialEq)] 14 | pub struct ScanWindow { 15 | interval_width: Duration, 16 | window_width: Duration, 17 | } 18 | 19 | impl ScanWindow { 20 | /// Returns the interval for the scanning window. The controller starts an LE scan every 21 | /// interval. 22 | pub fn interval(&self) -> Duration { 23 | self.interval_width 24 | } 25 | 26 | /// Returns the amount of time the controller is scanning every interval. 27 | pub fn window(&self) -> Duration { 28 | self.window_width 29 | } 30 | 31 | /// Serializes the window into the given byte buffer. 32 | /// 33 | /// # Panics 34 | /// 35 | /// The buffer must be at least 4 bytes long. 36 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 37 | assert!(bytes.len() >= 4); 38 | 39 | LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.interval_width)); 40 | LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.window_width)); 41 | } 42 | 43 | /// Begins building a [ScanWindow]. The scan window has the given interval. Returns a 44 | /// [builder](ScanWindowBuilder) that can be used to set the window duration. 45 | /// 46 | /// # Errors 47 | /// 48 | /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5 49 | /// ms. 50 | /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds 51 | /// or less. 52 | pub fn start_every(interval: Duration) -> Result { 53 | Ok(ScanWindowBuilder { 54 | interval: ScanWindow::validate(interval)?, 55 | }) 56 | } 57 | 58 | fn validate(d: Duration) -> Result { 59 | const MIN: Duration = Duration::from_micros(2500); 60 | if d < MIN { 61 | return Err(ScanWindowError::TooShort(d)); 62 | } 63 | 64 | const MAX: Duration = Duration::from_millis(10240); 65 | if d > MAX { 66 | return Err(ScanWindowError::TooLong(d)); 67 | } 68 | 69 | Ok(d) 70 | } 71 | 72 | fn duration_as_u16(d: Duration) -> u16 { 73 | // T = 0.625 ms * N 74 | // so N = T / 0.625 ms 75 | // = T / 625 us 76 | // 77 | // Note: 1600 = 1_000_000 / 625 78 | (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16 79 | } 80 | } 81 | 82 | /// Intermediate builder for the [`ScanWindow`]. 83 | pub struct ScanWindowBuilder { 84 | interval: Duration, 85 | } 86 | 87 | impl ScanWindowBuilder { 88 | /// Completes building a [ScanWindow]. The scan window has the given window. 89 | /// 90 | /// # Errors 91 | /// 92 | /// - [ScanWindowError::TooShort] if the provided interval is too short. It must be at least 2.5 93 | /// ms. 94 | /// - [ScanWindowError::TooLong] if the provided interval is too long. It must be 10.24 seconds 95 | /// or less. 96 | /// - [ScanWindowError::Inverted] if the window is longer than the interval. 97 | pub fn open_for(&self, window: Duration) -> Result { 98 | if window > self.interval { 99 | return Err(ScanWindowError::Inverted { 100 | interval: self.interval, 101 | window, 102 | }); 103 | } 104 | 105 | Ok(ScanWindow { 106 | interval_width: self.interval, 107 | window_width: ScanWindow::validate(window)?, 108 | }) 109 | } 110 | } 111 | 112 | /// Types of errors that can occure when creating a [`ScanWindow`]. 113 | #[derive(Copy, Clone, Debug, PartialEq)] 114 | pub enum ScanWindowError { 115 | /// The duration is too short. Both the interval and duration must be at least 2.5 ms. Includes 116 | /// the invalid duration. 117 | TooShort(Duration), 118 | /// The duration is too long. Both the interval and duration must be no more than 10.24 119 | /// seconds. Includes the invalid duration. 120 | TooLong(Duration), 121 | /// The interval and window are inverted. That is, the interval is shorter than the window. 122 | Inverted { 123 | /// The provided interval, which is shorter than the window. 124 | interval: Duration, 125 | /// The provided window, which is longer than the interval. 126 | window: Duration, 127 | }, 128 | } 129 | -------------------------------------------------------------------------------- /src/types/common.rs: -------------------------------------------------------------------------------- 1 | //! Common Data Types 2 | 3 | /// Enumeration of "Common Data Types" from the [Bluetooth Assigned Numbers][0] 4 | /// registry. 5 | /// 6 | /// [0]: https://www.bluetooth.com/specifications/assigned-numbers/ 7 | #[repr(u8)] 8 | #[derive(Copy, Clone, Debug, PartialEq)] 9 | pub enum CommonDataType { 10 | /// Ref: Core Specification Supplement, Part A, Section 1.3 11 | Flags = 0x01, 12 | /// Ref: Core Specification Supplement, Part A, Section 1.1 13 | IncompleteListOf16BitServiceClassUuids = 0x02, 14 | /// Ref: Core Specification Supplement, Part A, Section 1.1 15 | CompleteListOf16BitServiceClassUuids = 0x03, 16 | /// Ref: Core Specification Supplement, Part A, Section 1.1 17 | IncompleteListOf32BitServiceClassUuids = 0x04, 18 | /// Ref: Core Specification Supplement, Part A, Section 1.1 19 | CompleteListOf32BitServiceClassUuids = 0x05, 20 | /// Ref: Core Specification Supplement, Part A, Section 1.1 21 | IncompleteListOf128BitServiceClassUuids = 0x06, 22 | /// Ref: Core Specification Supplement, Part A, Section 1.1 23 | CompleteListOf128BitServiceClassUuids = 0x07, 24 | /// Ref: Core Specification Supplement, Part A, Section 1.2 25 | ShortenedLocalName = 0x08, 26 | /// Ref: Core Specification Supplement, Part A, Section 1.2 27 | CompleteLocalName = 0x09, 28 | /// Ref: Core Specification Supplement, Part A, Section 1.5 29 | TxPowerLevel = 0x0a, 30 | /// Ref: Core Specification Supplement, Part A, Section 1.6 31 | ClassOfDevice = 0x0d, 32 | /// Ref: Core Specification Supplement, Part A, Section 1.6 33 | SimplePairingHashC192 = 0x0e, 34 | /// Ref: Core Specification Supplement, Part A, Section 1.6 35 | SimplePairingRandomizerR192 = 0x0f, 36 | /// Ref: Device ID Profile 37 | DeviceId = 0x10, // (also SecurityManagerTkValue) 38 | /// Ref: Core Specification Supplement, Part A, Section 1.7 39 | SecurityManagerTkValue = 0x11, 40 | /// Ref: Core Specification Supplement, Part A, Section 1.9 41 | PeripheralConnectionIntervalRange = 0x12, 42 | /// Ref: Core Specification Supplement, Part A, Section 1.10 43 | ListOf16BitServiceSolicitationUuids = 0x14, 44 | /// Ref: Core Specification Supplement, Part A, Section 1.10 45 | ListOf128BitServiceSolicitationUuids = 0x15, 46 | /// Ref: Core Specification Supplement, Part A, Section 1.11 47 | ServiceData16BitUuid = 0x16, 48 | /// Ref: Core Specification Supplement, Part A, Section 1.13 49 | PublicTargetAddress = 0x17, 50 | /// Ref: Core Specification Supplement, Part A, Section 1.14 51 | RandomTargetAddress = 0x18, 52 | /// Ref: Core Specification Supplement, Part A, Section 1.12 53 | Appearance = 0x19, 54 | /// Ref: Core Specification Supplement, Part A, Section 1.15 55 | AdvertisingInterval = 0x1a, 56 | /// Ref: Core Specification Supplement, Part A, Section 1.16 57 | LeBluetoothDeviceAddress = 0x1b, 58 | /// Ref: Core Specification Supplement, Part A, Section 1.17 59 | LeRole = 0x1c, 60 | /// Ref: Core Specification Supplement, Part A, Section 1.6 61 | SimplePairingHashC256 = 0x1d, 62 | /// Ref: Core Specification Supplement, Part A, Section 1.6 63 | SimplePairingRandomizerR256 = 0x1e, 64 | /// Ref: Core Specification Supplement, Part A, Section 1.10 65 | ListOf32BitServiceSolicitationUuids = 0x1f, 66 | /// Ref: Core Specification Supplement, Part A, Section 1.11 67 | ServiceData32BitUuid = 0x20, 68 | /// Ref: Core Specification Supplement, Part A, Section 1.11 69 | ServiceData128BitUuid = 0x21, 70 | /// Ref: Core Specification Supplement, Part A, Section 1.6 71 | LeSecureConnectionsConfirmationValue = 0x22, 72 | /// Ref: Core Specification Supplement, Part A, Section 1.6 73 | LeSecureConnectionsRandomValue = 0x23, 74 | /// Ref: Core Specification Supplement, Part A, Section 1.18 75 | Uri = 0x24, 76 | /// Ref: Indoor Positioning Service 77 | IndoorPositioning = 0x25, 78 | /// Ref: Transport Discovery Service 79 | TransportDiscoveryData = 0x26, 80 | /// Ref: Core Specification Supplement, Part A, Section 1.19 81 | LeSupportedFeatures = 0x27, 82 | /// Ref: Core Specification Supplement, Part A, Section 1.20 83 | ChannelMapUpdateIndication = 0x28, 84 | /// Ref: Mesh Profile Specification, Section 5.2.1 85 | PbAdv = 0x29, 86 | /// Ref: Mesh Profile Specification, Section 3.3.1 87 | MeshMessage = 0x2a, 88 | /// Ref: Mesh Profile Specification, Section 3.9 89 | MeshBeacon = 0x2b, 90 | /// Ref: Core Specification Supplement, Part A, Section 1.21 91 | BigInfo = 0x2c, 92 | /// Ref: Core Specification Supplement, Part A, Section 1.22 93 | BroadcastCode = 0x2d, 94 | /// Ref: Coordinated Set Identification Profile 95 | ResolvableSetIdentifier = 0x2e, 96 | /// Ref: Core Specification Supplement, Part A, Section 1.15 97 | AdvertisingIntervalLong = 0x2f, 98 | /// Ref: Public Broadcast Profile 99 | BroadcastName = 0x30, 100 | /// Ref: 3D Synchronization Profile 101 | ThreeDInformationData = 0x3d, 102 | /// Ref: Core Specification Supplement, Part A, Section 1.4 103 | ManufacturerSpecificData = 0xff, 104 | } 105 | -------------------------------------------------------------------------------- /src/host/uart.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the HCI that includes the packet ID byte in the header. 2 | 3 | extern crate nb; 4 | 5 | use byteorder::{ByteOrder, LittleEndian}; 6 | 7 | const PACKET_TYPE_HCI_COMMAND: u8 = 0x01; 8 | // const PACKET_TYPE_ACL_DATA: u8 = 0x02; 9 | // const PACKET_TYPE_SYNC_DATA: u8 = 0x03; 10 | const PACKET_TYPE_HCI_EVENT: u8 = 0x04; 11 | 12 | /// Potential errors from reading or writing packets to the controller. 13 | /// 14 | /// Must be specialized both for communication errors (`E`) and vendor-specific errors (`VE`). 15 | #[derive(Copy, Clone, Debug, PartialEq)] 16 | pub enum Error { 17 | /// The host expected the controller to begin a packet, but the next byte is not a valid packet 18 | /// type byte. Contains the value of the byte. 19 | BadPacketType(u8), 20 | /// There was an error deserializing an event. Contains the underlying error. 21 | BLE(crate::event::Error), 22 | /// There was a communication error. Contains the underlying error. 23 | Comm(E), 24 | } 25 | 26 | /// Packet types that may be read from the controller. 27 | #[derive(Clone, Debug)] 28 | pub enum Packet 29 | where 30 | Vendor: crate::event::VendorEvent, 31 | { 32 | // AclData(AclData), 33 | // SyncData(SyncData), 34 | /// The HCI Event Packet is used by the Controller to notify the Host when events 35 | /// occur. The event is specialized to support vendor-specific events. 36 | Event(crate::Event), 37 | } 38 | 39 | /// Header for HCI Commands. 40 | pub struct CommandHeader { 41 | opcode: crate::opcode::Opcode, 42 | param_len: u8, 43 | } 44 | 45 | /// Trait for reading packets from the controller. 46 | /// 47 | /// Implementors must also implement [`crate::host::Hci`], which provides all of the functions to 48 | /// write commands to the controller. This trait adds the ability to read packets back from the 49 | /// controller. 50 | /// 51 | /// Must be specialized for communication errors (`E`), vendor-specific events (`Vendor`), and 52 | /// vendor-specific errors (`VE`). 53 | pub trait Hci: super::Hci { 54 | /// Reads and returns a packet from the controller. Consumes exactly enough bytes to read the 55 | /// next packet including its header. 56 | /// 57 | /// # Errors 58 | /// 59 | /// - Returns [`nb::Error::WouldBlock`] if the controller does not have enough bytes available 60 | /// to read the full packet right now. 61 | /// - Returns [`nb::Error::Other`]`(`[`Error::BadPacketType`]`)` if the next byte is not a valid 62 | /// packet type. 63 | /// - Returns [`nb::Error::Other`]`(`[`Error::BLE`]`)` if there is an error deserializing the 64 | /// packet (such as a mismatch between the packet length and the expected length of the 65 | /// event). See [`crate::event::Error`] for possible values of `e`. 66 | /// - Returns [`nb::Error::Other`]`(`[`Error::Comm`]`)` if there is an error reading from the 67 | /// controller. 68 | fn read(&mut self) -> nb::Result, Error> 69 | where 70 | Vendor: crate::event::VendorEvent; 71 | } 72 | 73 | impl super::HciHeader for CommandHeader { 74 | const HEADER_LENGTH: usize = 4; 75 | 76 | fn new(opcode: crate::opcode::Opcode, param_len: usize) -> CommandHeader { 77 | CommandHeader { 78 | opcode, 79 | param_len: param_len as u8, 80 | } 81 | } 82 | 83 | fn copy_into_slice(&self, buffer: &mut [u8]) { 84 | buffer[0] = PACKET_TYPE_HCI_COMMAND; 85 | LittleEndian::write_u16(&mut buffer[1..=2], self.opcode.0); 86 | buffer[3] = self.param_len; 87 | } 88 | } 89 | 90 | fn rewrap_error(e: nb::Error) -> nb::Error> { 91 | match e { 92 | nb::Error::WouldBlock => nb::Error::WouldBlock, 93 | nb::Error::Other(err) => nb::Error::Other(Error::Comm(err)), 94 | } 95 | } 96 | 97 | fn read_event( 98 | controller: &mut T, 99 | ) -> nb::Result, Error> 100 | where 101 | T: crate::Controller, 102 | Vendor: crate::event::VendorEvent, 103 | { 104 | const MAX_EVENT_LENGTH: usize = 255; 105 | const PACKET_HEADER_LENGTH: usize = 1; 106 | const EVENT_PACKET_HEADER_LENGTH: usize = 3; 107 | const PARAM_LEN_BYTE: usize = 2; 108 | 109 | let param_len = controller.peek(PARAM_LEN_BYTE).map_err(rewrap_error)? as usize; 110 | 111 | let mut buf = [0; MAX_EVENT_LENGTH + EVENT_PACKET_HEADER_LENGTH]; 112 | controller 113 | .read_into(&mut buf[..EVENT_PACKET_HEADER_LENGTH + param_len]) 114 | .map_err(rewrap_error)?; 115 | 116 | crate::event::Event::new(crate::event::Packet( 117 | &buf[PACKET_HEADER_LENGTH..EVENT_PACKET_HEADER_LENGTH + param_len], 118 | )) 119 | .map_err(|e| nb::Error::Other(Error::BLE(e))) 120 | } 121 | 122 | impl Hci for T 123 | where 124 | T: crate::Controller, 125 | { 126 | fn read(&mut self) -> nb::Result, Error> 127 | where 128 | Vendor: crate::event::VendorEvent, 129 | { 130 | match self.peek(0).map_err(rewrap_error)? { 131 | PACKET_TYPE_HCI_EVENT => Ok(Packet::Event(read_event(self)?)), 132 | x => Err(nb::Error::Other(Error::BadPacketType(x))), 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/types/advertising_interval.rs: -------------------------------------------------------------------------------- 1 | //! Types related to the LE advertising interval. 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use core::time::Duration; 5 | 6 | /// Define an advertising interval range. 7 | /// 8 | /// The advertising interval min shall be less than or equal to the advertising interval 9 | /// max. The advertising interval min and advertising interval max should not be the same value 10 | /// to enable the Controller to determine the best advertising interval given other activities, 11 | /// though this implementation allows them to be equal. 12 | /// 13 | /// For [high duty cycle directed 14 | /// advertising](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is 15 | /// not used and shall be ignored. This implementation sends 0 for both fields in that case. 16 | /// 17 | /// The advertising interval min and advertising interval max shall not be set to less than 100 18 | /// ms if the advertising type is [`ScannableUndirected`](AdvertisingType::ScannableUndirected) 19 | /// or [`NonConnectableUndirected`](AdvertisingType::NonConnectableUndirected). This 20 | /// restriction is removed in version 5.0 of the spec. 21 | #[derive(Clone, Debug)] 22 | pub struct AdvertisingInterval { 23 | // The first field is the min; the second is the max 24 | interval: (Duration, Duration), 25 | _advertising_type: AdvertisingType, 26 | } 27 | 28 | impl AdvertisingInterval { 29 | /// Begins building an advertising interval. 30 | pub fn for_type(adv_type: AdvertisingType) -> AdvertisingIntervalBuilder { 31 | AdvertisingIntervalBuilder { 32 | advertising_type: adv_type, 33 | } 34 | } 35 | 36 | /// Serialize the interval into the given buffer. 37 | /// 38 | /// Serializes the minimum range of the interval (2 bytes), the maximum range of the interval (2 39 | /// bytes), and the advertising type (1 byte). 40 | /// 41 | /// If the advertising type is [high duty cycle 42 | /// directed](AdvertisingType::ConnectableDirectedHighDutyCycle), the advertising interval is 43 | /// not used and shall be ignored. This implementation sends 0 for both fields in that case. 44 | /// 45 | /// # Panics 46 | /// 47 | /// - If the provided buffer is not at least 5 bytes long. 48 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 49 | if self._advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle { 50 | bytes[0..4].copy_from_slice(&[0; 4]); 51 | } else { 52 | LittleEndian::write_u16(&mut bytes[0..2], Self::duration_as_u16(self.interval.0)); 53 | LittleEndian::write_u16(&mut bytes[2..4], Self::duration_as_u16(self.interval.1)); 54 | } 55 | bytes[4] = self._advertising_type as u8; 56 | } 57 | 58 | fn duration_as_u16(d: Duration) -> u16 { 59 | // T = 0.625 ms * N 60 | // so N = T / 0.625 ms 61 | // = T / 625 us 62 | // 63 | // Note: 1600 = 1_000_000 / 625 64 | (1600 * d.as_secs() as u32 + (d.subsec_micros() / 625)) as u16 65 | } 66 | 67 | /// Returns the advertising type. 68 | pub fn advertising_type(&self) -> AdvertisingType { 69 | self._advertising_type 70 | } 71 | } 72 | 73 | /// Partially-specified advertising interval. 74 | pub struct AdvertisingIntervalBuilder { 75 | advertising_type: AdvertisingType, 76 | } 77 | 78 | impl AdvertisingIntervalBuilder { 79 | /// Completes the advertising interval with the provided minimum and maximum values. 80 | /// 81 | /// # Errors 82 | /// 83 | /// - [TooShort](AdvertisingIntervalError::TooShort) if the minimum value is too small. For 84 | /// Bluetooth specifications v4.x, if the advertising type is 85 | /// [ScannableUndirected](AdvertisingType::ScannableUndirected), then the minimum value is 100 86 | /// ms. In all other cases, the minimum value is 20 ms. 87 | /// - [TooLong](AdvertisingIntervalError::TooLong) if the maximum value is too large. The 88 | /// maximum value is 10.24 seconds. 89 | /// - [Inverted](AdvertisingIntervalError::Inverted) if the minimum is greater than the 90 | /// maximum. 91 | pub fn with_range( 92 | &self, 93 | min: Duration, 94 | max: Duration, 95 | ) -> Result { 96 | const MIN: Duration = Duration::from_millis(20); 97 | if min < MIN { 98 | return Err(AdvertisingIntervalError::TooShort(min)); 99 | } 100 | 101 | #[cfg(not(feature = "version-5-0"))] 102 | { 103 | if self.advertising_type == AdvertisingType::ScannableUndirected { 104 | const SCANNABLE_MIN: Duration = Duration::from_millis(100); 105 | if min < SCANNABLE_MIN { 106 | return Err(AdvertisingIntervalError::TooShort(min)); 107 | } 108 | } 109 | } 110 | 111 | const MAX: Duration = Duration::from_millis(10240); 112 | if max > MAX { 113 | return Err(AdvertisingIntervalError::TooLong(max)); 114 | } 115 | 116 | if min > max { 117 | return Err(AdvertisingIntervalError::Inverted(min, max)); 118 | } 119 | 120 | Ok(AdvertisingInterval { 121 | interval: (min, max), 122 | _advertising_type: self.advertising_type, 123 | }) 124 | } 125 | 126 | /// Completes the advertising interval without a range. 127 | /// 128 | /// This is only valid if the advertising type is 129 | /// [ScannableUndirected](AdvertisingType::ScannableUndirected). 130 | /// 131 | /// # Errors 132 | /// 133 | /// - [NoRange](AdvertisingIntervalError::NoRange) if the advertising type is anything except 134 | /// [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle). 135 | pub fn build(&self) -> Result { 136 | if self.advertising_type == AdvertisingType::ConnectableDirectedHighDutyCycle { 137 | Ok(AdvertisingInterval { 138 | interval: (Duration::from_secs(0), Duration::from_secs(0)), 139 | _advertising_type: self.advertising_type, 140 | }) 141 | } else { 142 | Err(AdvertisingIntervalError::NoRange) 143 | } 144 | } 145 | } 146 | 147 | /// Potential errors that can occur when specifying an [`AdvertisingInterval`]. 148 | #[derive(Copy, Clone, Debug, PartialEq)] 149 | pub enum AdvertisingIntervalError { 150 | /// The minimum value was too short. Includes the invalid value. 151 | TooShort(Duration), 152 | /// The maximum value was too long. Includes the invalid value. 153 | TooLong(Duration), 154 | /// The minimum value was greater than the maximum value. Includes the provided minimum and 155 | /// value, respectively. 156 | Inverted(Duration, Duration), 157 | /// The advertising interval was not given a range, and the type was not 158 | /// [ConnectableDirectedHighDutyCycle](AdvertisingType::ConnectableDirectedHighDutyCycle). 159 | NoRange, 160 | } 161 | 162 | /// The advertising type is used in the 163 | /// [`AdvertisingParameters`]($crate::host::AdvertisingParameters) to determine the packet type that 164 | /// is used for advertising when advertising is enabled. 165 | #[repr(u8)] 166 | #[derive(Copy, Clone, Debug, PartialEq)] 167 | pub enum AdvertisingType { 168 | /// Connectable undirected advertising 169 | ConnectableUndirected = 0x00, 170 | /// Connectable high duty cycle directed advertising 171 | ConnectableDirectedHighDutyCycle = 0x01, 172 | /// Scannable undirected advertising 173 | ScannableUndirected = 0x02, 174 | /// Non connectable undirected advertising 175 | NonConnectableUndirected = 0x03, 176 | /// Connectable low duty cycle directed advertising 177 | ConnectableDirectedLowDutyCycle = 0x04, 178 | } 179 | -------------------------------------------------------------------------------- /src/bitflag_array.rs: -------------------------------------------------------------------------------- 1 | // Re-export libcore using an alias so that the macros can work without 2 | // requiring `extern crate core` downstream. 3 | #[doc(hidden)] 4 | pub extern crate core as _core; 5 | 6 | /// Implements an arbitrary-length bitfield. The implementation and interface is similar to and 7 | /// derived from [bitflags](https://crates.io/crates/bitflags). 8 | /// 9 | /// Instead of implementing the bitfield over an integral type, this implements it on an array of 10 | /// bytes. Each flag is defined using the byte and the mask _within that byte_. It does not 11 | /// support masks across bytes. 12 | /// 13 | /// # Example 14 | /// 15 | /// See [`ChannelClassification`] and [`event::command::CommandFlags`] for examples in this crate. 16 | /// 17 | /// Basic usage is similar to [bitflags](https://crates.io/crates/bitflags): 18 | /// 19 | /// ``` 20 | /// # #[macro_use] 21 | /// # extern crate bluetooth_hci; 22 | /// # fn main() {} 23 | /// bitflag_array! { 24 | /// #[derive(Clone)] 25 | /// pub struct Flags : 3; // Bit field over a [u8; 3] 26 | /// pub struct Flag; // Name the internal struct 27 | /// 28 | /// const ALPHA = 0, 0x01; // First byte, first bit 29 | /// const BETA = 0, 0x02; 30 | /// // ... 31 | /// const THETA = 1, 0x01; // Second byte, first bit 32 | /// // ... 33 | /// const OMEGA = 2, 0x80; // Third byte, last bit 34 | /// } 35 | /// ``` 36 | /// 37 | /// A subset of the bitflags interface is implemented, including bitwise OR operations: 38 | /// ``` 39 | /// # #[macro_use] 40 | /// # extern crate bluetooth_hci; 41 | /// # bitflag_array! { 42 | /// # #[derive(Clone)] 43 | /// # pub struct Flags : 3; 44 | /// # pub struct Flag; 45 | /// # 46 | /// # const ALPHA = 0, 0x01; 47 | /// # const BETA = 0, 0x02; 48 | /// # const THETA = 1, 0x01; 49 | /// # const OMEGA = 2, 0x80; 50 | /// # } 51 | /// # fn main() { 52 | /// let mut letters = Flags::ALPHA | Flags::BETA; 53 | /// letters |= Flags::THETA; 54 | /// assert_eq!(letters.bits(), [0x03, 0x01, 0x00]); 55 | /// assert_eq!(letters.is_empty(), false); 56 | /// assert_eq!(letters.is_set(Flags::OMEGA), false); 57 | /// assert!(letters.contains(Flags::BETA | Flags::THETA)); 58 | /// # } 59 | /// ``` 60 | macro_rules! bitflag_array { 61 | { 62 | $(#[$inner:ident $($args:tt)*])* 63 | pub struct $flags:ident : $size:expr; 64 | pub struct $flag:ident; 65 | 66 | $( 67 | $(#[$var_inner:ident $($var_args:tt)*])* 68 | const $var:ident = $octet:expr, $mask:expr; 69 | )+ 70 | } => { 71 | $(#[$inner $($args)*])* 72 | pub struct $flags([u8; $size]); 73 | 74 | #[doc(hidden)] 75 | #[allow(missing_docs)] 76 | #[derive(Copy, Clone, Debug)] 77 | pub struct $flag { 78 | octet: usize, 79 | mask: u8, 80 | } 81 | 82 | impl $flags { 83 | $( 84 | $(#[$var_inner $($var_args)*])* 85 | pub const $var: $flag = $flag { 86 | octet: $octet, 87 | mask: $mask, 88 | }; 89 | )+ 90 | 91 | /// Attempts to create a bit field from the given byte array. If any unknown bit is 92 | /// set, returns None. 93 | pub fn from_bits(bits: &[u8]) -> Option<$flags> { 94 | assert_eq!(bits.len(), $size); 95 | 96 | let all_flags = $flags::all(); 97 | let all_bits = all_flags.bits(); 98 | for i in 0..$size { 99 | let provided_bits = bits[i]; 100 | let allowed_bits = all_bits[i]; 101 | if (provided_bits & !allowed_bits) != 0 { 102 | return None; 103 | } 104 | } 105 | 106 | let mut flags = $flags([0; $size]); 107 | flags.0.copy_from_slice(bits); 108 | Some(flags) 109 | } 110 | 111 | /// Copies the bitfield array into the given slice. The slice must have exactly the 112 | /// right number of elements. 113 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 114 | assert_eq!(self.0.len(), bytes.len()); 115 | bytes.copy_from_slice(&self.0); 116 | } 117 | 118 | /// Returns a bit field with all flags set. 119 | #[allow(deprecated)] 120 | #[allow(unused_doc_comments)] 121 | #[allow(unused_attributes)] 122 | pub fn all() -> $flags { 123 | let mut bits = [0; $size]; 124 | $( 125 | $(#[$var_inner $($var_args)*])* 126 | { 127 | bits[$octet] |= $mask; 128 | } 129 | )+ 130 | $flags(bits) 131 | } 132 | 133 | /// Returns a bit field with no flags set. 134 | pub fn empty() -> $flags { 135 | $flags([0; $size]) 136 | } 137 | 138 | /// Returns a slice to the underlying representation of the bit field. 139 | pub fn bits(&self) -> &[u8] { 140 | &self.0 141 | } 142 | 143 | /// Returns true if no fields are set. 144 | pub fn is_empty(&self) -> bool { 145 | self.0.iter().all(|&x| x == 0) 146 | } 147 | 148 | /// Returns true if the flag is set in the bitfield. 149 | pub fn is_set(&self, flag: $flag) -> bool { 150 | (self.0[flag.octet] & flag.mask) != 0 151 | } 152 | 153 | /// Returns true if all flags from `flags` are set in the bitfield. 154 | pub fn contains(&self, flags: $flags) -> bool { 155 | self.0 156 | .iter() 157 | .zip(flags.0.iter()) 158 | .all(|(a, b)| (a & b) == *b) 159 | } 160 | } 161 | 162 | impl $crate::bitflag_array::_core::ops::BitOr for $flag { 163 | type Output = $flags; 164 | 165 | fn bitor(self, rhs: $flag) -> Self::Output { 166 | let mut flags = $flags([0; $size]); 167 | flags.0[self.octet] |= self.mask; 168 | flags.0[rhs.octet] |= rhs.mask; 169 | 170 | flags 171 | } 172 | } 173 | 174 | impl $crate::bitflag_array::_core::ops::BitOr<$flag> for $flags { 175 | type Output = $flags; 176 | 177 | fn bitor(mut self, rhs: $flag) -> Self::Output { 178 | self |= rhs; 179 | 180 | self 181 | } 182 | } 183 | 184 | impl $crate::bitflag_array::_core::ops::BitOrAssign<$flag> for $flags { 185 | fn bitor_assign(&mut self, rhs: $flag) { 186 | self.0[rhs.octet] |= rhs.mask; 187 | } 188 | } 189 | 190 | impl $crate::bitflag_array::_core::cmp::PartialEq<$flag> for $flags { 191 | fn eq(&self, rhs: &$flag) -> bool { 192 | for i in 0..$size { 193 | if i == rhs.octet as usize { 194 | if self.0[i] != rhs.mask { 195 | return false; 196 | } 197 | } else if self.0[i] != 0 { 198 | return false; 199 | } 200 | } 201 | 202 | return true; 203 | } 204 | } 205 | 206 | impl $crate::bitflag_array::_core::cmp::PartialEq for $flags { 207 | fn eq(&self, rhs: &$flags) -> bool { 208 | self.0.iter().zip(rhs.0.iter()).all(|(a, b)| a == b) 209 | } 210 | } 211 | 212 | impl $crate::bitflag_array::_core::convert::From<$flag> for $flags { 213 | fn from(value: $flag) -> $flags { 214 | let mut flags = $flags([0; $size]); 215 | flags.0[value.octet] = value.mask; 216 | 217 | flags 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tests/connection_interval.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | use hci::types::{ 4 | ConnectionInterval, ConnectionIntervalBuilder, ConnectionIntervalError, FixedConnectionInterval, 5 | }; 6 | use std::time::Duration; 7 | 8 | #[test] 9 | fn valid() { 10 | let interval = ConnectionIntervalBuilder::new() 11 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 12 | .with_latency(10) 13 | .with_supervision_timeout(Duration::from_secs(15)) 14 | .build() 15 | .unwrap(); 16 | let mut bytes = [0; 8]; 17 | interval.copy_into_slice(&mut bytes); 18 | 19 | // 50 ms / 1.25 ms = 40 = 0x0028 20 | // 500 ms / 1.25 ms = 400 = 0x0190 21 | // 15000 ms / 10 ms = 1500 = 0x05DC 22 | assert_eq!(bytes, [0x28, 0x00, 0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05]); 23 | } 24 | 25 | #[test] 26 | fn incomplete() { 27 | assert_eq!( 28 | ConnectionIntervalBuilder::new() 29 | .with_latency(10) 30 | .with_supervision_timeout(Duration::from_secs(15)) 31 | .build() 32 | .err() 33 | .unwrap(), 34 | ConnectionIntervalError::Incomplete 35 | ); 36 | assert_eq!( 37 | ConnectionIntervalBuilder::new() 38 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 39 | .with_supervision_timeout(Duration::from_secs(15)) 40 | .build() 41 | .err() 42 | .unwrap(), 43 | ConnectionIntervalError::Incomplete 44 | ); 45 | assert_eq!( 46 | ConnectionIntervalBuilder::new() 47 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 48 | .with_latency(10) 49 | .build() 50 | .err() 51 | .unwrap(), 52 | ConnectionIntervalError::Incomplete 53 | ); 54 | } 55 | 56 | #[test] 57 | fn too_short() { 58 | let err = ConnectionIntervalBuilder::new() 59 | .with_range(Duration::from_millis(4), Duration::from_millis(1000)) 60 | .with_latency(10) 61 | .with_supervision_timeout(Duration::from_secs(15)) 62 | .build() 63 | .err() 64 | .unwrap(); 65 | assert_eq!( 66 | err, 67 | ConnectionIntervalError::IntervalTooShort(Duration::from_millis(4)) 68 | ); 69 | } 70 | 71 | #[test] 72 | fn too_long() { 73 | let err = ConnectionIntervalBuilder::new() 74 | .with_range(Duration::from_millis(100), Duration::from_millis(4001)) 75 | .with_latency(10) 76 | .with_supervision_timeout(Duration::from_secs(15)) 77 | .build() 78 | .err() 79 | .unwrap(); 80 | assert_eq!( 81 | err, 82 | ConnectionIntervalError::IntervalTooLong(Duration::from_millis(4001)) 83 | ); 84 | } 85 | 86 | #[test] 87 | fn inverted() { 88 | let err = ConnectionIntervalBuilder::new() 89 | .with_range(Duration::from_millis(500), Duration::from_millis(499)) 90 | .with_latency(10) 91 | .with_supervision_timeout(Duration::from_secs(15)) 92 | .build() 93 | .err() 94 | .unwrap(); 95 | assert_eq!( 96 | err, 97 | ConnectionIntervalError::IntervalInverted( 98 | Duration::from_millis(500), 99 | Duration::from_millis(499) 100 | ) 101 | ); 102 | } 103 | 104 | #[test] 105 | fn bad_conn_latency() { 106 | let err = ConnectionIntervalBuilder::new() 107 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 108 | .with_latency(500) 109 | .with_supervision_timeout(Duration::from_secs(15)) 110 | .build() 111 | .err() 112 | .unwrap(); 113 | assert_eq!(err, ConnectionIntervalError::BadConnectionLatency(500)); 114 | } 115 | 116 | #[test] 117 | fn supervision_timeout_too_short_absolute() { 118 | let err = ConnectionIntervalBuilder::new() 119 | .with_range(Duration::from_micros(7500), Duration::from_micros(7500)) 120 | .with_latency(0) 121 | .with_supervision_timeout(Duration::from_millis(99)) 122 | .build() 123 | .err() 124 | .unwrap(); 125 | 126 | // The relative minimum supervision timeout here would be 15 ms (7.5 ms * (1 + 0) * 2), so our 127 | // timeout would meet that requirement. However, it is lower than the absolute minimum. 128 | assert_eq!( 129 | err, 130 | ConnectionIntervalError::SupervisionTimeoutTooShort( 131 | Duration::from_millis(99), 132 | Duration::from_millis(100) 133 | ) 134 | ); 135 | } 136 | 137 | #[test] 138 | fn supervision_timeout_too_short_relative() { 139 | let err = ConnectionIntervalBuilder::new() 140 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 141 | .with_latency(10) 142 | .with_supervision_timeout(Duration::from_millis(10999)) 143 | .build() 144 | .err() 145 | .unwrap(); 146 | 147 | // The relative minimum supervision timeout here is be 11 s (500 ms * (1 + 10) * 2). 148 | assert_eq!( 149 | err, 150 | ConnectionIntervalError::SupervisionTimeoutTooShort( 151 | Duration::from_millis(10999), 152 | Duration::from_secs(11) 153 | ) 154 | ); 155 | } 156 | 157 | #[test] 158 | fn supervision_timeout_too_long() { 159 | let err = ConnectionIntervalBuilder::new() 160 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 161 | .with_latency(10) 162 | .with_supervision_timeout(Duration::from_millis(32001)) 163 | .build() 164 | .err() 165 | .unwrap(); 166 | assert_eq!( 167 | err, 168 | ConnectionIntervalError::SupervisionTimeoutTooLong(Duration::from_millis(32001)) 169 | ); 170 | } 171 | 172 | #[test] 173 | fn impossible_supervision_timeout() { 174 | let err = ConnectionIntervalBuilder::new() 175 | .with_range(Duration::from_millis(50), Duration::from_secs(4)) 176 | .with_latency(4) 177 | .with_supervision_timeout(Duration::from_secs(32)) 178 | .build() 179 | .err() 180 | .unwrap(); 181 | assert_eq!( 182 | err, 183 | ConnectionIntervalError::ImpossibleSupervisionTimeout(Duration::from_secs(40)) 184 | ); 185 | } 186 | 187 | #[test] 188 | fn from_bytes_valid() { 189 | let valid_bytes = [0x90, 0x00, 0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05]; 190 | let interval = ConnectionInterval::from_bytes(&valid_bytes).unwrap(); 191 | let mut bytes = [0; 8]; 192 | interval.copy_into_slice(&mut bytes); 193 | assert_eq!(bytes, valid_bytes); 194 | } 195 | 196 | #[test] 197 | fn fixed_from_bytes_valid() { 198 | let valid_bytes = [0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05]; 199 | let interval = FixedConnectionInterval::from_bytes(&valid_bytes).unwrap(); 200 | assert_eq!(interval.interval(), Duration::from_millis(0x190 * 5 / 4)); 201 | assert_eq!(interval.conn_latency(), 0x0A); 202 | assert_eq!( 203 | interval.supervision_timeout(), 204 | Duration::from_millis(10 * 0x05DC) 205 | ); 206 | } 207 | 208 | #[test] 209 | fn from_bytes_interval_too_short() { 210 | let bytes = [0x05, 0x00, 0x09, 0x00, 0x0A, 0x00, 0xDC, 0x05]; 211 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 212 | assert_eq!( 213 | err, 214 | ConnectionIntervalError::IntervalTooShort(Duration::from_micros(6250)) 215 | ); 216 | } 217 | 218 | #[test] 219 | fn fixed_from_bytes_interval_too_short() { 220 | let bytes = [0x05, 0x00, 0x0A, 0x00, 0xDC, 0x05]; 221 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 222 | assert_eq!( 223 | err, 224 | ConnectionIntervalError::IntervalTooShort(Duration::from_micros(6250)) 225 | ); 226 | } 227 | 228 | #[test] 229 | fn from_bytes_interval_too_long() { 230 | let bytes = [0x90, 0x00, 0x81, 0x0c, 0x0A, 0x00, 0xDC, 0x05]; 231 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 232 | assert_eq!( 233 | err, 234 | ConnectionIntervalError::IntervalTooLong(Duration::from_micros(4_001_250)) 235 | ); 236 | } 237 | 238 | #[test] 239 | fn fixed_from_bytes_interval_too_long() { 240 | let bytes = [0x81, 0x0c, 0x0A, 0x00, 0xDC, 0x05]; 241 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 242 | assert_eq!( 243 | err, 244 | ConnectionIntervalError::IntervalTooLong(Duration::from_micros(4_001_250)) 245 | ); 246 | } 247 | 248 | #[test] 249 | fn from_bytes_bad_connection_latency() { 250 | let bytes = [0x90, 0x00, 0x90, 0x01, 0xF4, 0x01, 0xDC, 0x05]; 251 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 252 | assert_eq!(err, ConnectionIntervalError::BadConnectionLatency(500)); 253 | } 254 | 255 | #[test] 256 | fn fixed_from_bytes_bad_connection_latency() { 257 | let bytes = [0x90, 0x01, 0xF4, 0x01, 0xDC, 0x05]; 258 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 259 | assert_eq!(err, ConnectionIntervalError::BadConnectionLatency(500)); 260 | } 261 | 262 | #[test] 263 | fn from_bytes_supervision_timeout_too_short_absolute() { 264 | let bytes = [0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00]; 265 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 266 | assert_eq!( 267 | err, 268 | ConnectionIntervalError::SupervisionTimeoutTooShort( 269 | Duration::from_millis(90), 270 | Duration::from_millis(100) 271 | ) 272 | ); 273 | } 274 | 275 | #[test] 276 | fn fixed_from_bytes_supervision_timeout_too_short_absolute() { 277 | let bytes = [0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00]; 278 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 279 | assert_eq!( 280 | err, 281 | ConnectionIntervalError::SupervisionTimeoutTooShort( 282 | Duration::from_millis(90), 283 | Duration::from_millis(100) 284 | ) 285 | ); 286 | } 287 | 288 | #[test] 289 | fn from_bytes_supervision_timeout_too_short_relative() { 290 | let bytes = [0x90, 0x00, 0x90, 0x01, 0x0A, 0x00, 0x4b, 0x04]; 291 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 292 | assert_eq!( 293 | err, 294 | ConnectionIntervalError::SupervisionTimeoutTooShort( 295 | Duration::from_millis(10990), 296 | Duration::from_secs(11) 297 | ) 298 | ); 299 | } 300 | 301 | #[test] 302 | fn fixed_from_bytes_supervision_timeout_too_short_relative() { 303 | let bytes = [0x90, 0x01, 0x0A, 0x00, 0x4b, 0x04]; 304 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 305 | assert_eq!( 306 | err, 307 | ConnectionIntervalError::SupervisionTimeoutTooShort( 308 | Duration::from_millis(10990), 309 | Duration::from_secs(11) 310 | ) 311 | ); 312 | } 313 | 314 | #[test] 315 | fn from_bytes_supervision_timeout_too_long() { 316 | let bytes = [0x90, 0x00, 0x90, 0x01, 0x0A, 0x00, 0x81, 0x0c]; 317 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 318 | assert_eq!( 319 | err, 320 | ConnectionIntervalError::SupervisionTimeoutTooLong(Duration::from_millis(32_010)) 321 | ); 322 | } 323 | 324 | #[test] 325 | fn fixed_from_bytes_supervision_timeout_too_long() { 326 | let bytes = [0x90, 0x01, 0x0A, 0x00, 0x81, 0x0c]; 327 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 328 | assert_eq!( 329 | err, 330 | ConnectionIntervalError::SupervisionTimeoutTooLong(Duration::from_millis(32_010)) 331 | ); 332 | } 333 | 334 | #[test] 335 | fn from_bytes_supervision_timeout_impossible() { 336 | let bytes = [0x90, 0x00, 0x80, 0x0C, 0x03, 0x00, 0x80, 0x0c]; 337 | let err = ConnectionInterval::from_bytes(&bytes).err().unwrap(); 338 | assert_eq!( 339 | err, 340 | ConnectionIntervalError::ImpossibleSupervisionTimeout(Duration::from_millis(32_000)) 341 | ); 342 | } 343 | 344 | #[test] 345 | fn fixed_from_bytes_supervision_timeout_impossible() { 346 | let bytes = [0x80, 0x0C, 0x03, 0x00, 0x80, 0x0c]; 347 | let err = FixedConnectionInterval::from_bytes(&bytes).err().unwrap(); 348 | assert_eq!( 349 | err, 350 | ConnectionIntervalError::ImpossibleSupervisionTimeout(Duration::from_millis(32_000)) 351 | ); 352 | } 353 | -------------------------------------------------------------------------------- /src/types/connection_interval.rs: -------------------------------------------------------------------------------- 1 | //! Types related to the connection interval. 2 | 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use core::cmp; 5 | use core::time::Duration; 6 | 7 | /// Define a connection interval range with its latency and supervision timeout. This value is 8 | /// passed to the controller, which determines the [actual connection 9 | /// interval](crate::types::FixedConnectionInterval). 10 | #[derive(Copy, Clone, Debug)] 11 | pub struct ConnectionInterval { 12 | interval_: (Duration, Duration), 13 | conn_latency_: u16, 14 | supervision_timeout_: Duration, 15 | } 16 | 17 | impl ConnectionInterval { 18 | /// Returns the connection interval. 19 | pub fn interval(&self) -> (Duration, Duration) { 20 | self.interval_ 21 | } 22 | 23 | /// Returns the connection latency, in number of events. 24 | pub fn conn_latency(&self) -> u16 { 25 | self.conn_latency_ 26 | } 27 | 28 | /// Returns the supervision timeout. 29 | pub fn supervision_timeout(&self) -> Duration { 30 | self.supervision_timeout_ 31 | } 32 | 33 | /// Serializes the connection interval into the given byte buffer. 34 | /// 35 | /// The interval is serialized as: 36 | /// - The minimum interval value, appropriately converted (2 bytes) 37 | /// - The maximum interval value, appropriately converted (2 bytes) 38 | /// - The connection latency (2 bytes) 39 | /// - The supervision timeout, appropriately converted (2 bytes) 40 | /// 41 | /// # Panics 42 | /// 43 | /// The provided buffer must be at least 8 bytes long. 44 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 45 | assert!(bytes.len() >= 8); 46 | 47 | LittleEndian::write_u16(&mut bytes[0..2], Self::interval_as_u16(self.interval_.0)); 48 | LittleEndian::write_u16(&mut bytes[2..4], Self::interval_as_u16(self.interval_.1)); 49 | LittleEndian::write_u16(&mut bytes[4..6], self.conn_latency_); 50 | LittleEndian::write_u16( 51 | &mut bytes[6..8], 52 | Self::timeout_as_u16(self.supervision_timeout_), 53 | ); 54 | } 55 | 56 | /// Deserializes the connection interval from the given byte buffer. 57 | /// 58 | /// - The minimum interval value, appropriately converted (2 bytes) 59 | /// - The maximum interval value, appropriately converted (2 bytes) 60 | /// - The connection latency (2 bytes) 61 | /// - The supervision timeout, appropriately converted (2 bytes) 62 | /// 63 | /// # Panics 64 | /// 65 | /// The provided buffer must be at least 8 bytes long. 66 | /// 67 | /// # Errors 68 | /// 69 | /// Any of the errors from the [builder](ConnectionIntervalBuilder::build) except for 70 | /// Incomplete. 71 | pub fn from_bytes(bytes: &[u8]) -> Result { 72 | assert!(bytes.len() >= 8); 73 | 74 | // Do the error checking with the standard connection interval builder. The min and max of 75 | // the interval range are allowed to be equal. 76 | let interval_min = 77 | Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[0..2])); 78 | let interval_max = 79 | Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[2..4])); 80 | let latency = LittleEndian::read_u16(&bytes[4..6]); 81 | let timeout = Duration::from_millis(10) * u32::from(LittleEndian::read_u16(&bytes[6..8])); 82 | ConnectionIntervalBuilder::new() 83 | .with_range(interval_min, interval_max) 84 | .with_latency(latency) 85 | .with_supervision_timeout(timeout) 86 | .build() 87 | } 88 | 89 | fn interval_as_u16(d: Duration) -> u16 { 90 | // T ms = N * 1.25 ms 91 | // N = T / 1.25 ms 92 | // = T / (5/4) ms 93 | // = 4 * T ms / 5 ms 94 | // 95 | // Note: 1000 * 4 / 5 = 800 96 | ((800 * d.as_secs()) as u32 + 4 * d.subsec_millis() / 5) as u16 97 | } 98 | 99 | fn timeout_as_u16(d: Duration) -> u16 { 100 | // T ms = N * 10 ms 101 | // N = T ms / 10 ms 102 | ((100 * d.as_secs()) as u32 + d.subsec_millis() / 10) as u16 103 | } 104 | } 105 | 106 | /// Intermediate builder for the [`ConnectionInterval`]. 107 | #[derive(Default)] 108 | pub struct ConnectionIntervalBuilder { 109 | interval: Option<(Duration, Duration)>, 110 | conn_latency: Option, 111 | supervision_timeout: Option, 112 | } 113 | 114 | impl ConnectionIntervalBuilder { 115 | /// Initializes a new builder. 116 | pub fn new() -> ConnectionIntervalBuilder { 117 | ConnectionIntervalBuilder { 118 | interval: None, 119 | conn_latency: None, 120 | supervision_timeout: None, 121 | } 122 | } 123 | 124 | /// Sets the connection interval range. 125 | /// 126 | /// # Errors 127 | /// 128 | /// There are no errors from this function, but it may cause errors in 129 | /// [build](ConnectionIntervalBuilder::build) if: 130 | /// - `min` is greater than `max` 131 | /// - Either `min` or `max` is less than 7.5 ms or more than 4 seconds. 132 | /// - `max` leads to an invalid relative supervision timeout. 133 | pub fn with_range(&mut self, min: Duration, max: Duration) -> &mut ConnectionIntervalBuilder { 134 | self.interval = Some((min, max)); 135 | self 136 | } 137 | 138 | /// Sets the connection latency. 139 | /// 140 | /// # Errors 141 | /// 142 | /// There are no errors from this function, but it may cause errors in 143 | /// [build](ConnectionIntervalBuilder::build) if: 144 | /// - `latency` is 500 or greater. 145 | /// - `latency` leads to an invalid relative supervision timeout. 146 | pub fn with_latency(&mut self, latency: u16) -> &mut ConnectionIntervalBuilder { 147 | self.conn_latency = Some(latency); 148 | self 149 | } 150 | 151 | /// Sets the supervision timeout. 152 | /// 153 | /// # Errors 154 | /// 155 | /// There are no errors from this function, but it may cause errors in 156 | /// [build](ConnectionIntervalBuilder::build) if: 157 | /// - `timeout` less than 100 ms or greater than 32 seconds 158 | /// - `timeout` results in an invalid relative supervision timeout. 159 | pub fn with_supervision_timeout( 160 | &mut self, 161 | timeout: Duration, 162 | ) -> &mut ConnectionIntervalBuilder { 163 | self.supervision_timeout = Some(timeout); 164 | self 165 | } 166 | 167 | /// Builds the connection interval if all parameters are valid. 168 | /// 169 | /// # Errors 170 | /// 171 | /// - [Incomplete](ConnectionIntervalError::Incomplete) if any of 172 | /// [`with_range`](ConnectionIntervalBuilder::with_range), 173 | /// [`with_latency`](ConnectionIntervalBuilder::with_latency), or 174 | /// [`with_supervision_timeout`](ConnectionIntervalBuilder::with_supervision_timeout) have not 175 | /// been called. 176 | /// - [IntervalTooShort](ConnectionIntervalError::IntervalTooShort) if the minimum range value 177 | /// is less than 7.5 ms. 178 | /// - [IntervalTooLong](ConnectionIntervalError::IntervalTooLong) if the maximum range value 179 | /// is greater than 4 seconds. 180 | /// - [IntervalInverted](ConnectionIntervalError::IntervalInverted) if the minimum range value 181 | /// is greater than the maximum. 182 | /// - [BadConnectionLatency](ConnectionIntervalError::BadConnectionLatency) if the connection 183 | /// latency is 500 or more. 184 | /// - [SupervisionTimeoutTooShort](ConnectionIntervalError::SupervisionTimeoutTooShort) if the 185 | /// supervision timeout is less than 100 ms, or if it is less than the computed minimum: (1 + 186 | /// latency) * interval max * 2. 187 | /// - [SupervisionTimeoutTooLong](ConnectionIntervalError::SupervisionTimeoutTooLong) if the 188 | /// supervision timeout is more than 32 seconds. 189 | /// - [ImpossibleSupervisionTimeout](ConnectionIntervalError::ImpossibleSupervisionTimeout) if 190 | /// the computed minimum supervision timeout ((1 + latency) * interval max * 2) is 32 seconds 191 | /// or more. 192 | pub fn build(&self) -> Result { 193 | if self.interval.is_none() 194 | || self.conn_latency.is_none() 195 | || self.supervision_timeout.is_none() 196 | { 197 | return Err(ConnectionIntervalError::Incomplete); 198 | } 199 | 200 | let interval = self.interval.unwrap(); 201 | const INTERVAL_MIN: Duration = Duration::from_micros(7500); 202 | if interval.0 < INTERVAL_MIN { 203 | return Err(ConnectionIntervalError::IntervalTooShort(interval.0)); 204 | } 205 | 206 | const INTERVAL_MAX: Duration = Duration::from_secs(4); 207 | if interval.1 > INTERVAL_MAX { 208 | return Err(ConnectionIntervalError::IntervalTooLong(interval.1)); 209 | } 210 | 211 | if interval.0 > interval.1 { 212 | return Err(ConnectionIntervalError::IntervalInverted( 213 | interval.0, interval.1, 214 | )); 215 | } 216 | 217 | let conn_latency = self.conn_latency.unwrap(); 218 | const LATENCY_MAX: u16 = 0x1F3; 219 | if conn_latency > LATENCY_MAX { 220 | return Err(ConnectionIntervalError::BadConnectionLatency(conn_latency)); 221 | } 222 | 223 | let supervision_timeout = self.supervision_timeout.unwrap(); 224 | let computed_timeout_min = interval.1 * (1 + u32::from(conn_latency)) * 2; 225 | const TIMEOUT_MAX: Duration = Duration::from_secs(32); 226 | if computed_timeout_min >= TIMEOUT_MAX { 227 | return Err(ConnectionIntervalError::ImpossibleSupervisionTimeout( 228 | computed_timeout_min, 229 | )); 230 | } 231 | 232 | const TIMEOUT_ABS_MIN: Duration = Duration::from_millis(100); 233 | let timeout_min = cmp::max(computed_timeout_min, TIMEOUT_ABS_MIN); 234 | if supervision_timeout <= timeout_min { 235 | return Err(ConnectionIntervalError::SupervisionTimeoutTooShort( 236 | supervision_timeout, 237 | timeout_min, 238 | )); 239 | } 240 | 241 | if supervision_timeout > TIMEOUT_MAX { 242 | return Err(ConnectionIntervalError::SupervisionTimeoutTooLong( 243 | supervision_timeout, 244 | )); 245 | } 246 | 247 | Ok(ConnectionInterval { 248 | interval_: interval, 249 | conn_latency_: conn_latency, 250 | supervision_timeout_: supervision_timeout, 251 | }) 252 | } 253 | } 254 | 255 | /// Types of errors that can occure when creating a [`ConnectionInterval`]. 256 | #[derive(Copy, Clone, Debug, PartialEq)] 257 | pub enum ConnectionIntervalError { 258 | /// At least one of any of [`with_range`](ConnectionIntervalBuilder::with_range), 259 | /// [`with_latency`](ConnectionIntervalBuilder::with_latency), or 260 | /// [`with_supervision_timeout`](ConnectionIntervalBuilder::with_supervision_timeout) has not 261 | /// been called. 262 | Incomplete, 263 | /// The minimum range value is less than 7.5 ms. Includes the invalid value. 264 | IntervalTooShort(Duration), 265 | /// The maximum range value is greater than 4 seconds. Includes the invalid value. 266 | IntervalTooLong(Duration), 267 | /// The minimum range value is greater than the maximum. Includes the provided minimum and 268 | /// maximum, respectively. 269 | IntervalInverted(Duration, Duration), 270 | /// The connection latency is 500 or more. Includes the provided value. 271 | BadConnectionLatency(u16), 272 | /// The supervision timeout is less than 100 ms, or it is less than the computed minimum: (1 + 273 | /// latency) * interval max * 2. The first value is the provided timeout; the second is the 274 | /// required minimum. 275 | SupervisionTimeoutTooShort(Duration, Duration), 276 | /// The supervision timeout is more than 32 seconds. Includes the provided timeout. 277 | SupervisionTimeoutTooLong(Duration), 278 | /// The computed minimum supervision timeout ((1 + latency) * interval max * 2) is 32 seconds 279 | /// or more. Includes the computed minimum. 280 | ImpossibleSupervisionTimeout(Duration), 281 | } 282 | 283 | /// Define a connection interval with its latency and supervision timeout. This value is 284 | /// returned from the controller. 285 | #[derive(Copy, Clone, Debug)] 286 | pub struct FixedConnectionInterval { 287 | interval_: Duration, 288 | conn_latency_: u16, 289 | supervision_timeout_: Duration, 290 | } 291 | 292 | impl FixedConnectionInterval { 293 | /// Deserializes the connection interval from the given byte buffer. 294 | /// 295 | /// - The interval value, appropriately converted (2 bytes) 296 | /// - The connection latency (2 bytes) 297 | /// - The supervision timeout, appropriately converted (2 bytes) 298 | /// 299 | /// # Panics 300 | /// 301 | /// The provided buffer must be at least 6 bytes long. 302 | /// 303 | /// # Errors 304 | /// 305 | /// Any of the errors from the [builder](ConnectionIntervalBuilder::build) except for 306 | /// Incomplete. 307 | pub fn from_bytes(bytes: &[u8]) -> Result { 308 | assert!(bytes.len() >= 6); 309 | 310 | // Do the error checking with the standard connection interval builder. The min and max of 311 | // the interval range are allowed to be equal. 312 | let interval = 313 | Duration::from_micros(1_250) * u32::from(LittleEndian::read_u16(&bytes[0..2])); 314 | let latency = LittleEndian::read_u16(&bytes[2..4]); 315 | let timeout = Duration::from_millis(10) * u32::from(LittleEndian::read_u16(&bytes[4..6])); 316 | ConnectionIntervalBuilder::new() 317 | .with_range(interval, interval) 318 | .with_latency(latency) 319 | .with_supervision_timeout(timeout) 320 | .build()?; 321 | 322 | Ok(FixedConnectionInterval { 323 | interval_: interval, 324 | conn_latency_: latency, 325 | supervision_timeout_: timeout, 326 | }) 327 | } 328 | 329 | /// Returns the connection interval. 330 | pub fn interval(&self) -> Duration { 331 | self.interval_ 332 | } 333 | 334 | /// Returns the connection latency, in number of events. 335 | pub fn conn_latency(&self) -> u16 { 336 | self.conn_latency_ 337 | } 338 | 339 | /// Returns the supervision timeout. 340 | pub fn supervision_timeout(&self) -> Duration { 341 | self.supervision_timeout_ 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /tests/event.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | mod vendor; 4 | 5 | use hci::event::*; 6 | use std::time::Duration; 7 | use vendor::VendorStatus; 8 | 9 | type TestEvent = Event; 10 | 11 | #[test] 12 | fn connection_complete() { 13 | let buffer = [ 14 | 0x03, 11, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 15 | ]; 16 | match TestEvent::new(Packet(&buffer)) { 17 | Ok(Event::ConnectionComplete(event)) => { 18 | assert_eq!(event.status, hci::Status::Success); 19 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 20 | assert_eq!( 21 | event.bd_addr, 22 | hci::BdAddr([0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) 23 | ); 24 | assert_eq!(event.link_type, LinkType::Sco); 25 | assert_eq!(event.encryption_enabled, false); 26 | } 27 | other => panic!("Did not get connection complete event: {:?}", other), 28 | } 29 | } 30 | 31 | #[test] 32 | fn connection_complete_failed_bad_status() { 33 | let buffer = [ 34 | 0x03, 11, 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 35 | ]; 36 | match TestEvent::new(Packet(&buffer)) { 37 | Err(Error::BadStatus(0x80)) => (), 38 | other => panic!("Did not get bad status: {:?}", other), 39 | } 40 | } 41 | 42 | #[test] 43 | fn connection_complete_failed_bad_link_type() { 44 | let buffer = [ 45 | 0x03, 11, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x02, 0x00, 46 | ]; 47 | match TestEvent::new(Packet(&buffer)) { 48 | Err(Error::BadLinkType(0x02)) => (), 49 | other => panic!("Did not get bad connection link type: {:?}", other), 50 | } 51 | } 52 | 53 | #[test] 54 | fn connection_complete_failed_encryption_enabled() { 55 | let buffer = [ 56 | 0x03, 11, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 57 | ]; 58 | match TestEvent::new(Packet(&buffer)) { 59 | Err(Error::BadEncryptionEnabledValue(0x02)) => (), 60 | other => panic!("Did not get bad connection link type: {:?}", other), 61 | } 62 | } 63 | 64 | #[test] 65 | fn disconnection_complete() { 66 | let buffer = [0x05, 4, 0, 0x01, 0x02, 0]; 67 | match TestEvent::new(Packet(&buffer)) { 68 | Ok(Event::DisconnectionComplete(event)) => { 69 | assert_eq!(event.status, hci::Status::Success); 70 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 71 | assert_eq!(event.reason, hci::Status::Success); 72 | } 73 | other => panic!("Did not get disconnection complete event: {:?}", other), 74 | } 75 | } 76 | 77 | #[test] 78 | fn disconnection_complete_failed_bad_status() { 79 | let buffer = [0x05, 4, 0x80, 0x01, 0x02, 0]; 80 | match TestEvent::new(Packet(&buffer)) { 81 | Err(Error::BadStatus(0x80)) => (), 82 | other => panic!("Did not get bad status: {:?}", other), 83 | } 84 | } 85 | 86 | #[test] 87 | fn disconnection_complete_failed_bad_reason() { 88 | let buffer = [0x05, 4, 0, 0x01, 0x02, 0x80]; 89 | match TestEvent::new(Packet(&buffer)) { 90 | Err(Error::BadReason(0x80)) => (), 91 | other => panic!("Did not get bad reason: {:?}", other), 92 | } 93 | } 94 | 95 | #[test] 96 | fn encryption_change() { 97 | let buffer = [0x08, 4, 0x00, 0x01, 0x02, 0x00]; 98 | match TestEvent::new(Packet(&buffer)) { 99 | Ok(Event::EncryptionChange(event)) => { 100 | assert_eq!(event.status, hci::Status::Success); 101 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 102 | assert_eq!(event.encryption, Encryption::Off); 103 | } 104 | other => panic!("Did not get encryption change event: {:?}", other), 105 | } 106 | } 107 | 108 | #[test] 109 | fn encryption_change_failed_bad_status() { 110 | let buffer = [0x08, 4, 0x80, 0x01, 0x02, 0x00]; 111 | match TestEvent::new(Packet(&buffer)) { 112 | Err(Error::BadStatus(0x80)) => (), 113 | other => panic!("Did not get bad status: {:?}", other), 114 | } 115 | } 116 | 117 | #[test] 118 | fn encryption_change_failed_bad_encryption() { 119 | let buffer = [0x08, 4, 0x00, 0x01, 0x02, 0x03]; 120 | match TestEvent::new(Packet(&buffer)) { 121 | Err(Error::BadEncryptionType(0x03)) => (), 122 | other => panic!("Did not get bad encryption type: {:?}", other), 123 | } 124 | } 125 | 126 | #[test] 127 | fn read_remote_version_complete() { 128 | let buffer = [0x0C, 8, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; 129 | match TestEvent::new(Packet(&buffer)) { 130 | Ok(Event::ReadRemoteVersionInformationComplete(event)) => { 131 | assert_eq!(event.status, hci::Status::Success); 132 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 133 | assert_eq!(event.version, 0x03); 134 | assert_eq!(event.mfgr_name, 0x0504); 135 | assert_eq!(event.subversion, 0x0706); 136 | } 137 | other => panic!("Did not get read remote version info event: {:?}", other), 138 | } 139 | } 140 | 141 | #[test] 142 | fn read_remote_version_complete_failed_bad_status() { 143 | let buffer = [0x0C, 8, 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; 144 | match TestEvent::new(Packet(&buffer)) { 145 | Err(Error::BadStatus(0x80)) => (), 146 | other => panic!("Did not get bad status: {:?}", other), 147 | } 148 | } 149 | 150 | // The Command Complete event has its own set of tests in command_complete.rs 151 | 152 | #[test] 153 | fn command_status() { 154 | let buffer = [0x0F, 4, 0, 8, 0x01, 0x02]; 155 | match TestEvent::new(Packet(&buffer)) { 156 | Ok(Event::CommandStatus(event)) => { 157 | assert_eq!(event.num_hci_command_packets, 8); 158 | assert_eq!(event.status, hci::Status::Success); 159 | assert_eq!(event.opcode, hci::Opcode(0x0201)); 160 | } 161 | other => panic!("Did not get command status: {:?}", other), 162 | } 163 | } 164 | 165 | #[test] 166 | fn command_status_vendor_status() { 167 | let buffer = [0x0F, 4, 0x45, 8, 0x01, 0x02]; 168 | match TestEvent::new(Packet(&buffer)) { 169 | Ok(Event::CommandStatus(event)) => { 170 | assert_eq!(event.num_hci_command_packets, 8); 171 | assert_eq!(event.status, hci::Status::Vendor(VendorStatus::FourFive)); 172 | assert_eq!(event.opcode, hci::Opcode(0x0201)); 173 | } 174 | other => panic!("Did not get command status: {:?}", other), 175 | } 176 | } 177 | 178 | #[test] 179 | fn hardware_error() { 180 | let buffer = [0x10, 1, 0x12]; 181 | match TestEvent::new(Packet(&buffer)) { 182 | Ok(Event::HardwareError(event)) => { 183 | assert_eq!(event.code, 0x12); 184 | } 185 | other => panic!("Did not get hardware error: {:?}", other), 186 | } 187 | } 188 | 189 | #[test] 190 | fn number_of_completed_packets() { 191 | let buffer = [0x13, 9, 2, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; 192 | match TestEvent::new(Packet(&buffer)) { 193 | Ok(Event::NumberOfCompletedPackets(event)) => { 194 | let expected_conn_handles = 195 | [hci::ConnectionHandle(0x0201), hci::ConnectionHandle(0x0605)]; 196 | let expected_num_packets = [0x0403, 0x0807]; 197 | for (actual, (conn_handle, num_packets)) in event.iter().zip( 198 | expected_conn_handles 199 | .iter() 200 | .zip(expected_num_packets.iter()), 201 | ) { 202 | assert_eq!(actual.conn_handle, *conn_handle); 203 | assert_eq!(actual.num_completed_packets, *num_packets); 204 | } 205 | } 206 | other => panic!("Did not get number of completed packets: {:?}", other), 207 | } 208 | } 209 | 210 | #[test] 211 | fn data_buffer_overflow() { 212 | let buffer = [0x1A, 1, 0x00]; 213 | match TestEvent::new(Packet(&buffer)) { 214 | Ok(Event::DataBufferOverflow(event)) => { 215 | assert_eq!(event.link_type, LinkType::Sco); 216 | } 217 | other => panic!("Did not get data buffer overflow: {:?}", other), 218 | } 219 | } 220 | 221 | #[test] 222 | fn data_buffer_overflow_failed_bad_link_type() { 223 | let buffer = [0x1A, 1, 0x02]; 224 | match TestEvent::new(Packet(&buffer)) { 225 | Err(Error::BadLinkType(link_type)) => assert_eq!(link_type, 0x02), 226 | other => panic!("Did not get bad link type: {:?}", other), 227 | } 228 | } 229 | 230 | #[test] 231 | fn encryption_key_refresh_complete() { 232 | let buffer = [0x30, 3, 0, 0x01, 0x02]; 233 | match TestEvent::new(Packet(&buffer)) { 234 | Ok(Event::EncryptionKeyRefreshComplete(event)) => { 235 | assert_eq!(event.status, hci::Status::Success); 236 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 237 | } 238 | other => panic!("Did not get encryption key refresh complete: {:?}", other), 239 | } 240 | } 241 | 242 | #[test] 243 | fn le_connection_complete() { 244 | let buffer = [ 245 | 0x3E, 19, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 246 | 0x00, 0x0B, 0x00, 0x0D, 0x0A, 0x00, 247 | ]; 248 | match TestEvent::new(Packet(&buffer)) { 249 | Ok(Event::LeConnectionComplete(event)) => { 250 | assert_eq!(event.status, hci::Status::Success); 251 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 252 | assert_eq!(event.role, ConnectionRole::Central); 253 | assert_eq!( 254 | event.peer_bd_addr, 255 | hci::BdAddrType::Public(hci::BdAddr([0x03, 0x04, 0x05, 0x06, 0x07, 0x08])) 256 | ); 257 | 258 | // Connection interval time = value * 1.25 ms 259 | assert_eq!( 260 | event.conn_interval.interval(), 261 | Duration::from_micros(0x0009 * 1_250) 262 | ); 263 | assert_eq!(event.conn_interval.conn_latency(), 0x000B); 264 | 265 | // Supervision timeout = value * 10 ms 266 | assert_eq!( 267 | event.conn_interval.supervision_timeout(), 268 | Duration::from_millis(0x0A0D * 10) 269 | ); 270 | assert_eq!(event.central_clock_accuracy, CentralClockAccuracy::Ppm500); 271 | } 272 | other => panic!("Did not get LE connection complete: {:?}", other), 273 | } 274 | } 275 | 276 | #[test] 277 | fn le_connection_complete_failed_bad_role() { 278 | let buffer = [ 279 | 0x3E, 19, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 280 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x00, 281 | ]; 282 | match TestEvent::new(Packet(&buffer)) { 283 | Err(Error::BadLeConnectionRole(code)) => assert_eq!(code, 0x02), 284 | other => panic!("Did not get bad LE connection role: {:?}", other), 285 | } 286 | } 287 | 288 | #[test] 289 | fn le_connection_complete_failed_bad_address_type() { 290 | let buffer = [ 291 | 0x3E, 19, 0x01, 0x00, 0x01, 0x02, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 292 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x00, 293 | ]; 294 | match TestEvent::new(Packet(&buffer)) { 295 | Err(Error::BadLeAddressType(code)) => assert_eq!(code, 0x02), 296 | other => panic!("Did not get bad address type: {:?}", other), 297 | } 298 | } 299 | 300 | #[test] 301 | fn le_connection_complete_failed_bad_central_clock_accuracy() { 302 | let buffer = [ 303 | 0x3E, 19, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x07, 304 | 0x00, 0x0B, 0x00, 0x0D, 0x0B, 0x08, 305 | ]; 306 | match TestEvent::new(Packet(&buffer)) { 307 | Err(Error::BadLeCentralClockAccuracy(code)) => assert_eq!(code, 0x08), 308 | other => panic!("Did not get bad LE central clock accuracy: {:?}", other), 309 | } 310 | } 311 | 312 | #[test] 313 | fn le_advertising_report() { 314 | let buffer = [ 315 | 0x3E, 27, 0x02, 2, 0, 0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 2, 0x07, 0x08, 0x09, 1, 1, 316 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 3, 0x10, 0x11, 0x12, 0x13, 317 | ]; 318 | match TestEvent::new(Packet(&buffer)) { 319 | Ok(Event::LeAdvertisingReport(event)) => { 320 | let mut iter = event.iter(); 321 | let report = iter.next().unwrap(); 322 | assert_eq!(report.event_type, AdvertisementEvent::Advertisement); 323 | assert_eq!( 324 | report.address, 325 | hci::BdAddrType::Public(hci::BdAddr([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])) 326 | ); 327 | assert_eq!(report.data, [0x07, 0x08]); 328 | assert_eq!(report.rssi, Some(0x09)); 329 | 330 | let report = iter.next().unwrap(); 331 | assert_eq!(report.event_type, AdvertisementEvent::DirectAdvertisement); 332 | assert_eq!( 333 | report.address, 334 | hci::BdAddrType::Random(hci::BdAddr([0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F])) 335 | ); 336 | assert_eq!(report.data, [0x10, 0x11, 0x12]); 337 | assert_eq!(report.rssi, Some(0x13)); 338 | } 339 | other => panic!("Did not get advertising report: {:?}", other), 340 | } 341 | } 342 | 343 | #[test] 344 | fn le_advertising_report_failed_incomplete() { 345 | let buffer = [ 346 | 0x3E, 27, 0x02, 2, 0, 0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 2, 0x07, 0x08, 0x09, 1, 1, 347 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 4, 0x10, 0x11, 0x12, 0x13, 348 | ]; 349 | match TestEvent::new(Packet(&buffer)) { 350 | Err(Error::LeAdvertisementReportIncomplete) => (), 351 | other => panic!("Did not get incomplete advertising report: {:?}", other), 352 | } 353 | } 354 | 355 | #[test] 356 | fn le_advertising_report_failed_bad_advertisement_type() { 357 | let buffer = [ 358 | 0x3E, 14, 0x02, 1, 5, 0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 2, 0x07, 0x08, 0x09, 359 | ]; 360 | match TestEvent::new(Packet(&buffer)) { 361 | Err(Error::BadLeAdvertisementType(code)) => assert_eq!(code, 5), 362 | other => panic!("Did not get bad advertisement type: {:?}", other), 363 | } 364 | } 365 | 366 | #[test] 367 | fn le_advertising_report_failed_bad_addr_type() { 368 | let buffer = [ 369 | 0x3E, 14, 0x02, 1, 1, 4, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 2, 0x07, 0x08, 0x09, 370 | ]; 371 | match TestEvent::new(Packet(&buffer)) { 372 | Err(Error::BadLeAddressType(code)) => assert_eq!(code, 4), 373 | other => panic!("Did not get bad LE Address type: {:?}", other), 374 | } 375 | } 376 | 377 | #[test] 378 | fn le_connection_update_complete() { 379 | let buffer = [ 380 | 0x3E, 10, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x07, 0x08, 381 | ]; 382 | match TestEvent::new(Packet(&buffer)) { 383 | Ok(Event::LeConnectionUpdateComplete(event)) => { 384 | assert_eq!(event.status, hci::Status::Success); 385 | assert_eq!(event.conn_handle, hci::ConnectionHandle(0x0201)); 386 | assert_eq!( 387 | event.conn_interval.interval(), 388 | Duration::from_micros(0x0403 * 1_250) 389 | ); 390 | assert_eq!(event.conn_interval.conn_latency(), 0x0005); 391 | assert_eq!( 392 | event.conn_interval.supervision_timeout(), 393 | Duration::from_millis(10 * 0x0807) 394 | ); 395 | } 396 | other => panic!( 397 | "Did not get LE connection update complete event: {:?}", 398 | other 399 | ), 400 | } 401 | } 402 | 403 | #[cfg(feature = "version-4-1")] 404 | #[test] 405 | fn le_read_remote_used_features_complete() { 406 | let buffer = [ 407 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0b00010101, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 408 | ]; 409 | match TestEvent::new(Packet(&buffer)) { 410 | Ok(Event::LeReadRemoteUsedFeaturesComplete(event)) => { 411 | assert_eq!(event.status, ::hci::Status::Success); 412 | assert_eq!(event.conn_handle, ::hci::ConnectionHandle(0x0201)); 413 | assert_eq!( 414 | event.features, 415 | ::hci::LinkLayerFeature::LE_ENCRYPTION 416 | | ::hci::LinkLayerFeature::EXTENDED_REJECT_INDICATION 417 | | ::hci::LinkLayerFeature::LE_PING 418 | ); 419 | } 420 | other => panic!( 421 | "Did not get LE Read Remote Used Features Complete: {:?}", 422 | other 423 | ), 424 | } 425 | } 426 | 427 | #[cfg(feature = "version-4-1")] 428 | #[test] 429 | fn le_read_remote_used_features_complete_failed_bad_flag() { 430 | let buffer = [ 431 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0b00100000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 432 | ]; 433 | match TestEvent::new(Packet(&buffer)) { 434 | Err(Error::BadRemoteUsedFeatureFlag(flags)) => assert_eq!(flags, 0x0000_0000_0000_0020), 435 | other => panic!( 436 | "Did not get LE Read Remote Used Features Complete: {:?}", 437 | other 438 | ), 439 | } 440 | } 441 | 442 | #[cfg(feature = "version-4-2")] 443 | #[test] 444 | fn le_read_remote_used_features_complete() { 445 | let buffer = [ 446 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0b00100000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 447 | ]; 448 | match TestEvent::new(Packet(&buffer)) { 449 | Ok(Event::LeReadRemoteUsedFeaturesComplete(event)) => { 450 | assert_eq!(event.status, ::hci::Status::Success); 451 | assert_eq!(event.conn_handle, ::hci::ConnectionHandle(0x0201)); 452 | assert_eq!( 453 | event.features, 454 | ::hci::LinkLayerFeature::LE_DATA_PACKET_LENGTH_EXTENSION 455 | ); 456 | } 457 | other => panic!( 458 | "Did not get LE Read Remote Used Features Complete: {:?}", 459 | other 460 | ), 461 | } 462 | } 463 | 464 | #[cfg(feature = "version-4-2")] 465 | #[test] 466 | fn le_read_remote_used_features_complete_failed_bad_flag() { 467 | let buffer = [ 468 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 469 | ]; 470 | match TestEvent::new(Packet(&buffer)) { 471 | Err(Error::BadRemoteUsedFeatureFlag(flags)) => assert_eq!(flags, 0x0000_0000_0000_0100), 472 | other => panic!( 473 | "Did not get LE Read Remote Used Features Complete: {:?}", 474 | other 475 | ), 476 | } 477 | } 478 | 479 | #[cfg(feature = "version-5-0")] 480 | #[test] 481 | fn le_read_remote_used_features_complete() { 482 | let buffer = [ 483 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 484 | ]; 485 | match TestEvent::new(Packet(&buffer)) { 486 | Ok(Event::LeReadRemoteUsedFeaturesComplete(event)) => { 487 | assert_eq!(event.status, ::hci::Status::Success); 488 | assert_eq!(event.conn_handle, ::hci::ConnectionHandle(0x0201)); 489 | assert_eq!(event.features, ::hci::LinkLayerFeature::LE_2M_PHY); 490 | } 491 | other => panic!( 492 | "Did not get LE Read Remote Used Features Complete: {:?}", 493 | other 494 | ), 495 | } 496 | } 497 | 498 | #[cfg(feature = "version-5-0")] 499 | #[test] 500 | fn le_read_remote_used_features_complete_failed_bad_flag() { 501 | let buffer = [ 502 | 0x3E, 12, 0x04, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 503 | ]; 504 | match TestEvent::new(Packet(&buffer)) { 505 | Err(Error::BadRemoteUsedFeatureFlag(flags)) => assert_eq!(flags, 0x0000_0000_0002_0000), 506 | other => panic!( 507 | "Did not get LE Read Remote Used Features Complete: {:?}", 508 | other 509 | ), 510 | } 511 | } 512 | 513 | #[test] 514 | fn le_long_term_key_request() { 515 | let buffer = [ 516 | 0x3E, 13, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 517 | ]; 518 | match TestEvent::new(Packet(&buffer)) { 519 | Ok(Event::LeLongTermKeyRequest(event)) => { 520 | assert_eq!(event.conn_handle, ::hci::ConnectionHandle(0x0201)); 521 | assert_eq!(event.random_value, 0x0A09080706050403); 522 | assert_eq!(event.encrypted_diversifier, 0x0C0B); 523 | } 524 | other => panic!("Did not Get LE LTK Request: {:?}", other), 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /tests/host.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | extern crate nb; 3 | 4 | mod vendor; 5 | 6 | use hci::host::*; 7 | use std::time::Duration; 8 | use vendor::RecordingSink; 9 | 10 | #[test] 11 | fn disconnect() { 12 | let mut sink = RecordingSink::new(); 13 | sink.as_controller() 14 | .disconnect(hci::ConnectionHandle(0x0201), hci::Status::AuthFailure) 15 | .unwrap(); 16 | assert_eq!(sink.written_data, [1, 0x06, 0x04, 3, 0x01, 0x02, 0x05]); 17 | } 18 | 19 | #[test] 20 | fn disconnect_bad_reason() { 21 | let mut sink = RecordingSink::new(); 22 | let err = sink 23 | .as_controller() 24 | .disconnect(hci::ConnectionHandle(0x0201), hci::Status::UnknownCommand) 25 | .err() 26 | .unwrap(); 27 | assert_eq!( 28 | err, 29 | nb::Error::Other(Error::BadDisconnectionReason(hci::Status::UnknownCommand)) 30 | ); 31 | assert_eq!(sink.written_data, []); 32 | } 33 | 34 | macro_rules! conn_handle_only { 35 | { 36 | $($(#[$inner:ident $($args:tt)*])* 37 | $fn:ident($oc0:expr, $oc1:expr);)* 38 | } => { 39 | $( 40 | $(#[$inner $($args)*])* 41 | #[test] 42 | fn $fn() { 43 | let mut sink = RecordingSink::new(); 44 | sink.as_controller() 45 | .$fn(hci::ConnectionHandle(0x0201)) 46 | .unwrap(); 47 | assert_eq!(sink.written_data, [1, $oc0, $oc1, 2, 0x01, 0x02]); 48 | } 49 | )* 50 | } 51 | } 52 | 53 | conn_handle_only! { 54 | read_remote_version_information(0x1D, 0x04); 55 | read_rssi(0x05, 0x14); 56 | le_read_channel_map(0x15, 0x20); 57 | le_read_remote_used_features(0x16, 0x20); 58 | le_long_term_key_request_negative_reply(0x1B, 0x20); 59 | } 60 | 61 | macro_rules! no_params { 62 | { 63 | $($(#[$inner:ident $($args:tt)*])* 64 | $fn:ident($oc0:expr, $oc1:expr);)* 65 | } => { 66 | $( 67 | $(#[$inner $($args)*])* 68 | #[test] 69 | fn $fn() { 70 | let mut sink = RecordingSink::new(); 71 | sink.as_controller() 72 | .$fn() 73 | .unwrap(); 74 | assert_eq!(sink.written_data, [1, $oc0, $oc1, 0]); 75 | } 76 | )* 77 | } 78 | } 79 | 80 | no_params! { 81 | reset(0x03, 0x0C); 82 | read_local_version_information(0x01, 0x10); 83 | read_local_supported_commands(0x02, 0x10); 84 | read_local_supported_features(0x03, 0x10); 85 | read_bd_addr(0x09, 0x10); 86 | le_read_buffer_size(0x02, 0x20); 87 | le_read_local_supported_features(0x03, 0x20); 88 | le_read_advertising_channel_tx_power(0x07, 0x20); 89 | le_create_connection_cancel(0x0E, 0x20); 90 | le_read_white_list_size(0x0F, 0x20); 91 | le_clear_white_list(0x10, 0x20); 92 | le_rand(0x18, 0x20); 93 | le_read_supported_states(0x1C, 0x20); 94 | le_test_end(0x1F, 0x20); 95 | } 96 | 97 | #[test] 98 | fn set_event_mask() { 99 | let mut sink = RecordingSink::new(); 100 | sink.as_controller() 101 | .set_event_mask(EventFlags::INQUIRY_COMPLETE | EventFlags::AUTHENTICATION_COMPLETE) 102 | .unwrap(); 103 | assert_eq!( 104 | sink.written_data, 105 | [1, 0x01, 0x0C, 8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] 106 | ); 107 | } 108 | 109 | #[test] 110 | fn read_tx_power_level() { 111 | let mut sink = RecordingSink::new(); 112 | sink.as_controller() 113 | .read_tx_power_level(hci::ConnectionHandle(0x0201), TxPowerLevel::Current) 114 | .unwrap(); 115 | assert_eq!(sink.written_data, [1, 0x2D, 0x0C, 3, 0x01, 0x02, 0x00]) 116 | } 117 | 118 | #[test] 119 | fn le_set_event_mask() { 120 | let mut sink = RecordingSink::new(); 121 | sink.as_controller() 122 | .le_set_event_mask( 123 | LeEventFlags::CONNECTION_COMPLETE | LeEventFlags::REMOTE_CONNECTION_PARAMETER_REQUEST, 124 | ) 125 | .unwrap(); 126 | assert_eq!( 127 | sink.written_data, 128 | [1, 0x01, 0x20, 8, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] 129 | ); 130 | } 131 | 132 | #[test] 133 | fn le_set_random_address() { 134 | let mut sink = RecordingSink::new(); 135 | sink.as_controller() 136 | .le_set_random_address(hci::BdAddr([0x01, 0x02, 0x04, 0x08, 0x10, 0x20])) 137 | .unwrap(); 138 | assert_eq!( 139 | sink.written_data, 140 | [1, 0x05, 0x20, 6, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20] 141 | ); 142 | } 143 | 144 | #[test] 145 | fn le_set_random_address_invalid_addr_type() { 146 | let mut sink = RecordingSink::new(); 147 | for bd_addr in [ 148 | // The most significant bits of the BD ADDR must be either 11 (static address) or 00 149 | // (non-resolvable private address), or 10 (resolvable private address). An MSB of 01 is 150 | // not valid. 151 | hci::BdAddr([0x01, 0x02, 0x04, 0x08, 0x10, 0b01000000]), 152 | // The random part of a static address must contain at least one 0. 153 | hci::BdAddr([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), 154 | // The random part of a static address must contain at least one 1. 155 | hci::BdAddr([0x00, 0x00, 0x00, 0x00, 0x00, 0b11000000]), 156 | // The random part of a non-resolvable private address must contain at least one 0. 157 | hci::BdAddr([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0b00111111]), 158 | // The random part of a non-resolvable private address must contain at least one 1. 159 | hci::BdAddr([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), 160 | // The random part of a resolvable private address must contain at least one 0. The first 3 161 | // bytes are a hash, which can have any value. 162 | hci::BdAddr([0x01, 0x02, 0x04, 0xFF, 0xFF, 0b10111111]), 163 | // The random part of a resolvable private address must contain at least one 1. The first 3 164 | // bytes are a hash, which can have any value. 165 | hci::BdAddr([0x01, 0x02, 0x04, 0x00, 0x00, 0b10000000]), 166 | ] 167 | .iter() 168 | { 169 | let err = sink 170 | .as_controller() 171 | .le_set_random_address(*bd_addr) 172 | .err() 173 | .unwrap(); 174 | assert_eq!(err, nb::Error::Other(Error::BadRandomAddress(*bd_addr))); 175 | } 176 | assert_eq!(sink.written_data, []); 177 | } 178 | 179 | #[test] 180 | fn le_set_advertising_parameters() { 181 | let mut sink = RecordingSink::new(); 182 | sink.as_controller() 183 | .le_set_advertising_parameters(&AdvertisingParameters { 184 | advertising_interval: AdvertisingInterval::for_type( 185 | AdvertisingType::ConnectableUndirected, 186 | ) 187 | .with_range(Duration::from_millis(21), Duration::from_millis(1000)) 188 | .unwrap(), 189 | own_address_type: OwnAddressType::Public, 190 | peer_address: hci::BdAddrType::Random(hci::BdAddr([ 191 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 192 | ])), 193 | advertising_channel_map: Channels::CH_37 | Channels::CH_39, 194 | advertising_filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, 195 | }) 196 | .unwrap(); 197 | assert_eq!( 198 | sink.written_data, 199 | [ 200 | 1, 201 | 0x06, 202 | 0x20, 203 | 15, 204 | 0x21, // 0x21, 0x00 = 0x0021 = 33 ~= 21 ms / 0.625 ms 205 | 0x00, 206 | 0x40, // 0x40, 0x06 = 0x0640 = 1600 = 1000 ms / 0.625 ms 207 | 0x06, 208 | 0x00, 209 | 0x00, 210 | 0x01, 211 | 0x01, 212 | 0x02, 213 | 0x03, 214 | 0x04, 215 | 0x05, 216 | 0x06, 217 | 0b0000_0101, 218 | 0x00 219 | ] 220 | ); 221 | } 222 | 223 | #[test] 224 | fn le_set_advertising_parameters_bad_channel_map() { 225 | let mut sink = RecordingSink::new(); 226 | let err = sink 227 | .as_controller() 228 | .le_set_advertising_parameters(&AdvertisingParameters { 229 | advertising_interval: AdvertisingInterval::for_type( 230 | AdvertisingType::ConnectableUndirected, 231 | ) 232 | .with_range(Duration::from_millis(20), Duration::from_millis(1000)) 233 | .unwrap(), 234 | own_address_type: OwnAddressType::Public, 235 | peer_address: hci::BdAddrType::Random(hci::BdAddr([ 236 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 237 | ])), 238 | advertising_channel_map: Channels::empty(), 239 | advertising_filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, 240 | }) 241 | .err() 242 | .unwrap(); 243 | assert_eq!( 244 | err, 245 | nb::Error::Other(Error::BadChannelMap(Channels::empty())) 246 | ); 247 | assert_eq!(sink.written_data, []); 248 | } 249 | 250 | #[test] 251 | fn le_set_advertising_data_empty() { 252 | let mut sink = RecordingSink::new(); 253 | sink.as_controller().le_set_advertising_data(&[]).unwrap(); 254 | assert_eq!( 255 | sink.written_data, 256 | vec![ 257 | 1, 0x08, 0x20, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 259 | ] 260 | ); 261 | } 262 | 263 | #[test] 264 | fn le_set_advertising_data_partial() { 265 | let mut sink = RecordingSink::new(); 266 | sink.as_controller() 267 | .le_set_advertising_data(&[1, 2, 3, 4, 5, 6, 7, 8]) 268 | .unwrap(); 269 | assert_eq!( 270 | sink.written_data, 271 | vec![ 272 | 1, 0x08, 0x20, 32, 8, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 273 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 274 | ] 275 | ); 276 | } 277 | 278 | #[test] 279 | fn le_set_advertising_data_full() { 280 | let mut sink = RecordingSink::new(); 281 | sink.as_controller() 282 | .le_set_advertising_data(&[ 283 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 284 | 25, 26, 27, 28, 29, 30, 31, 285 | ]) 286 | .unwrap(); 287 | assert_eq!( 288 | sink.written_data, 289 | vec![ 290 | 1, 0x08, 0x20, 32, 31, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 291 | 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 292 | ] 293 | ); 294 | } 295 | 296 | #[test] 297 | fn le_set_advertising_data_too_long() { 298 | let mut sink = RecordingSink::new(); 299 | let err = sink 300 | .as_controller() 301 | .le_set_advertising_data(&[0; 32]) 302 | .err() 303 | .unwrap(); 304 | assert_eq!(err, nb::Error::Other(Error::AdvertisingDataTooLong(32))); 305 | } 306 | 307 | #[test] 308 | fn le_set_scan_response_data_empty() { 309 | let mut sink = RecordingSink::new(); 310 | sink.as_controller().le_set_scan_response_data(&[]).unwrap(); 311 | assert_eq!( 312 | sink.written_data, 313 | vec![ 314 | 1, 0x09, 0x20, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 315 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 316 | ] 317 | ); 318 | } 319 | 320 | #[test] 321 | fn le_set_scan_response_data_partial() { 322 | let mut sink = RecordingSink::new(); 323 | sink.as_controller() 324 | .le_set_scan_response_data(&[1, 2, 3, 4, 5, 6, 7, 8]) 325 | .unwrap(); 326 | assert_eq!( 327 | sink.written_data, 328 | vec![ 329 | 1, 0x09, 0x20, 32, 8, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 330 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 331 | ] 332 | ); 333 | } 334 | 335 | #[test] 336 | fn le_set_scan_response_data_full() { 337 | let mut sink = RecordingSink::new(); 338 | sink.as_controller() 339 | .le_set_scan_response_data(&[ 340 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 341 | 25, 26, 27, 28, 29, 30, 31, 342 | ]) 343 | .unwrap(); 344 | assert_eq!( 345 | sink.written_data, 346 | vec![ 347 | 1, 0x09, 0x20, 32, 31, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 348 | 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 349 | ] 350 | ); 351 | } 352 | 353 | #[test] 354 | fn le_set_scan_response_data_too_long() { 355 | let mut sink = RecordingSink::new(); 356 | let err = sink 357 | .as_controller() 358 | .le_set_scan_response_data(&[0; 32]) 359 | .err() 360 | .unwrap(); 361 | assert_eq!(err, nb::Error::Other(Error::AdvertisingDataTooLong(32))); 362 | } 363 | 364 | #[cfg(not(feature = "version-5-0"))] 365 | #[test] 366 | fn le_set_advertise_enable() { 367 | let mut sink = RecordingSink::new(); 368 | sink.as_controller().le_set_advertise_enable(true).unwrap(); 369 | assert_eq!(sink.written_data, [1, 0x0A, 0x20, 1, 1]); 370 | } 371 | 372 | #[cfg(feature = "version-5-0")] 373 | #[test] 374 | fn le_set_advertising_enable() { 375 | let mut sink = RecordingSink::new(); 376 | sink.as_controller() 377 | .le_set_advertising_enable(true) 378 | .unwrap(); 379 | assert_eq!(sink.written_data, [1, 0x0A, 0x20, 1, 1]); 380 | } 381 | 382 | #[test] 383 | fn le_set_scan_parameters() { 384 | let mut sink = RecordingSink::new(); 385 | sink.as_controller() 386 | .le_set_scan_parameters(&ScanParameters { 387 | scan_type: ScanType::Passive, 388 | scan_window: ScanWindow::start_every(Duration::from_millis(21)) 389 | .and_then(|b| b.open_for(Duration::from_millis(10))) 390 | .unwrap(), 391 | own_address_type: OwnAddressType::Public, 392 | filter_policy: ScanFilterPolicy::AcceptAll, 393 | }) 394 | .unwrap(); 395 | 396 | // bytes 5-6: 0x21, 0x00 = 0x0021 = 33 ~= 21 ms / 0.625 ms 397 | // bytes 7-8: 0x10, 0x00 = 0x0010 = 16 = 10 ms / 0.625 ms 398 | assert_eq!( 399 | sink.written_data, 400 | [1, 0x0B, 0x20, 7, 0x00, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00] 401 | ); 402 | } 403 | 404 | #[test] 405 | fn le_set_scan_enable() { 406 | let mut sink = RecordingSink::new(); 407 | sink.as_controller() 408 | .le_set_scan_enable(true, false) 409 | .unwrap(); 410 | assert_eq!(sink.written_data, [1, 0x0C, 0x20, 2, 1, 0]); 411 | } 412 | 413 | #[test] 414 | fn le_create_connection_no_whitelist() { 415 | let mut sink = RecordingSink::new(); 416 | sink.as_controller() 417 | .le_create_connection(&ConnectionParameters { 418 | scan_window: ScanWindow::start_every(Duration::from_millis(50)) 419 | .and_then(|b| b.open_for(Duration::from_millis(25))) 420 | .unwrap(), 421 | initiator_filter_policy: ConnectionFilterPolicy::UseAddress, 422 | peer_address: PeerAddrType::PublicDeviceAddress(hci::BdAddr([1, 2, 3, 4, 5, 6])), 423 | own_address_type: OwnAddressType::Public, 424 | conn_interval: ConnectionIntervalBuilder::new() 425 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 426 | .with_latency(10) 427 | .with_supervision_timeout(Duration::from_secs(15)) 428 | .build() 429 | .unwrap(), 430 | expected_connection_length: ExpectedConnectionLength::new( 431 | Duration::from_millis(200), 432 | Duration::from_millis(500), 433 | ) 434 | .unwrap(), 435 | }) 436 | .unwrap(); 437 | assert_eq!( 438 | sink.written_data, 439 | vec![ 440 | 1, 0x0D, 0x20, 25, 0x50, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 441 | 0x06, 0x00, 0x28, 0x00, 0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05, 0x40, 0x01, 0x20, 0x03, 442 | ] 443 | ); 444 | } 445 | 446 | #[test] 447 | fn le_create_connection_use_whitelist() { 448 | let mut sink = RecordingSink::new(); 449 | sink.as_controller() 450 | .le_create_connection(&ConnectionParameters { 451 | scan_window: ScanWindow::start_every(Duration::from_millis(50)) 452 | .and_then(|b| b.open_for(Duration::from_millis(25))) 453 | .unwrap(), 454 | initiator_filter_policy: ConnectionFilterPolicy::WhiteList, 455 | peer_address: PeerAddrType::PublicDeviceAddress(hci::BdAddr([1, 2, 3, 4, 5, 6])), 456 | own_address_type: OwnAddressType::Public, 457 | conn_interval: ConnectionIntervalBuilder::new() 458 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 459 | .with_latency(10) 460 | .with_supervision_timeout(Duration::from_secs(15)) 461 | .build() 462 | .unwrap(), 463 | expected_connection_length: ExpectedConnectionLength::new( 464 | Duration::from_millis(200), 465 | Duration::from_millis(500), 466 | ) 467 | .unwrap(), 468 | }) 469 | .unwrap(); 470 | assert_eq!( 471 | sink.written_data, 472 | vec![ 473 | 1, 0x0D, 0x20, 25, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 474 | 0x00, 0x00, 0x28, 0x00, 0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05, 0x40, 0x01, 0x20, 0x03, 475 | ] 476 | ); 477 | } 478 | 479 | #[test] 480 | fn le_add_device_to_white_list() { 481 | let mut sink = RecordingSink::new(); 482 | sink.as_controller() 483 | .le_add_device_to_white_list(hci::BdAddrType::Public(hci::BdAddr([1, 2, 3, 4, 5, 6]))) 484 | .unwrap(); 485 | assert_eq!( 486 | sink.written_data, 487 | [1, 0x11, 0x20, 7, 0x00, 1, 2, 3, 4, 5, 6] 488 | ); 489 | } 490 | 491 | #[cfg(feature = "version-5-0")] 492 | #[test] 493 | fn le_add_anon_advertising_devices_to_white_list() { 494 | let mut sink = RecordingSink::new(); 495 | sink.as_controller() 496 | .le_add_anon_advertising_devices_to_white_list() 497 | .unwrap(); 498 | assert_eq!( 499 | sink.written_data, 500 | [1, 0x11, 0x20, 7, 0xFF, 0, 0, 0, 0, 0, 0] 501 | ); 502 | } 503 | 504 | #[test] 505 | fn le_remove_device_from_white_list() { 506 | let mut sink = RecordingSink::new(); 507 | sink.as_controller() 508 | .le_remove_device_from_white_list(hci::BdAddrType::Public(hci::BdAddr([1, 2, 3, 4, 5, 6]))) 509 | .unwrap(); 510 | assert_eq!( 511 | sink.written_data, 512 | [1, 0x12, 0x20, 7, 0x00, 1, 2, 3, 4, 5, 6] 513 | ); 514 | } 515 | 516 | #[cfg(feature = "version-5-0")] 517 | #[test] 518 | fn le_remove_anon_advertising_devices_from_white_list() { 519 | let mut sink = RecordingSink::new(); 520 | sink.as_controller() 521 | .le_remove_anon_advertising_devices_from_white_list() 522 | .unwrap(); 523 | assert_eq!( 524 | sink.written_data, 525 | [1, 0x12, 0x20, 7, 0xFF, 0, 0, 0, 0, 0, 0] 526 | ); 527 | } 528 | 529 | #[test] 530 | fn le_connection_update() { 531 | let mut sink = RecordingSink::new(); 532 | sink.as_controller() 533 | .le_connection_update(&ConnectionUpdateParameters { 534 | conn_handle: hci::ConnectionHandle(0x0201), 535 | conn_interval: ConnectionIntervalBuilder::new() 536 | .with_range(Duration::from_millis(50), Duration::from_millis(500)) 537 | .with_latency(10) 538 | .with_supervision_timeout(Duration::from_secs(15)) 539 | .build() 540 | .unwrap(), 541 | expected_connection_length: ExpectedConnectionLength::new( 542 | Duration::from_millis(200), 543 | Duration::from_millis(500), 544 | ) 545 | .unwrap(), 546 | }) 547 | .unwrap(); 548 | assert_eq!( 549 | sink.written_data, 550 | vec![ 551 | 1, 0x13, 0x20, 14, 0x01, 0x02, 0x28, 0x00, 0x90, 0x01, 0x0A, 0x00, 0xDC, 0x05, 0x40, 552 | 0x01, 0x20, 0x03, 553 | ] 554 | ); 555 | } 556 | 557 | #[test] 558 | fn le_set_host_channel_classification() { 559 | let mut sink = RecordingSink::new(); 560 | sink.as_controller() 561 | .le_set_host_channel_classification( 562 | hci::ChannelClassification::CH_0 563 | | hci::ChannelClassification::CH_4 564 | | hci::ChannelClassification::CH_8 565 | | hci::ChannelClassification::CH_12 566 | | hci::ChannelClassification::CH_16 567 | | hci::ChannelClassification::CH_20 568 | | hci::ChannelClassification::CH_24 569 | | hci::ChannelClassification::CH_28 570 | | hci::ChannelClassification::CH_32 571 | | hci::ChannelClassification::CH_36, 572 | ) 573 | .unwrap(); 574 | assert_eq!( 575 | sink.written_data, 576 | [1, 0x14, 0x20, 5, 0x11, 0x11, 0x11, 0x11, 0x11] 577 | ); 578 | } 579 | 580 | #[test] 581 | fn le_set_host_channel_classification_failed_empty() { 582 | let mut sink = RecordingSink::new(); 583 | let err = sink 584 | .as_controller() 585 | .le_set_host_channel_classification(hci::ChannelClassification::empty()) 586 | .err() 587 | .unwrap(); 588 | assert_eq!(err, nb::Error::Other(Error::NoValidChannel)); 589 | assert_eq!(sink.written_data, []); 590 | } 591 | 592 | #[test] 593 | fn le_encrypt() { 594 | let mut sink = RecordingSink::new(); 595 | sink.as_controller() 596 | .le_encrypt(&AesParameters { 597 | key: EncryptionKey([ 598 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 599 | ]), 600 | plaintext_data: PlaintextBlock([ 601 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 602 | 0x1e, 0x1f, 603 | ]), 604 | }) 605 | .unwrap(); 606 | assert_eq!( 607 | sink.written_data, 608 | vec![ 609 | 1, 0x17, 0x20, 32, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 610 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 611 | 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 612 | ] 613 | ); 614 | } 615 | 616 | #[test] 617 | fn le_start_encryption() { 618 | let mut sink = RecordingSink::new(); 619 | sink.as_controller() 620 | .le_start_encryption(&EncryptionParameters { 621 | conn_handle: hci::ConnectionHandle(0x0201), 622 | random_number: 0x0807_0605_0403_0201, 623 | encrypted_diversifier: 0x0a09, 624 | long_term_key: EncryptionKey([ 625 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 626 | ]), 627 | }) 628 | .unwrap(); 629 | assert_eq!( 630 | sink.written_data, 631 | vec![ 632 | 1, 0x19, 0x20, 28, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 633 | 0x0a, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 634 | ] 635 | ); 636 | } 637 | 638 | #[test] 639 | fn le_long_term_key_request_reply() { 640 | let mut sink = RecordingSink::new(); 641 | sink.as_controller() 642 | .le_long_term_key_request_reply( 643 | hci::ConnectionHandle(0x0201), 644 | &EncryptionKey([ 645 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 646 | ]), 647 | ) 648 | .unwrap(); 649 | assert_eq!( 650 | sink.written_data, 651 | vec![ 652 | 1, 0x1A, 0x20, 18, 0x01, 0x02, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 653 | 0xb, 0xc, 0xd, 0xe, 0xf, 654 | ] 655 | ); 656 | } 657 | 658 | #[test] 659 | fn le_receiver_test() { 660 | let mut sink = RecordingSink::new(); 661 | sink.as_controller().le_receiver_test(0x27).unwrap(); 662 | assert_eq!(sink.written_data, [1, 0x1D, 0x20, 1, 0x27]); 663 | } 664 | 665 | #[test] 666 | fn le_receiver_test_out_of_range() { 667 | let mut sink = RecordingSink::new(); 668 | let err = sink.as_controller().le_receiver_test(0x28).err().unwrap(); 669 | assert_eq!(err, nb::Error::Other(Error::InvalidTestChannel(0x28))); 670 | assert_eq!(sink.written_data, []); 671 | } 672 | 673 | #[test] 674 | fn le_transmitter_test() { 675 | let mut sink = RecordingSink::new(); 676 | sink.as_controller() 677 | .le_transmitter_test(0x27, 0x25, TestPacketPayload::PrbS9) 678 | .unwrap(); 679 | assert_eq!(sink.written_data, [1, 0x1E, 0x20, 3, 0x27, 0x25, 0x00]); 680 | } 681 | 682 | #[test] 683 | fn le_transmitter_test_channel_out_of_range() { 684 | let mut sink = RecordingSink::new(); 685 | let err = sink 686 | .as_controller() 687 | .le_transmitter_test(0x28, 0x25, TestPacketPayload::PrbS9) 688 | .err() 689 | .unwrap(); 690 | assert_eq!(err, nb::Error::Other(Error::InvalidTestChannel(0x28))); 691 | assert_eq!(sink.written_data, []); 692 | } 693 | 694 | #[test] 695 | fn le_transmitter_test_length_out_of_range() { 696 | let mut sink = RecordingSink::new(); 697 | let err = sink 698 | .as_controller() 699 | .le_transmitter_test(0x27, 0x26, TestPacketPayload::PrbS9) 700 | .err() 701 | .unwrap(); 702 | assert_eq!(err, nb::Error::Other(Error::InvalidTestPayloadLength(0x26))); 703 | assert_eq!(sink.written_data, []); 704 | } 705 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A Bluetooth implementation for embedded systems. 2 | //! 3 | //! This crate is a proof-of-concept implementation of the host (application) side of the 4 | //! [`Bluetooth`] specification. It is still woefully incomplete, and will undoubtedly be redesigned 5 | //! completely, and potentially split into multiple crates before being stabilized. 6 | //! 7 | //! When the documentation refers to a specific section of "the" Bluetooth specification, the same 8 | //! section applies for all supported versions of the specification. If the versions differ, the 9 | //! specific version will also be included in the reference. 10 | //! 11 | //! # Design 12 | //! 13 | //! Like other core embedded crates (e.g, [`embedded-hal`]), this crate uses traits to be agnostic 14 | //! about the specific Bluetooth module. It provides a default implementation of the HCI for devices 15 | //! that implement the core [`Controller`] trait. The traits also make use of the [`nb`] crate to 16 | //! support different asynchronous or synchronous operation modes. 17 | //! 18 | //! ## Commands 19 | //! 20 | //! The [`host::Hci`] trait defines all of the functions that communicate from the host to the 21 | //! controller. The [`host::uart::Hci`] trait defines a read function that returns a 22 | //! [`host::uart::Packet`], which can contain an [`Event`], `AclData` (TODO), or `SyncData` 23 | //! (TODO). Both of these traits have default implementations in terms of the [`Controller`], so 24 | //! calling code does not need to implement any commands or event parsing code. 25 | //! 26 | //! ## Vendor-specific commands and events 27 | //! 28 | //! The [`host::uart::Hci`] trait requires specialization for the type of vendor-specific events 29 | //! (which implement [`event::VendorEvent`]) and vendor-specific errors. Any vendor-specific 30 | //! extensions will need to convert byte buffers into the appropriate event type (as defined by the 31 | //! vendor), but will not need to read data using the [`Controller`]. The Bluetooth standard 32 | //! provides packet length in a common header, so only complete packets will be passed on to the 33 | //! vendor code for deserialization. 34 | //! 35 | //! There is not yet support for vendor-specific commands. The vendor crate will have to serialize 36 | //! the command packets directly and write them to the [`Controller`]. 37 | //! 38 | //! # Reference implementation 39 | //! 40 | //! The [`bluenrg`] crate provides a sample implementation for STMicro's BlueNRG Bluetooth 41 | //! controllers. 42 | //! 43 | //! # Ideas for discussion and improvement 44 | //! 45 | //! - Add traits to facilitate writing Bluetooth controllers. These controllers would have a host on 46 | //! one side and a link layer on the other. Separate crate? If so, move common definitions (Status 47 | //! codes, opcodes, etc.) to a bluetooth-core crate. 48 | //! 49 | //! - Add a helper function for vendor-specific commands. This should take care of creating the 50 | //! header and writing the data to the [`Controller`]. Vendor code should only be responsible for 51 | //! serializing commands into byte slices. 52 | //! 53 | //! - Remove the `cmd_link` and `event_link` modules, and merge `uart` up into `host`. The Bluetooth 54 | //! spec made it seem like there were devices that do not include the packet type byte at the 55 | //! beginning of packets, but STMicro's BlueNRG implementation and Nordic's Zephyr implementation 56 | //! both include it. If there is a controller that does *not* include the packet type, the 57 | //! `event_link` HCI can always be brought back. 58 | //! 59 | //! - Provide config features for different versions of the Bluetooth Specification. 60 | //! 61 | //! - Implement all of the specified functions and events. 62 | //! 63 | //! - Provide opt-in config features for certain types of commands and events. For example, BlueNRG 64 | //! devices only implement 40 commands and 14 events, but the spec has around 250 commands and 76 65 | //! events. It would be nice if unused events could be compiled out. This would be less important 66 | //! for commands, since those functions would simply never be called, and could be removed by the 67 | //! linker. This would entail significant work both on the part of the crate authors and on crate 68 | //! users, who would need to configure the crate appropriately. All combinations of features would 69 | //! also never be tested; there are simply too many, even if we only provide features for the 70 | //! events. On the other hand, those features should not interact, so maybe it would be feasible. 71 | //! 72 | //! [`Bluetooth`]: https://www.bluetooth.com/specifications/bluetooth-core-specification 73 | //! [`embedded-hal`]: https://crates.io/crates/embedded-hal 74 | //! [`nb`]: https://crates.io/crates/nb 75 | //! [`bluenrg`]: https://github.com/danielgallagher0/bluenrg 76 | 77 | #![no_std] 78 | #![deny(missing_docs)] 79 | 80 | #[macro_use] 81 | extern crate bitflags; 82 | extern crate byteorder; 83 | extern crate nb; 84 | 85 | #[macro_use] 86 | mod bitflag_array; 87 | 88 | pub mod event; 89 | pub mod host; 90 | mod opcode; 91 | pub mod types; 92 | 93 | pub use event::Event; 94 | pub use opcode::Opcode; 95 | 96 | use core::convert::TryFrom; 97 | use core::fmt::Debug; 98 | 99 | /// Interface to the Bluetooth controller from the host's perspective. 100 | /// 101 | /// The Bluetooth application host must communicate with a controller (which, in turn, communicates 102 | /// with the link layer) to control the Bluetooth radio. Device crates must implement this trait, 103 | /// which enables full access to all of the functions and events of the HCI through [`host::Hci`] 104 | /// and [`host::uart::Hci`], respectively. 105 | pub trait Controller { 106 | /// Enumeration of [`Controller`] errors. These typically will be specializations of 107 | /// [`host::uart::Error`] that specify both the vendor-specific error type _and_ a communication 108 | /// error type. The communication error type in turn will depend on the bus used to communicate 109 | /// with the controller as well as the device crate (e.g., [`linux-embedded-hal::Spidev`] uses 110 | /// [`std::io::Error`]). 111 | /// 112 | /// [`linux-embedded-hal::Spidev`]: 113 | /// https://docs.rs/linux-embedded-hal/0.1.1/linux_embedded_hal/struct.Spidev.html 114 | /// [`std::io::Error`]: https://doc.rust-lang.org/nightly/std/io/struct.Error.html 115 | type Error; 116 | 117 | /// The type of header sent to the controller for HCI commands. Should be either 118 | /// [`host::uart::CommandHeader`], [`host::cmd_link::Header`], or 119 | /// [`host::event_link::NoCommands`], depending on the controller implementation. 120 | type Header; 121 | 122 | /// Type containing vendor-specific extensions for the controller, including vendor-specific 123 | /// errors, events, and status codes. 124 | type Vendor: Vendor; 125 | 126 | /// Writes the bytes to the controller, in a single transaction if possible. All of `header` 127 | /// shall be written, followed by all of `payload`. `write` is allowed to block internally, but 128 | /// should return [`nb::Error::WouldBlock`] if the controller is not ready to receive the data. 129 | fn write(&mut self, header: &[u8], payload: &[u8]) -> nb::Result<(), Self::Error>; 130 | 131 | /// Reads data from the controller into the provided `buffer`. The length of the buffer 132 | /// indicates the number of bytes to read. The implementor must not return bytes in an order 133 | /// different from that in which they were received from the controller. For example, the 134 | /// implementor may read all available bytes from the controller and maintain them in an 135 | /// internal buffer, but `read_into` shall only read the number of bytes requested. 136 | /// 137 | /// # Example 138 | /// 139 | /// ``` 140 | /// // Controller sends: 141 | /// // +------+------+------+------+------+------+------+------+ 142 | /// // | 0x12 | 0x34 | 0x56 | 0x78 | 0x9a | 0xbc | 0xde | 0xf0 | 143 | /// // +------+------+------+------+------+------+------+------+ 144 | /// 145 | /// // host calls: 146 | /// 147 | /// # extern crate nb; 148 | /// # extern crate bluetooth_hci; 149 | /// # use bluetooth_hci::Controller as HciController; 150 | /// # struct Controller; 151 | /// # struct Error; 152 | /// # struct Header; 153 | /// # struct Vendor; 154 | /// # impl bluetooth_hci::Vendor for Vendor { 155 | /// # type Status = VendorStatus; 156 | /// # type Event = VendorEvent; 157 | /// # } 158 | /// # #[derive(Clone, Debug)] 159 | /// # struct VendorStatus; 160 | /// # impl std::convert::TryFrom for VendorStatus { 161 | /// # type Error = bluetooth_hci::BadStatusError; 162 | /// # fn try_from(value: u8) -> Result { 163 | /// # Err(bluetooth_hci::BadStatusError::BadValue(value)) 164 | /// # } 165 | /// # } 166 | /// # impl std::convert::Into for VendorStatus { 167 | /// # fn into(self) -> u8 { 168 | /// # 0 169 | /// # } 170 | /// # } 171 | /// # struct VendorEvent; 172 | /// # impl bluetooth_hci::event::VendorEvent for VendorEvent { 173 | /// # type Error = Error; 174 | /// # type Status = VendorStatus; 175 | /// # type ReturnParameters = ReturnParameters; 176 | /// # fn new(_buffer: &[u8]) -> Result> 177 | /// # where 178 | /// # Self: Sized 179 | /// # { 180 | /// # Ok(VendorEvent{}) 181 | /// # } 182 | /// # } 183 | /// # #[derive(Clone, Debug)] 184 | /// # struct ReturnParameters; 185 | /// # impl bluetooth_hci::event::VendorReturnParameters for ReturnParameters { 186 | /// # type Error = Error; 187 | /// # fn new(_buffer: &[u8]) -> Result> { 188 | /// # Ok(ReturnParameters{}) 189 | /// # } 190 | /// # } 191 | /// # impl HciController for Controller { 192 | /// # type Error = Error; 193 | /// # type Header = Header; 194 | /// # type Vendor = Vendor; 195 | /// # fn write(&mut self, _header: &[u8], _payload: &[u8]) -> nb::Result<(), Self::Error> { 196 | /// # Ok(()) 197 | /// # } 198 | /// # fn read_into(&mut self, _buffer: &mut [u8]) -> nb::Result<(), Self::Error> { 199 | /// # Ok(()) 200 | /// # } 201 | /// # fn peek(&mut self, _n: usize) -> nb::Result { 202 | /// # Ok(0) 203 | /// # } 204 | /// # } 205 | /// # fn main() { 206 | /// # let mut controller = Controller; 207 | /// let mut buffer = [0; 4]; 208 | /// controller.read_into(&mut buffer[1..]); // read 3 bytes into buffer[1..] 209 | /// 210 | /// // buffer contains: 211 | /// // +------+------+------+------+ 212 | /// // | 0x00 | 0x12 | 0x34 | 0x56 | 213 | /// // +------+------+------+------+ 214 | /// 215 | /// // now the host calls: 216 | /// controller.read_into(&mut buffer); // read 4 bytes into buffer 217 | /// 218 | /// // buffer contains: 219 | /// // +------+------+------+------+ 220 | /// // | 0x78 | 0x9a | 0xbc | 0xde | 221 | /// // +------+------+------+------+ 222 | /// # } 223 | /// ``` 224 | /// If the next call to `read_into` requests more than 1 byte, the controller may return 225 | /// [`nb::Error::WouldBlock`], or may attempt to read more data from the controller. If not 226 | /// enough data is available from the controller, the implementor shall return 227 | /// [`nb::Error::WouldBlock`]. 228 | fn read_into(&mut self, buffer: &mut [u8]) -> nb::Result<(), Self::Error>; 229 | 230 | /// Looks ahead at the data coming from the Controller without consuming it. Implementors should 231 | /// be able to support values of `n` up to 5 to support all potential data types. 232 | /// 233 | /// `peek(0)` will typically be used to the the packet type (see Bluetooth Spec, Vol 4, Part A, 234 | /// Section 2), which will be followed by another peek to determine the amount of data to 235 | /// read. For example, the code to read an HCI event looks like this: 236 | /// 237 | /// ``` 238 | /// # extern crate nb; 239 | /// # extern crate bluetooth_hci; 240 | /// # use bluetooth_hci::Controller as HciController; 241 | /// # struct Controller; 242 | /// # #[derive(Debug)] 243 | /// # struct Error; 244 | /// # struct Header; 245 | /// # struct Vendor; 246 | /// # impl bluetooth_hci::Vendor for Vendor { 247 | /// # type Status = VendorStatus; 248 | /// # type Event = VendorEvent; 249 | /// # } 250 | /// # #[derive(Clone, Debug)] 251 | /// # struct VendorStatus; 252 | /// # impl std::convert::TryFrom for VendorStatus { 253 | /// # type Error = bluetooth_hci::BadStatusError; 254 | /// # fn try_from(value: u8) -> Result { 255 | /// # Err(bluetooth_hci::BadStatusError::BadValue(value)) 256 | /// # } 257 | /// # } 258 | /// # impl std::convert::Into for VendorStatus { 259 | /// # fn into(self) -> u8 { 260 | /// # 0 261 | /// # } 262 | /// # } 263 | /// # struct VendorEvent; 264 | /// # impl bluetooth_hci::event::VendorEvent for VendorEvent { 265 | /// # type Error = Error; 266 | /// # type Status = VendorStatus; 267 | /// # type ReturnParameters = ReturnParameters; 268 | /// # fn new(_buffer: &[u8]) -> Result> 269 | /// # where 270 | /// # Self: Sized 271 | /// # { 272 | /// # Ok(VendorEvent{}) 273 | /// # } 274 | /// # } 275 | /// # #[derive(Clone, Debug)] 276 | /// # struct ReturnParameters; 277 | /// # impl bluetooth_hci::event::VendorReturnParameters for ReturnParameters { 278 | /// # type Error = Error; 279 | /// # fn new(_buffer: &[u8]) -> Result> { 280 | /// # Ok(ReturnParameters{}) 281 | /// # } 282 | /// # } 283 | /// # impl HciController for Controller { 284 | /// # type Error = Error; 285 | /// # type Header = Header; 286 | /// # type Vendor = Vendor; 287 | /// # fn write(&mut self, _header: &[u8], _payload: &[u8]) -> nb::Result<(), Self::Error> { 288 | /// # Ok(()) 289 | /// # } 290 | /// # fn read_into(&mut self, _buffer: &mut [u8]) -> nb::Result<(), Self::Error> { 291 | /// # Ok(()) 292 | /// # } 293 | /// # fn peek(&mut self, _n: usize) -> nb::Result { 294 | /// # Ok(0) 295 | /// # } 296 | /// # } 297 | /// # fn main() -> nb::Result<(), Error> { 298 | /// # const PACKET_TYPE_HCI_EVENT: u8 = 4; 299 | /// # let mut controller = Controller; 300 | /// const MAX_EVENT_LENGTH: usize = 255; 301 | /// const HEADER_LENGTH: usize = 2; 302 | /// let mut buffer = [0; MAX_EVENT_LENGTH + HEADER_LENGTH]; 303 | /// let packet_type = controller.peek(0)?; 304 | /// if packet_type == PACKET_TYPE_HCI_EVENT { 305 | /// // Byte 3 has the parameter length in HCI events 306 | /// let param_len = controller.peek(3)? as usize; 307 | /// 308 | /// // We want to consume the full HCI Event packet, and we now know the length. 309 | /// controller.read_into(&mut buffer[..HEADER_LENGTH + param_len])?; 310 | /// } 311 | /// # Ok(()) 312 | /// # } 313 | /// ``` 314 | fn peek(&mut self, n: usize) -> nb::Result; 315 | } 316 | 317 | /// Trait defining vendor-specific extensions for the Bluetooth Controller. 318 | pub trait Vendor { 319 | /// Enumeration of vendor-specific status codes. 320 | type Status: TryFrom + Into + Clone + Debug; 321 | 322 | /// Enumeration of vendor-specific events. 323 | type Event: event::VendorEvent; 324 | } 325 | 326 | /// List of possible error codes, Bluetooth Spec, Vol 2, Part D, Section 2. 327 | /// 328 | /// Includes an extension point for vendor-specific status codes. 329 | #[derive(Copy, Clone, Debug, PartialEq)] 330 | pub enum Status { 331 | /// Success 332 | Success, 333 | /// Unknown HCI Command 334 | UnknownCommand, 335 | /// Unknown Connection Identifier 336 | UnknownConnectionId, 337 | /// Hardware Failure 338 | HardwareFailure, 339 | /// Page Timeout 340 | PageTimeout, 341 | /// Authentication Failure 342 | AuthFailure, 343 | /// PIN or Key Missing 344 | PinOrKeyMissing, 345 | /// Memory Capacity Exceeded 346 | OutOfMemory, 347 | /// Connection Timeout 348 | ConnectionTimeout, 349 | /// Connection Limit Exceeded 350 | ConnectionLimitExceeeded, 351 | /// Synchronous Connection Limit To A Device Exceeded 352 | SyncConnectionLimitExceeded, 353 | /// Connection Already Exists 354 | ConnectionAlreadyExists, 355 | /// Command Disallowed 356 | CommandDisallowed, 357 | /// Connection Rejected due to Limited Resources 358 | LimitedResources, 359 | /// Connection Rejected Due To Security Reasons 360 | ConnectionRejectedSecurity, 361 | /// Connection Rejected due to Unacceptable BD_ADDR 362 | UnacceptableBdAddr, 363 | /// Connection Accept Timeout Exceeded 364 | AcceptTimeoutExceeded, 365 | /// Unsupported Feature or Parameter Value 366 | UnsupportedFeature, 367 | /// Invalid HCI Command Parameters 368 | InvalidParameters, 369 | /// Remote User Terminated Connection 370 | RemoteTerminationByUser, 371 | /// Remote Device Terminated Connection due to Low Resources 372 | RemoteTerminationLowResources, 373 | /// Remote Device Terminated Connection due to Power Off 374 | RemoteTerminationPowerOff, 375 | /// Connection Terminated By Local Host 376 | ConnectionTerminatedByHost, 377 | /// Repeated Attempts 378 | RepeatedAttempts, 379 | /// Pairing Not Allowed 380 | PairingNotAllowed, 381 | /// Unknown LMP PDU 382 | UnknownLmpPdu, 383 | /// Unsupported Remote Feature / Unsupported LMP Feature 384 | UnsupportedRemoteFeature, 385 | /// SCO Offset Rejected 386 | ScoOffsetRejected, 387 | /// SCO Interval Rejected 388 | ScoIntervalRejected, 389 | /// SCO Air Mode Rejected 390 | ScoAirModeRejected, 391 | /// Invalid LMP Parameters / Invalid LL Parameters 392 | InvalidLmpParameters, 393 | /// Unspecified Error 394 | UnspecifiedError, 395 | /// Unsupported LMP Parameter Value / Unsupported LL Parameter Value 396 | UnsupportedLmpParameterValue, 397 | /// Role Change Not Allowed 398 | RoleChangeNotAllowed, 399 | /// LMP Response Timeout / LL Response Timeout 400 | LmpResponseTimeout, 401 | /// LMP Error Transaction Collision / LL Procedure Collision 402 | LmpTransactionCollision, 403 | /// LMP PDU Not Allowed 404 | LmpPduNotAllowed, 405 | /// Encryption Mode Not Acceptable 406 | EncryptionModeNotAcceptable, 407 | /// Link Key cannot be Changed 408 | LinkKeyCannotBeChanged, 409 | /// Requested QoS Not Supported 410 | RequestedQosNotSupported, 411 | /// Instant Passed 412 | InstantPassed, 413 | /// Pairing With Unit Key Not Supported 414 | PairingWithUnitKeyNotSupported, 415 | /// Different Transaction Collision 416 | DifferentTransactionCollision, 417 | /// Reserved for Future Use 418 | ReservedforFutureUse, 419 | /// QoS Unacceptable Parameter 420 | QosUnacceptableParameter, 421 | /// QoS Rejected 422 | QosRejected, 423 | /// Channel Classification Not Supported 424 | ChannelClassificationNotSupported, 425 | /// Insufficient Security 426 | InsufficientSecurity, 427 | /// Parameter Out Of Mandatory Range 428 | ParameterOutOfMandatoryRange, 429 | /// Reserved for Future Use 430 | ReservedForFutureUse49, 431 | /// Role Switch Pending 432 | RoleSwitchPending, 433 | /// Reserved for Future Use 434 | ReservedForFutureUse51, 435 | /// Reserved Slot Violation 436 | ReservedSlotViolation, 437 | /// Role Switch Failed 438 | RoleSwitchFailed, 439 | /// Extended Inquiry Response Too Large 440 | ExtendedInquiryResponseTooLarge, 441 | /// Secure Simple Pairing Not Supported By Host 442 | SecureSimplePairingNotSupportedByHost, 443 | /// Host Busy - Pairing 444 | HostBusyPairing, 445 | /// Connection Rejected due to No Suitable Channel Found 446 | ConnectionRejectedNoSuitableChannel, 447 | /// Controller Busy 448 | ControllerBusy, 449 | /// Unacceptable Connection Parameters 450 | UnacceptableConnectionParameters, 451 | /// Advertising Timeout 452 | AdvertisingTimeout, 453 | /// Connection Terminated due to MIC Failure 454 | ConnectionTerminatedMicFailure, 455 | /// Connection Failed to be Established 456 | ConnectionFailedToEstablish, 457 | /// MAC Connection Failed 458 | MacConnectionFailed, 459 | /// Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging 460 | CoarseClockAdjustmentRejectedDraggingAttempted, 461 | #[cfg(feature = "version-5-0")] 462 | /// Type0 Submap Not Defined 463 | /// 464 | /// First introduced in version 5.0 465 | Type0SubmapNotDefined, 466 | #[cfg(feature = "version-5-0")] 467 | /// Unknown Advertising Identifier 468 | /// 469 | /// First introduced in version 5.0 470 | UnknownAdvertisingId, 471 | #[cfg(feature = "version-5-0")] 472 | /// Limit Reached 473 | /// 474 | /// First introduced in version 5.0 475 | LimitReached, 476 | #[cfg(feature = "version-5-0")] 477 | /// Operation Cancelled by Host 478 | /// 479 | /// First introduced in version 5.0 480 | OperationCancelledByHost, 481 | /// Vendor-specific status code 482 | Vendor(V), 483 | } 484 | 485 | /// Wrapper enum for errors converting a u8 into a [`Status`]. 486 | pub enum BadStatusError { 487 | /// The value does not map to a [`Status`]. 488 | BadValue(u8), 489 | } 490 | 491 | impl core::convert::TryFrom for Status 492 | where 493 | V: core::convert::TryFrom, 494 | { 495 | type Error = V::Error; 496 | 497 | fn try_from(value: u8) -> Result, Self::Error> { 498 | match value { 499 | 0x00 => Ok(Status::Success), 500 | 0x01 => Ok(Status::UnknownCommand), 501 | 0x02 => Ok(Status::UnknownConnectionId), 502 | 0x03 => Ok(Status::HardwareFailure), 503 | 0x04 => Ok(Status::PageTimeout), 504 | 0x05 => Ok(Status::AuthFailure), 505 | 0x06 => Ok(Status::PinOrKeyMissing), 506 | 0x07 => Ok(Status::OutOfMemory), 507 | 0x08 => Ok(Status::ConnectionTimeout), 508 | 0x09 => Ok(Status::ConnectionLimitExceeeded), 509 | 0x0A => Ok(Status::SyncConnectionLimitExceeded), 510 | 0x0B => Ok(Status::ConnectionAlreadyExists), 511 | 0x0C => Ok(Status::CommandDisallowed), 512 | 0x0D => Ok(Status::LimitedResources), 513 | 0x0E => Ok(Status::ConnectionRejectedSecurity), 514 | 0x0F => Ok(Status::UnacceptableBdAddr), 515 | 0x10 => Ok(Status::AcceptTimeoutExceeded), 516 | 0x11 => Ok(Status::UnsupportedFeature), 517 | 0x12 => Ok(Status::InvalidParameters), 518 | 0x13 => Ok(Status::RemoteTerminationByUser), 519 | 0x14 => Ok(Status::RemoteTerminationLowResources), 520 | 0x15 => Ok(Status::RemoteTerminationPowerOff), 521 | 0x16 => Ok(Status::ConnectionTerminatedByHost), 522 | 0x17 => Ok(Status::RepeatedAttempts), 523 | 0x18 => Ok(Status::PairingNotAllowed), 524 | 0x19 => Ok(Status::UnknownLmpPdu), 525 | 0x1A => Ok(Status::UnsupportedRemoteFeature), 526 | 0x1B => Ok(Status::ScoOffsetRejected), 527 | 0x1C => Ok(Status::ScoIntervalRejected), 528 | 0x1D => Ok(Status::ScoAirModeRejected), 529 | 0x1E => Ok(Status::InvalidLmpParameters), 530 | 0x1F => Ok(Status::UnspecifiedError), 531 | 0x20 => Ok(Status::UnsupportedLmpParameterValue), 532 | 0x21 => Ok(Status::RoleChangeNotAllowed), 533 | 0x22 => Ok(Status::LmpResponseTimeout), 534 | 0x23 => Ok(Status::LmpTransactionCollision), 535 | 0x24 => Ok(Status::LmpPduNotAllowed), 536 | 0x25 => Ok(Status::EncryptionModeNotAcceptable), 537 | 0x26 => Ok(Status::LinkKeyCannotBeChanged), 538 | 0x27 => Ok(Status::RequestedQosNotSupported), 539 | 0x28 => Ok(Status::InstantPassed), 540 | 0x29 => Ok(Status::PairingWithUnitKeyNotSupported), 541 | 0x2A => Ok(Status::DifferentTransactionCollision), 542 | 0x2B => Ok(Status::ReservedforFutureUse), 543 | 0x2C => Ok(Status::QosUnacceptableParameter), 544 | 0x2D => Ok(Status::QosRejected), 545 | 0x2E => Ok(Status::ChannelClassificationNotSupported), 546 | 0x2F => Ok(Status::InsufficientSecurity), 547 | 0x30 => Ok(Status::ParameterOutOfMandatoryRange), 548 | 0x31 => Ok(Status::ReservedForFutureUse49), 549 | 0x32 => Ok(Status::RoleSwitchPending), 550 | 0x33 => Ok(Status::ReservedForFutureUse51), 551 | 0x34 => Ok(Status::ReservedSlotViolation), 552 | 0x35 => Ok(Status::RoleSwitchFailed), 553 | 0x36 => Ok(Status::ExtendedInquiryResponseTooLarge), 554 | 0x37 => Ok(Status::SecureSimplePairingNotSupportedByHost), 555 | 0x38 => Ok(Status::HostBusyPairing), 556 | 0x39 => Ok(Status::ConnectionRejectedNoSuitableChannel), 557 | 0x3A => Ok(Status::ControllerBusy), 558 | 0x3B => Ok(Status::UnacceptableConnectionParameters), 559 | 0x3C => Ok(Status::AdvertisingTimeout), 560 | 0x3D => Ok(Status::ConnectionTerminatedMicFailure), 561 | 0x3E => Ok(Status::ConnectionFailedToEstablish), 562 | 0x3F => Ok(Status::MacConnectionFailed), 563 | 0x40 => Ok(Status::CoarseClockAdjustmentRejectedDraggingAttempted), 564 | 0x41 => { 565 | #[cfg(feature = "version-5-0")] 566 | { 567 | Ok(Status::Type0SubmapNotDefined) 568 | } 569 | #[cfg(not(feature = "version-5-0"))] 570 | { 571 | Ok(Status::Vendor(V::try_from(value)?)) 572 | } 573 | } 574 | 0x42 => { 575 | #[cfg(feature = "version-5-0")] 576 | { 577 | Ok(Status::UnknownAdvertisingId) 578 | } 579 | #[cfg(not(feature = "version-5-0"))] 580 | { 581 | Ok(Status::Vendor(V::try_from(value)?)) 582 | } 583 | } 584 | 0x43 => { 585 | #[cfg(feature = "version-5-0")] 586 | { 587 | Ok(Status::LimitReached) 588 | } 589 | #[cfg(not(feature = "version-5-0"))] 590 | { 591 | Ok(Status::Vendor(V::try_from(value)?)) 592 | } 593 | } 594 | 0x44 => { 595 | #[cfg(feature = "version-5-0")] 596 | { 597 | Ok(Status::OperationCancelledByHost) 598 | } 599 | #[cfg(not(feature = "version-5-0"))] 600 | { 601 | Ok(Status::Vendor(V::try_from(value)?)) 602 | } 603 | } 604 | _ => Ok(Status::Vendor(V::try_from(value)?)), 605 | } 606 | } 607 | } 608 | 609 | impl core::convert::Into for Status 610 | where 611 | V: core::convert::Into, 612 | { 613 | fn into(self) -> u8 { 614 | match self { 615 | Status::Success => 0x00, 616 | Status::UnknownCommand => 0x01, 617 | Status::UnknownConnectionId => 0x02, 618 | Status::HardwareFailure => 0x03, 619 | Status::PageTimeout => 0x04, 620 | Status::AuthFailure => 0x05, 621 | Status::PinOrKeyMissing => 0x06, 622 | Status::OutOfMemory => 0x07, 623 | Status::ConnectionTimeout => 0x08, 624 | Status::ConnectionLimitExceeeded => 0x09, 625 | Status::SyncConnectionLimitExceeded => 0x0A, 626 | Status::ConnectionAlreadyExists => 0x0B, 627 | Status::CommandDisallowed => 0x0C, 628 | Status::LimitedResources => 0x0D, 629 | Status::ConnectionRejectedSecurity => 0x0E, 630 | Status::UnacceptableBdAddr => 0x0F, 631 | Status::AcceptTimeoutExceeded => 0x10, 632 | Status::UnsupportedFeature => 0x11, 633 | Status::InvalidParameters => 0x12, 634 | Status::RemoteTerminationByUser => 0x13, 635 | Status::RemoteTerminationLowResources => 0x14, 636 | Status::RemoteTerminationPowerOff => 0x15, 637 | Status::ConnectionTerminatedByHost => 0x16, 638 | Status::RepeatedAttempts => 0x17, 639 | Status::PairingNotAllowed => 0x18, 640 | Status::UnknownLmpPdu => 0x19, 641 | Status::UnsupportedRemoteFeature => 0x1A, 642 | Status::ScoOffsetRejected => 0x1B, 643 | Status::ScoIntervalRejected => 0x1C, 644 | Status::ScoAirModeRejected => 0x1D, 645 | Status::InvalidLmpParameters => 0x1E, 646 | Status::UnspecifiedError => 0x1F, 647 | Status::UnsupportedLmpParameterValue => 0x20, 648 | Status::RoleChangeNotAllowed => 0x21, 649 | Status::LmpResponseTimeout => 0x22, 650 | Status::LmpTransactionCollision => 0x23, 651 | Status::LmpPduNotAllowed => 0x24, 652 | Status::EncryptionModeNotAcceptable => 0x25, 653 | Status::LinkKeyCannotBeChanged => 0x26, 654 | Status::RequestedQosNotSupported => 0x27, 655 | Status::InstantPassed => 0x28, 656 | Status::PairingWithUnitKeyNotSupported => 0x29, 657 | Status::DifferentTransactionCollision => 0x2A, 658 | Status::ReservedforFutureUse => 0x2B, 659 | Status::QosUnacceptableParameter => 0x2C, 660 | Status::QosRejected => 0x2D, 661 | Status::ChannelClassificationNotSupported => 0x2E, 662 | Status::InsufficientSecurity => 0x2F, 663 | Status::ParameterOutOfMandatoryRange => 0x30, 664 | Status::ReservedForFutureUse49 => 0x31, 665 | Status::RoleSwitchPending => 0x32, 666 | Status::ReservedForFutureUse51 => 0x33, 667 | Status::ReservedSlotViolation => 0x34, 668 | Status::RoleSwitchFailed => 0x35, 669 | Status::ExtendedInquiryResponseTooLarge => 0x36, 670 | Status::SecureSimplePairingNotSupportedByHost => 0x37, 671 | Status::HostBusyPairing => 0x38, 672 | Status::ConnectionRejectedNoSuitableChannel => 0x39, 673 | Status::ControllerBusy => 0x3A, 674 | Status::UnacceptableConnectionParameters => 0x3B, 675 | Status::AdvertisingTimeout => 0x3C, 676 | Status::ConnectionTerminatedMicFailure => 0x3D, 677 | Status::ConnectionFailedToEstablish => 0x3E, 678 | Status::MacConnectionFailed => 0x3F, 679 | Status::CoarseClockAdjustmentRejectedDraggingAttempted => 0x40, 680 | _ => { 681 | #[cfg(feature = "version-5-0")] 682 | { 683 | match self { 684 | Status::Type0SubmapNotDefined => 0x41, 685 | Status::UnknownAdvertisingId => 0x42, 686 | Status::LimitReached => 0x43, 687 | Status::OperationCancelledByHost => 0x44, 688 | Status::Vendor(v) => v.into(), 689 | _ => 0xFF, 690 | } 691 | } 692 | 693 | #[cfg(not(feature = "version-5-0"))] 694 | { 695 | if let Status::Vendor(v) = self { 696 | v.into() 697 | } else { 698 | 0xFF 699 | } 700 | } 701 | } 702 | } 703 | } 704 | } 705 | 706 | /// Newtype for a connection handle. 707 | #[derive(Clone, Copy, Debug, PartialEq)] 708 | pub struct ConnectionHandle(pub u16); 709 | 710 | /// Newtype for BDADDR. 711 | #[derive(Copy, Clone, Debug, PartialEq)] 712 | pub struct BdAddr(pub [u8; 6]); 713 | 714 | /// Potential values for BDADDR 715 | #[derive(Copy, Clone, Debug, PartialEq)] 716 | pub enum BdAddrType { 717 | /// Public address. 718 | Public(BdAddr), 719 | 720 | /// Random address. 721 | Random(BdAddr), 722 | } 723 | 724 | impl BdAddrType { 725 | /// Writes a `BdAddrType` into the given slice. The slice must be exactly the right length (7 726 | /// bytes). 727 | pub fn copy_into_slice(&self, bytes: &mut [u8]) { 728 | assert_eq!(bytes.len(), 7); 729 | match *self { 730 | BdAddrType::Public(addr) => { 731 | bytes[0] = 0; 732 | bytes[1..7].copy_from_slice(&addr.0); 733 | } 734 | BdAddrType::Random(addr) => { 735 | bytes[0] = 1; 736 | bytes[1..7].copy_from_slice(&addr.0); 737 | } 738 | } 739 | } 740 | } 741 | 742 | /// The BD Address type is not recognized. Includes the unrecognized byte. 743 | /// 744 | /// See [`to_bd_addr_type`] 745 | pub struct BdAddrTypeError(pub u8); 746 | 747 | /// Wraps a [`BdAddr`] in a [`BdAddrType`]. 748 | /// 749 | /// # Errors 750 | /// 751 | /// - `bd_addr_type` does not denote an appropriate type. Returns the byte. The address is 752 | /// discarded. 753 | pub fn to_bd_addr_type(bd_addr_type: u8, addr: BdAddr) -> Result { 754 | match bd_addr_type { 755 | 0 => Ok(BdAddrType::Public(addr)), 756 | 1 => Ok(BdAddrType::Random(addr)), 757 | _ => Err(BdAddrTypeError(bd_addr_type)), 758 | } 759 | } 760 | 761 | bitflags! { 762 | /// Bitfield for LE Remote Features. 763 | /// 764 | /// Fields are defined in Vol 6, Part B, Section 4.6 of the spec. See Table 4.3 (version 4.1) 765 | /// or Table 4.4 (version 4.2 and 5.0). 766 | #[derive(Default)] 767 | pub struct LinkLayerFeature : u64 { 768 | /// See section 4.6.1 769 | const LE_ENCRYPTION = 1 << 0; 770 | /// See section 4.6.2 771 | const CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1 << 1; 772 | /// See section 4.6.3 773 | const EXTENDED_REJECT_INDICATION = 1 << 2; 774 | /// See section 4.6.4 775 | const PERIPHERAL_INITIATED_FEATURES_EXCHANGE = 1 << 3; 776 | /// See section 4.6.5 777 | const LE_PING = 1 << 4; 778 | /// See section 4.6.6 779 | #[cfg(any(feature = "version-4-2", feature = "version-5-0"))] 780 | const LE_DATA_PACKET_LENGTH_EXTENSION = 1 << 5; 781 | /// See section 4.6.7 782 | #[cfg(any(feature = "version-4-2", feature = "version-5-0"))] 783 | const LL_PRIVACY = 1 << 6; 784 | /// See section 4.6.8 785 | #[cfg(any(feature = "version-4-2", feature = "version-5-0"))] 786 | const EXTENDED_SCANNER_FILTER_POLICIES = 1 << 7; 787 | /// See section 4.6.9 788 | #[cfg(feature = "version-5-0")] 789 | const LE_2M_PHY = 1 << 8; 790 | /// See section 4.6.10 791 | #[cfg(feature = "version-5-0")] 792 | const STABLE_MODULATION_INDEX_TX = 1 << 9; 793 | /// See section 4.6.11 794 | #[cfg(feature = "version-5-0")] 795 | const STABLE_MODULATION_INDEX_RX = 1 << 10; 796 | /// Not in section 4.6 797 | #[cfg(feature = "version-5-0")] 798 | const LE_CODED_PHY = 1 << 11; 799 | /// See section 4.6.12 800 | #[cfg(feature = "version-5-0")] 801 | const LE_EXTENDED_ADVERTISING = 1 << 12; 802 | /// See section 4.6.13 803 | #[cfg(feature = "version-5-0")] 804 | const LE_PERIODIC_ADVERTISING = 1 << 13; 805 | /// See section 4.6.14 806 | #[cfg(feature = "version-5-0")] 807 | const CHANNEL_SELECTION_ALGORITHM_2 = 1 << 14; 808 | /// Not in section 4.6 809 | #[cfg(feature = "version-5-0")] 810 | const LE_POWER_CLASS_1 = 1 << 15; 811 | /// See section 4.6.15 812 | #[cfg(feature = "version-5-0")] 813 | const MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE = 1 << 16; 814 | } 815 | } 816 | 817 | bitflag_array! { 818 | /// Channel classifications for the LE Set Host Channel Classification command. 819 | /// 820 | /// If a flag is set, its classification is "Unknown". If the flag is cleared, it is known 821 | /// "bad". 822 | #[derive(Copy, Clone, Debug)] 823 | pub struct ChannelClassification : 5; 824 | pub struct ChannelFlag; 825 | 826 | /// Channel 0 classification not known. 827 | const CH_0 = 0, 1 << 0; 828 | /// Channel 1 classification not known. 829 | const CH_1 = 0, 1 << 1; 830 | /// Channel 2 classification not known. 831 | const CH_2 = 0, 1 << 2; 832 | /// Channel 3 classification not known. 833 | const CH_3 = 0, 1 << 3; 834 | /// Channel 4 classification not known. 835 | const CH_4 = 0, 1 << 4; 836 | /// Channel 5 classification not known. 837 | const CH_5 = 0, 1 << 5; 838 | /// Channel 6 classification not known. 839 | const CH_6 = 0, 1 << 6; 840 | /// Channel 7 classification not known. 841 | const CH_7 = 0, 1 << 7; 842 | /// Channel 8 classification not known. 843 | const CH_8 = 1, 1 << 0; 844 | /// Channel 9 classification not known. 845 | const CH_9 = 1, 1 << 1; 846 | /// Channel 10 classification not known. 847 | const CH_10 = 1, 1 << 2; 848 | /// Channel 11 classification not known. 849 | const CH_11 = 1, 1 << 3; 850 | /// Channel 12 classification not known. 851 | const CH_12 = 1, 1 << 4; 852 | /// Channel 13 classification not known. 853 | const CH_13 = 1, 1 << 5; 854 | /// Channel 14 classification not known. 855 | const CH_14 = 1, 1 << 6; 856 | /// Channel 15 classification not known. 857 | const CH_15 = 1, 1 << 7; 858 | /// Channel 16 classification not known. 859 | const CH_16 = 2, 1 << 0; 860 | /// Channel 17 classification not known. 861 | const CH_17 = 2, 1 << 1; 862 | /// Channel 18 classification not known. 863 | const CH_18 = 2, 1 << 2; 864 | /// Channel 19 classification not known. 865 | const CH_19 = 2, 1 << 3; 866 | /// Channel 20 classification not known. 867 | const CH_20 = 2, 1 << 4; 868 | /// Channel 21 classification not known. 869 | const CH_21 = 2, 1 << 5; 870 | /// Channel 22 classification not known. 871 | const CH_22 = 2, 1 << 6; 872 | /// Channel 23 classification not known. 873 | const CH_23 = 2, 1 << 7; 874 | /// Channel 24 classification not known. 875 | const CH_24 = 3, 1 << 0; 876 | /// Channel 25 classification not known. 877 | const CH_25 = 3, 1 << 1; 878 | /// Channel 26 classification not known. 879 | const CH_26 = 3, 1 << 2; 880 | /// Channel 27 classification not known. 881 | const CH_27 = 3, 1 << 3; 882 | /// Channel 28 classification not known. 883 | const CH_28 = 3, 1 << 4; 884 | /// Channel 29 classification not known. 885 | const CH_29 = 3, 1 << 5; 886 | /// Channel 30 classification not known. 887 | const CH_30 = 3, 1 << 6; 888 | /// Channel 31 classification not known. 889 | const CH_31 = 3, 1 << 7; 890 | /// Channel 32 classification not known. 891 | const CH_32 = 4, 1 << 0; 892 | /// Channel 33 classification not known. 893 | const CH_33 = 4, 1 << 1; 894 | /// Channel 34 classification not known. 895 | const CH_34 = 4, 1 << 2; 896 | /// Channel 35 classification not known. 897 | const CH_35 = 4, 1 << 3; 898 | /// Channel 36 classification not known. 899 | const CH_36 = 4, 1 << 4; 900 | } 901 | -------------------------------------------------------------------------------- /tests/command_complete.rs: -------------------------------------------------------------------------------- 1 | extern crate bluetooth_hci as hci; 2 | 3 | use hci::event::command::*; 4 | use hci::event::*; 5 | use std::convert::{TryFrom, TryInto}; 6 | 7 | #[derive(Debug)] 8 | struct VendorEvent; 9 | #[derive(Debug)] 10 | struct VendorError; 11 | #[derive(Clone, Debug)] 12 | enum VendorReturnParameters { 13 | Opcode10 { status: hci::Status }, 14 | } 15 | #[derive(Copy, Clone, Debug, PartialEq)] 16 | pub enum VendorStatus { 17 | FourFive, 18 | FiveZero, 19 | } 20 | 21 | impl hci::event::VendorEvent for VendorEvent { 22 | type Error = VendorError; 23 | type ReturnParameters = VendorReturnParameters; 24 | type Status = VendorStatus; 25 | 26 | fn new(_buffer: &[u8]) -> Result> { 27 | Err(hci::event::Error::Vendor(VendorError)) 28 | } 29 | } 30 | 31 | impl hci::event::VendorReturnParameters for VendorReturnParameters { 32 | type Error = VendorError; 33 | 34 | fn new(buffer: &[u8]) -> Result> { 35 | if buffer.len() < 4 { 36 | return Err(hci::event::Error::Vendor(VendorError)); 37 | } 38 | if buffer[1] != 10 { 39 | return Err(hci::event::Error::Vendor(VendorError)); 40 | } 41 | 42 | Ok(VendorReturnParameters::Opcode10 { 43 | status: buffer[3] 44 | .try_into() 45 | .map_err(|_e| hci::event::Error::Vendor(VendorError))?, 46 | }) 47 | } 48 | } 49 | 50 | impl TryFrom for VendorStatus { 51 | type Error = hci::BadStatusError; 52 | 53 | fn try_from(value: u8) -> Result { 54 | match value { 55 | 0x45 => Ok(VendorStatus::FourFive), 56 | 0x50 => Ok(VendorStatus::FiveZero), 57 | _ => Err(hci::BadStatusError::BadValue(value)), 58 | } 59 | } 60 | } 61 | 62 | type TestEvent = Event; 63 | 64 | #[test] 65 | fn command_complete_failed() { 66 | let buffer = [0x0E, 3, 1, 0x67, 0x43]; 67 | match TestEvent::new(Packet(&buffer)) { 68 | Err(Error::UnknownOpcode(opcode)) => assert_eq!(opcode.0, 0x4367), 69 | other => panic!("Did not get unknown opcode: {:?}", other), 70 | } 71 | } 72 | 73 | #[test] 74 | fn unsolicited_command_complete() { 75 | let buffer = [0x0E, 3, 1, 0x00, 0x00]; 76 | match TestEvent::new(Packet(&buffer)) { 77 | Ok(Event::CommandComplete(event)) => { 78 | assert_eq!(event.num_hci_command_packets, 1); 79 | match event.return_params { 80 | ReturnParameters::Spontaneous => (), 81 | other => panic!("Got return parameters: {:?}", other), 82 | } 83 | } 84 | other => panic!("Did not get command complete event: {:?}", other), 85 | } 86 | } 87 | 88 | macro_rules! status_only { 89 | { 90 | $($(#[$inner:ident $($args:tt)*])* 91 | $fn:ident($oc0:expr, $oc1:expr, $return:path);)* 92 | } => { 93 | $( 94 | $(#[$inner $($args)*])* 95 | #[test] 96 | fn $fn() { 97 | let buffer = [0x0E, 4, 8, $oc0, $oc1, 0]; 98 | match TestEvent::new(Packet(&buffer)) { 99 | Ok(Event::CommandComplete(event)) => { 100 | assert_eq!(event.num_hci_command_packets, 8); 101 | match event.return_params { 102 | $return(status) => { 103 | assert_eq!(status, hci::Status::Success); 104 | } 105 | other => panic!("Wrong return parameters: {:?}", other), 106 | } 107 | } 108 | other => panic!("Did not get command complete event: {:?}", other), 109 | } 110 | } 111 | )* 112 | } 113 | } 114 | 115 | status_only! { 116 | set_event_mask(0x01, 0x0C, ReturnParameters::SetEventMask); 117 | reset(0x03, 0x0C, ReturnParameters::Reset); 118 | le_set_event_mask(0x01, 0x20, ReturnParameters::LeSetEventMask); 119 | le_set_random_address(0x05, 0x20, ReturnParameters::LeSetRandomAddress); 120 | le_set_advertising_parameters(0x06, 0x20, ReturnParameters::LeSetAdvertisingParameters); 121 | le_set_advertising_data(0x08, 0x20, ReturnParameters::LeSetAdvertisingData); 122 | le_set_scan_response_data(0x09, 0x20, ReturnParameters::LeSetScanResponseData); 123 | #[cfg(not(feature = "version-5-0"))] 124 | le_set_advertise_enable(0x0A, 0x20, ReturnParameters::LeSetAdvertiseEnable); 125 | #[cfg(feature = "version-5-0")] 126 | le_set_advertising_enable(0x0A, 0x20, ReturnParameters::LeSetAdvertisingEnable); 127 | le_set_scan_parameters(0x0B, 0x20, ReturnParameters::LeSetScanParameters); 128 | le_set_scan_enable(0x0C, 0x20, ReturnParameters::LeSetScanEnable); 129 | le_create_connection_cancel(0x0E, 0x20, ReturnParameters::LeCreateConnectionCancel); 130 | le_clear_white_list(0x10, 0x20, ReturnParameters::LeClearWhiteList); 131 | le_add_device_to_whitelist(0x11, 0x20, ReturnParameters::LeAddDeviceToWhiteList); 132 | le_remove_device_from_whitelist(0x12, 0x20, ReturnParameters::LeRemoveDeviceFromWhiteList); 133 | le_set_host_channel_classification(0x14, 0x20, 134 | ReturnParameters::LeSetHostChannelClassification); 135 | le_receiver_test(0x1D, 0x20, ReturnParameters::LeReceiverTest); 136 | le_transmitter_test(0x1E, 0x20, ReturnParameters::LeTransmitterTest); 137 | } 138 | 139 | #[test] 140 | fn read_tx_power_level() { 141 | let buffer = [0x0E, 7, 6, 0x2D, 0x0C, 0x00, 0x01, 0x02, 0x03]; 142 | match TestEvent::new(Packet(&buffer)) { 143 | Ok(Event::CommandComplete(event)) => { 144 | assert_eq!(event.num_hci_command_packets, 6); 145 | match event.return_params { 146 | ReturnParameters::ReadTxPowerLevel(params) => { 147 | assert_eq!(params.status, hci::Status::Success); 148 | assert_eq!(params.conn_handle, hci::ConnectionHandle(0x0201)); 149 | assert_eq!(params.tx_power_level_dbm, 0x03); 150 | } 151 | other => panic!("Got return parameters: {:?}", other), 152 | } 153 | } 154 | other => panic!("Did not get command complete event: {:?}", other), 155 | } 156 | } 157 | 158 | #[test] 159 | fn read_local_version_information() { 160 | let buffer = [ 161 | 0x0E, 12, 0x01, 0x01, 0x10, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 162 | ]; 163 | match TestEvent::new(Packet(&buffer)) { 164 | Ok(Event::CommandComplete(event)) => { 165 | assert_eq!(event.num_hci_command_packets, 1); 166 | match event.return_params { 167 | ReturnParameters::ReadLocalVersionInformation(local_version_info) => { 168 | assert_eq!(local_version_info.status, hci::Status::Success); 169 | assert_eq!(local_version_info.hci_version, 2); 170 | assert_eq!(local_version_info.hci_revision, 0x0403); 171 | assert_eq!(local_version_info.lmp_version, 5); 172 | assert_eq!(local_version_info.manufacturer_name, 0x0706); 173 | assert_eq!(local_version_info.lmp_subversion, 0x0908); 174 | } 175 | other => panic!( 176 | "Did not get Read Local Version Info return params: {:?}", 177 | other 178 | ), 179 | } 180 | } 181 | other => panic!("Did not get command complete event: {:?}", other), 182 | } 183 | } 184 | 185 | #[cfg(feature = "version-4-1")] 186 | #[test] 187 | fn read_local_supported_commands() { 188 | let buffer = [ 189 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 190 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 191 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 193 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 194 | ]; 195 | match TestEvent::new(Packet(&buffer)) { 196 | Ok(Event::CommandComplete(event)) => { 197 | assert_eq!(event.num_hci_command_packets, 1); 198 | match event.return_params { 199 | ReturnParameters::ReadLocalSupportedCommands(params) => { 200 | assert_eq!(params.status, hci::Status::Success); 201 | assert_eq!( 202 | params.supported_commands, 203 | CommandFlags::INQUIRY 204 | | CommandFlags::REJECT_CONNECTION_REQUEST 205 | | CommandFlags::MASTER_LINK_KEY 206 | | CommandFlags::PARK_STATE 207 | | CommandFlags::FLOW_SPECIFICATION 208 | | CommandFlags::WRITE_STORED_LINK_KEY 209 | | CommandFlags::WRITE_SCAN_ENABLE 210 | | CommandFlags::READ_PAGE_SCAN_ACTIVITY 211 | | CommandFlags::WRITE_CLASS_OF_DEVICE 212 | | CommandFlags::READ_TRANSMIT_POWER_LEVEL 213 | | CommandFlags::READ_CURRENT_IAC_LAP 214 | | CommandFlags::READ_INQUIRY_SCAN_TYPE 215 | | CommandFlags::READ_LOCAL_EXTENDED_FEATURES 216 | | CommandFlags::READ_CLOCK 217 | | CommandFlags::READ_LOOPBACK_MODE 218 | | CommandFlags::WRITE_EXTENDED_INQUIRY_RESPONSE 219 | | CommandFlags::READ_DEFAULT_ERRONEOUS_DATA_REPORTING 220 | | CommandFlags::USER_PASSKEY_REQUEST_NEGATIVE_REPLY 221 | | CommandFlags::READ_ENCRYPTION_KEY_SIZE 222 | | CommandFlags::DISCONNECT_LOGICAL_LINK 223 | | CommandFlags::READ_LOCAL_AMP_ASSOC 224 | | CommandFlags::AMP_TEST 225 | | CommandFlags::READ_ENHANCED_TRANSMIT_POWER_LEVEL 226 | | CommandFlags::LE_READ_BUFFER_SIZE 227 | | CommandFlags::LE_SET_SCAN_PARAMETERS 228 | | CommandFlags::LE_SET_HOST_CHANNEL_CLASSIFICATION 229 | | CommandFlags::LE_RECEIVER_TEST 230 | | CommandFlags::READ_LOCAL_SUPPORTED_CODECS 231 | | CommandFlags::TRUNCATED_PAGE 232 | | CommandFlags::READ_SYNCHRONIZATION_TRAIN_PARAMETERS 233 | | CommandFlags::WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS 234 | | CommandFlags::WRITE_EXTENDED_PAGE_TIMEOUT 235 | ); 236 | assert!(params.supported_commands.is_set(CommandFlags::INQUIRY)); 237 | assert!(params 238 | .supported_commands 239 | .contains(CommandFlags::INQUIRY | CommandFlags::REJECT_CONNECTION_REQUEST)); 240 | } 241 | other => panic!( 242 | "Did not get Read Supported Commands return params: {:?}", 243 | other 244 | ), 245 | } 246 | } 247 | other => panic!("Did not get command complete event: {:?}", other), 248 | } 249 | } 250 | 251 | #[cfg(feature = "version-4-1")] 252 | #[test] 253 | fn read_local_supported_commands_failed_bad_command_flag() { 254 | let buffer = [ 255 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 256 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 257 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 258 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 259 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 260 | ]; 261 | match TestEvent::new(Packet(&buffer)) { 262 | Err(Error::BadCommandFlag) => (), 263 | other => panic!("Did not get Bad Command Flag: {:?}", other), 264 | } 265 | } 266 | 267 | #[cfg(feature = "version-4-2")] 268 | #[test] 269 | fn read_local_supported_commands() { 270 | let buffer = [ 271 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 272 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 273 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 274 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 275 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 276 | ]; 277 | match TestEvent::new(Packet(&buffer)) { 278 | Ok(Event::CommandComplete(event)) => { 279 | assert_eq!(event.num_hci_command_packets, 1); 280 | match event.return_params { 281 | ReturnParameters::ReadLocalSupportedCommands(params) => { 282 | assert_eq!(params.status, hci::Status::Success); 283 | assert_eq!( 284 | params.supported_commands, 285 | CommandFlags::INQUIRY 286 | | CommandFlags::REJECT_CONNECTION_REQUEST 287 | | CommandFlags::MASTER_LINK_KEY 288 | | CommandFlags::PARK_STATE 289 | | CommandFlags::FLOW_SPECIFICATION 290 | | CommandFlags::WRITE_STORED_LINK_KEY 291 | | CommandFlags::WRITE_SCAN_ENABLE 292 | | CommandFlags::READ_PAGE_SCAN_ACTIVITY 293 | | CommandFlags::WRITE_CLASS_OF_DEVICE 294 | | CommandFlags::READ_TRANSMIT_POWER_LEVEL 295 | | CommandFlags::READ_CURRENT_IAC_LAP 296 | | CommandFlags::READ_INQUIRY_SCAN_TYPE 297 | | CommandFlags::READ_LOCAL_EXTENDED_FEATURES 298 | | CommandFlags::READ_CLOCK 299 | | CommandFlags::READ_LOOPBACK_MODE 300 | | CommandFlags::WRITE_EXTENDED_INQUIRY_RESPONSE 301 | | CommandFlags::READ_DEFAULT_ERRONEOUS_DATA_REPORTING 302 | | CommandFlags::USER_PASSKEY_REQUEST_NEGATIVE_REPLY 303 | | CommandFlags::READ_ENCRYPTION_KEY_SIZE 304 | | CommandFlags::DISCONNECT_LOGICAL_LINK 305 | | CommandFlags::READ_LOCAL_AMP_ASSOC 306 | | CommandFlags::AMP_TEST 307 | | CommandFlags::READ_ENHANCED_TRANSMIT_POWER_LEVEL 308 | | CommandFlags::LE_READ_BUFFER_SIZE 309 | | CommandFlags::LE_SET_SCAN_PARAMETERS 310 | | CommandFlags::LE_SET_HOST_CHANNEL_CLASSIFICATION 311 | | CommandFlags::LE_RECEIVER_TEST 312 | | CommandFlags::READ_LOCAL_SUPPORTED_CODECS 313 | | CommandFlags::TRUNCATED_PAGE 314 | | CommandFlags::READ_SYNCHRONIZATION_TRAIN_PARAMETERS 315 | | CommandFlags::WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS 316 | | CommandFlags::WRITE_EXTENDED_PAGE_TIMEOUT 317 | | CommandFlags::LE_GENERATE_DH_KEY 318 | | CommandFlags::LE_READ_MAXIMUM_DATA_LENGTH 319 | ); 320 | assert!(params.supported_commands.is_set(CommandFlags::INQUIRY)); 321 | assert!(params 322 | .supported_commands 323 | .contains(CommandFlags::INQUIRY | CommandFlags::REJECT_CONNECTION_REQUEST)); 324 | } 325 | other => panic!( 326 | "Did not get Read Supported Commands return params: {:?}", 327 | other 328 | ), 329 | } 330 | } 331 | other => panic!("Did not get command complete event: {:?}", other), 332 | } 333 | } 334 | 335 | #[cfg(feature = "version-4-2")] 336 | #[test] 337 | fn read_local_supported_commands_failed_bad_command_flag() { 338 | let buffer = [ 339 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 340 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 341 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 343 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 344 | ]; 345 | match TestEvent::new(Packet(&buffer)) { 346 | Err(Error::BadCommandFlag) => (), 347 | other => panic!("Did not get Bad Command Flag: {:?}", other), 348 | } 349 | } 350 | 351 | #[cfg(feature = "version-5-0")] 352 | #[test] 353 | fn read_local_supported_commands() { 354 | let buffer = [ 355 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 356 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 357 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 358 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 359 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 360 | ]; 361 | match TestEvent::new(Packet(&buffer)) { 362 | Ok(Event::CommandComplete(event)) => { 363 | assert_eq!(event.num_hci_command_packets, 1); 364 | match event.return_params { 365 | ReturnParameters::ReadLocalSupportedCommands(params) => { 366 | assert_eq!(params.status, hci::Status::Success); 367 | assert_eq!( 368 | params.supported_commands, 369 | CommandFlags::INQUIRY 370 | | CommandFlags::REJECT_CONNECTION_REQUEST 371 | | CommandFlags::MASTER_LINK_KEY 372 | | CommandFlags::PARK_STATE 373 | | CommandFlags::FLOW_SPECIFICATION 374 | | CommandFlags::WRITE_STORED_LINK_KEY 375 | | CommandFlags::WRITE_SCAN_ENABLE 376 | | CommandFlags::READ_PAGE_SCAN_ACTIVITY 377 | | CommandFlags::WRITE_CLASS_OF_DEVICE 378 | | CommandFlags::READ_TRANSMIT_POWER_LEVEL 379 | | CommandFlags::READ_CURRENT_IAC_LAP 380 | | CommandFlags::READ_INQUIRY_SCAN_TYPE 381 | | CommandFlags::READ_LOCAL_EXTENDED_FEATURES 382 | | CommandFlags::READ_CLOCK 383 | | CommandFlags::READ_LOOPBACK_MODE 384 | | CommandFlags::WRITE_EXTENDED_INQUIRY_RESPONSE 385 | | CommandFlags::READ_DEFAULT_ERRONEOUS_DATA_REPORTING 386 | | CommandFlags::USER_PASSKEY_REQUEST_NEGATIVE_REPLY 387 | | CommandFlags::READ_ENCRYPTION_KEY_SIZE 388 | | CommandFlags::DISCONNECT_LOGICAL_LINK 389 | | CommandFlags::READ_LOCAL_AMP_ASSOC 390 | | CommandFlags::AMP_TEST 391 | | CommandFlags::READ_ENHANCED_TRANSMIT_POWER_LEVEL 392 | | CommandFlags::LE_READ_BUFFER_SIZE 393 | | CommandFlags::LE_SET_SCAN_PARAMETERS 394 | | CommandFlags::LE_SET_HOST_CHANNEL_CLASSIFICATION 395 | | CommandFlags::LE_RECEIVER_TEST 396 | | CommandFlags::READ_LOCAL_SUPPORTED_CODECS 397 | | CommandFlags::TRUNCATED_PAGE 398 | | CommandFlags::READ_SYNCHRONIZATION_TRAIN_PARAMETERS 399 | | CommandFlags::WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS 400 | | CommandFlags::WRITE_EXTENDED_PAGE_TIMEOUT 401 | | CommandFlags::LE_GENERATE_DH_KEY 402 | | CommandFlags::LE_READ_MAXIMUM_DATA_LENGTH 403 | | CommandFlags::LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND 404 | | CommandFlags::LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND 405 | | CommandFlags::LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND 406 | ); 407 | assert!(params.supported_commands.is_set(CommandFlags::INQUIRY)); 408 | assert!(params 409 | .supported_commands 410 | .contains(CommandFlags::INQUIRY | CommandFlags::REJECT_CONNECTION_REQUEST)); 411 | } 412 | other => panic!( 413 | "Did not get Read Supported Commands return params: {:?}", 414 | other 415 | ), 416 | } 417 | } 418 | other => panic!("Did not get command complete event: {:?}", other), 419 | } 420 | } 421 | 422 | #[cfg(feature = "version-5-0")] 423 | #[test] 424 | fn read_local_supported_commands_failed_bad_command_flag() { 425 | let buffer = [ 426 | 0x0E, 68, 1, 0x02, 0x10, 0x00, 0x01, 0x02, 0x04, 0x00, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 427 | 0x04, 0x08, 0x10, 0x00, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 428 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 429 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 430 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 431 | ]; 432 | match TestEvent::new(Packet(&buffer)) { 433 | Err(Error::BadCommandFlag) => (), 434 | other => panic!("Did not get Bad Command Flag: {:?}", other), 435 | } 436 | } 437 | 438 | #[test] 439 | fn read_local_supported_features() { 440 | let buffer = [ 441 | 0x0E, 12, 1, 0x03, 0x10, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 442 | ]; 443 | match TestEvent::new(Packet(&buffer)) { 444 | Ok(Event::CommandComplete(event)) => { 445 | assert_eq!(event.num_hci_command_packets, 1); 446 | match event.return_params { 447 | ReturnParameters::ReadLocalSupportedFeatures(params) => { 448 | assert_eq!(params.status, hci::Status::Success); 449 | assert_eq!( 450 | params.supported_features, 451 | LmpFeatures::THREE_SLOT_PACKETS 452 | | LmpFeatures::POWER_CONTROL_REQUESTS 453 | | LmpFeatures::POWER_CONTROL 454 | | LmpFeatures::ENHANCED_INQUIRY_SCAN 455 | | LmpFeatures::AFH_CLASSIFICATION_PERIPHERAL 456 | | LmpFeatures::ENHANCED_DATA_RATE_ESCO_2_MB_PER_S_MODE 457 | | LmpFeatures::NON_FLUSHABLE_PACKET_BOUNDARY_FLAG 458 | | LmpFeatures::EXTENDED_FEATURES 459 | ); 460 | } 461 | other => panic!( 462 | "Did not get Read Supported Features return params: {:?}", 463 | other 464 | ), 465 | } 466 | } 467 | other => panic!("Did not get command complete event: {:?}", other), 468 | } 469 | } 470 | 471 | #[test] 472 | fn read_bd_addr() { 473 | let buffer = [ 474 | 0x0E, 10, 1, 0x09, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 475 | ]; 476 | match TestEvent::new(Packet(&buffer)) { 477 | Ok(Event::CommandComplete(event)) => { 478 | assert_eq!(event.num_hci_command_packets, 1); 479 | match event.return_params { 480 | ReturnParameters::ReadBdAddr(params) => { 481 | assert_eq!(params.status, hci::Status::Success); 482 | assert_eq!( 483 | params.bd_addr, 484 | hci::BdAddr([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) 485 | ); 486 | } 487 | other => panic!("Did not get Read BDADDR return params: {:?}", other), 488 | } 489 | } 490 | other => panic!("Did not get command complete event: {:?}", other), 491 | } 492 | } 493 | 494 | #[test] 495 | fn read_rssi() { 496 | let buffer = [0x0E, 7, 1, 0x05, 0x14, 0x00, 0x01, 0x02, 0x03]; 497 | match TestEvent::new(Packet(&buffer)) { 498 | Ok(Event::CommandComplete(event)) => { 499 | assert_eq!(event.num_hci_command_packets, 1); 500 | match event.return_params { 501 | ReturnParameters::ReadRssi(params) => { 502 | assert_eq!(params.status, hci::Status::Success); 503 | assert_eq!(params.conn_handle, hci::ConnectionHandle(0x0201)); 504 | assert_eq!(params.rssi, 0x03); 505 | } 506 | other => panic!("Did not get Read RSSI return params: {:?}", other), 507 | } 508 | } 509 | other => panic!("Did not get command complete event: {:?}", other), 510 | } 511 | } 512 | 513 | #[test] 514 | fn le_read_buffer_size() { 515 | let buffer = [0x0E, 7, 2, 0x02, 0x20, 0x00, 0x01, 0x02, 0x03]; 516 | match TestEvent::new(Packet(&buffer)) { 517 | Ok(Event::CommandComplete(event)) => { 518 | assert_eq!(event.num_hci_command_packets, 2); 519 | match event.return_params { 520 | ReturnParameters::LeReadBufferSize(event) => { 521 | assert_eq!(event.status, hci::Status::Success); 522 | assert_eq!(event.data_packet_length, 0x0201); 523 | assert_eq!(event.data_packet_count, 0x03); 524 | } 525 | other => panic!("Did not get LE Read Buffer Size return params: {:?}", other), 526 | } 527 | } 528 | other => panic!("Did not get command complete event: {:?}", other), 529 | } 530 | } 531 | 532 | #[cfg(feature = "version-4-1")] 533 | #[test] 534 | fn le_read_local_supported_features() { 535 | let buffer = [ 536 | 0x0E, 12, 1, 0x03, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 537 | ]; 538 | match TestEvent::new(Packet(&buffer)) { 539 | Ok(Event::CommandComplete(event)) => { 540 | assert_eq!(event.num_hci_command_packets, 1); 541 | match event.return_params { 542 | ReturnParameters::LeReadLocalSupportedFeatures(event) => { 543 | assert_eq!(event.status, hci::Status::Success); 544 | assert_eq!( 545 | event.supported_features, 546 | LeFeatures::ENCRYPTION | LeFeatures::PING 547 | ); 548 | } 549 | other => panic!("Did not get LE Read Buffer Size return params: {:?}", other), 550 | } 551 | } 552 | other => panic!("Did not get command complete event: {:?}", other), 553 | } 554 | } 555 | 556 | #[cfg(feature = "version-4-2")] 557 | #[test] 558 | fn le_read_local_supported_features() { 559 | let buffer = [ 560 | 0x0E, 12, 1, 0x03, 0x20, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 561 | ]; 562 | match TestEvent::new(Packet(&buffer)) { 563 | Ok(Event::CommandComplete(event)) => { 564 | assert_eq!(event.num_hci_command_packets, 1); 565 | match event.return_params { 566 | ReturnParameters::LeReadLocalSupportedFeatures(event) => { 567 | assert_eq!(event.status, hci::Status::Success); 568 | assert_eq!( 569 | event.supported_features, 570 | LeFeatures::ENCRYPTION | LeFeatures::EXTENDED_SCANNER_FILTER_POLICIES 571 | ); 572 | } 573 | other => panic!("Did not get LE Read Buffer Size return params: {:?}", other), 574 | } 575 | } 576 | other => panic!("Did not get command complete event: {:?}", other), 577 | } 578 | } 579 | 580 | #[cfg(feature = "version-5-0")] 581 | #[test] 582 | fn le_read_local_supported_features() { 583 | let buffer = [ 584 | 0x0E, 12, 1, 0x03, 0x20, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 585 | ]; 586 | match TestEvent::new(Packet(&buffer)) { 587 | Ok(Event::CommandComplete(event)) => { 588 | assert_eq!(event.num_hci_command_packets, 1); 589 | match event.return_params { 590 | ReturnParameters::LeReadLocalSupportedFeatures(event) => { 591 | assert_eq!(event.status, hci::Status::Success); 592 | assert_eq!( 593 | event.supported_features, 594 | LeFeatures::EXTENDED_REJECT_INDICATION 595 | | LeFeatures::STABLE_MODULATION_INDEX_TX 596 | | LeFeatures::MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE 597 | ); 598 | } 599 | other => panic!("Did not get LE Read Buffer Size return params: {:?}", other), 600 | } 601 | } 602 | other => panic!("Did not get command complete event: {:?}", other), 603 | } 604 | } 605 | 606 | #[test] 607 | fn le_read_advertising_channel_tx_power() { 608 | let buffer = [0x0E, 5, 1, 0x07, 0x20, 0x00, 0x01]; 609 | match TestEvent::new(Packet(&buffer)) { 610 | Ok(Event::CommandComplete(event)) => { 611 | assert_eq!(event.num_hci_command_packets, 1); 612 | match event.return_params { 613 | ReturnParameters::LeReadAdvertisingChannelTxPower(params) => { 614 | assert_eq!(params.status, hci::Status::Success); 615 | assert_eq!(params.power, 0x01); 616 | } 617 | other => panic!( 618 | "Did not get LE Read Advertising Channel TX Power return params: {:?}", 619 | other 620 | ), 621 | } 622 | } 623 | other => panic!("Did not get command complete event: {:?}", other), 624 | } 625 | } 626 | 627 | #[test] 628 | fn le_read_white_list_size() { 629 | let buffer = [0x0E, 5, 1, 0x0F, 0x20, 0x00, 0x16]; 630 | match TestEvent::new(Packet(&buffer)) { 631 | Ok(Event::CommandComplete(event)) => { 632 | assert_eq!(event.num_hci_command_packets, 1); 633 | match event.return_params { 634 | ReturnParameters::LeReadWhiteListSize(status, white_list_size) => { 635 | assert_eq!(status, hci::Status::Success); 636 | assert_eq!(white_list_size, 0x16); 637 | } 638 | other => panic!( 639 | "Did not get LE Read White List Size return params: {:?}", 640 | other 641 | ), 642 | } 643 | } 644 | other => panic!("Did not get command complete event: {:?}", other), 645 | } 646 | } 647 | 648 | #[test] 649 | fn le_read_channel_map() { 650 | let buffer = [ 651 | 0x0E, 11, 1, 0x15, 0x20, 0x00, 0x01, 0x02, 0x11, 0x11, 0x11, 0x11, 0x11, 652 | ]; 653 | match TestEvent::new(Packet(&buffer)) { 654 | Ok(Event::CommandComplete(event)) => { 655 | assert_eq!(event.num_hci_command_packets, 1); 656 | match event.return_params { 657 | ReturnParameters::LeReadChannelMap(params) => { 658 | assert_eq!(params.status, hci::Status::Success); 659 | assert_eq!(params.conn_handle, hci::ConnectionHandle(0x0201)); 660 | assert_eq!( 661 | params.channel_map, 662 | hci::ChannelClassification::CH_0 663 | | hci::ChannelClassification::CH_4 664 | | hci::ChannelClassification::CH_8 665 | | hci::ChannelClassification::CH_12 666 | | hci::ChannelClassification::CH_16 667 | | hci::ChannelClassification::CH_20 668 | | hci::ChannelClassification::CH_24 669 | | hci::ChannelClassification::CH_28 670 | | hci::ChannelClassification::CH_32 671 | | hci::ChannelClassification::CH_36 672 | ); 673 | } 674 | other => panic!( 675 | "Did not get LE Read White List Size return params: {:?}", 676 | other 677 | ), 678 | } 679 | } 680 | other => panic!("Did not get command complete event: {:?}", other), 681 | } 682 | } 683 | 684 | #[test] 685 | fn le_read_channel_map_failed_reserved() { 686 | let buffer = [ 687 | 0x0E, 11, 1, 0x15, 0x20, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 688 | ]; 689 | match TestEvent::new(Packet(&buffer)) { 690 | Err(Error::InvalidChannelMap(bytes)) => { 691 | assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x20]); 692 | } 693 | other => panic!("Did not get invalid channel map: {:?}", other), 694 | } 695 | } 696 | 697 | #[test] 698 | fn le_encrypt() { 699 | let buffer = [ 700 | 0x0E, 20, 1, 0x17, 0x20, 0x00, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 701 | ]; 702 | match TestEvent::new(Packet(&buffer)) { 703 | Ok(Event::CommandComplete(event)) => { 704 | assert_eq!(event.num_hci_command_packets, 1); 705 | match event.return_params { 706 | ReturnParameters::LeEncrypt(params) => { 707 | assert_eq!(params.status, hci::Status::Success); 708 | assert_eq!( 709 | params.encrypted_data.0, 710 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 711 | ); 712 | } 713 | other => panic!("Did not get LE Encrypt return params: {:?}", other), 714 | } 715 | } 716 | other => panic!("Did not get command complete event: {:?}", other), 717 | } 718 | } 719 | 720 | #[test] 721 | fn le_rand() { 722 | let buffer = [ 723 | 0x0E, 12, 1, 0x18, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 724 | ]; 725 | match TestEvent::new(Packet(&buffer)) { 726 | Ok(Event::CommandComplete(event)) => { 727 | assert_eq!(event.num_hci_command_packets, 1); 728 | match event.return_params { 729 | ReturnParameters::LeRand(params) => { 730 | assert_eq!(params.status, hci::Status::Success); 731 | assert_eq!(params.random_number, 0x0807_0605_0403_0201); 732 | } 733 | other => panic!("Did not get LE Rand return params: {:?}", other), 734 | } 735 | } 736 | other => panic!("Did not get command complete event: {:?}", other), 737 | } 738 | } 739 | 740 | #[test] 741 | fn le_long_term_key_request_reply() { 742 | let buffer = [0x0E, 6, 1, 0x1A, 0x20, 0x00, 0x01, 0x02]; 743 | match TestEvent::new(Packet(&buffer)) { 744 | Ok(Event::CommandComplete(event)) => { 745 | assert_eq!(event.num_hci_command_packets, 1); 746 | match event.return_params { 747 | ReturnParameters::LeLongTermKeyRequestReply(params) => { 748 | assert_eq!(params.status, hci::Status::Success); 749 | assert_eq!(params.conn_handle, hci::ConnectionHandle(0x0201)); 750 | } 751 | other => panic!( 752 | "Did not get LE LTK Request Reply return params: {:?}", 753 | other 754 | ), 755 | } 756 | } 757 | other => panic!("Did not get command complete event: {:?}", other), 758 | } 759 | } 760 | 761 | #[test] 762 | fn le_long_term_key_request_negative_reply() { 763 | let buffer = [0x0E, 6, 1, 0x1B, 0x20, 0x00, 0x01, 0x02]; 764 | match TestEvent::new(Packet(&buffer)) { 765 | Ok(Event::CommandComplete(event)) => { 766 | assert_eq!(event.num_hci_command_packets, 1); 767 | match event.return_params { 768 | ReturnParameters::LeLongTermKeyRequestNegativeReply(params) => { 769 | assert_eq!(params.status, hci::Status::Success); 770 | assert_eq!(params.conn_handle, hci::ConnectionHandle(0x0201)); 771 | } 772 | other => panic!( 773 | "Did not get LE LTK Request Negative Reply return params: {:?}", 774 | other 775 | ), 776 | } 777 | } 778 | other => panic!("Did not get command complete event: {:?}", other), 779 | } 780 | } 781 | 782 | #[test] 783 | fn le_read_supported_states() { 784 | let buffer = [ 785 | 0x0E, 12, 1, 0x1C, 0x20, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x02, 0x00, 0x00, 786 | ]; 787 | match TestEvent::new(Packet(&buffer)) { 788 | Ok(Event::CommandComplete(event)) => { 789 | assert_eq!(event.num_hci_command_packets, 1); 790 | match event.return_params { 791 | ReturnParameters::LeReadSupportedStates(params) => { 792 | assert_eq!(params.status, hci::Status::Success); 793 | assert_eq!( 794 | params.supported_states, 795 | LeStates::NON_CONNECTABLE_ADVERTISING 796 | | LeStates::SCAN_AD_AND_PASS_SCAN 797 | | LeStates::NONCONN_AD_AND_CENTRAL_CONN 798 | | LeStates::ACT_SCAN_AND_PERIPH_CONN 799 | | LeStates::DIR_AD_HDC_AND_CENTRAL_CONN 800 | | LeStates::INITIATING_AND_PERIPH_CONN 801 | ); 802 | } 803 | other => panic!( 804 | "Did not get LE Read Supported States return params: {:?}", 805 | other 806 | ), 807 | } 808 | } 809 | other => panic!("Did not get command complete event: {:?}", other), 810 | } 811 | } 812 | 813 | #[test] 814 | fn le_read_supported_states_failed_reserved_flag() { 815 | let buffer = [ 816 | 0x0E, 12, 1, 0x1C, 0x20, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x04, 0x00, 0x00, 817 | ]; 818 | match TestEvent::new(Packet(&buffer)) { 819 | Err(Error::InvalidLeStates(bitfield)) => { 820 | assert_eq!(bitfield, 0x0000_0410_0804_0201); 821 | } 822 | other => panic!("Did not get bad LE State flags: {:?}", other), 823 | } 824 | } 825 | 826 | #[test] 827 | fn le_test_end() { 828 | let buffer = [0x0E, 6, 1, 0x1F, 0x20, 0x00, 0x01, 0x02]; 829 | match TestEvent::new(Packet(&buffer)) { 830 | Ok(Event::CommandComplete(event)) => { 831 | assert_eq!(event.num_hci_command_packets, 1); 832 | match event.return_params { 833 | ReturnParameters::LeTestEnd(params) => { 834 | assert_eq!(params.status, hci::Status::Success); 835 | assert_eq!(params.number_of_packets, 0x0201); 836 | } 837 | other => panic!("Did not get LE Test End return params: {:?}", other), 838 | } 839 | } 840 | other => panic!("Did not get command complete event: {:?}", other), 841 | } 842 | } 843 | 844 | #[test] 845 | fn vendor_command() { 846 | let buffer = [0x0E, 4, 1, 0x0A, 0xFC, 0x00]; 847 | match TestEvent::new(Packet(&buffer)) { 848 | Ok(Event::CommandComplete(event)) => { 849 | assert_eq!(event.num_hci_command_packets, 1); 850 | match event.return_params { 851 | ReturnParameters::Vendor(params) => match params { 852 | VendorReturnParameters::Opcode10 { status } => { 853 | assert_eq!(status, hci::Status::Success); 854 | } 855 | }, 856 | other => panic!("Did not get Vendor command return params: {:?}", other), 857 | } 858 | } 859 | other => panic!("Did not get command complete event: {:?}", other), 860 | } 861 | } 862 | --------------------------------------------------------------------------------