├── .gitignore ├── src ├── bluez │ ├── protocol │ │ ├── mod.rs │ │ ├── att.rs │ │ └── hci.rs │ ├── util │ │ └── mod.rs │ ├── mod.rs │ ├── constants.rs │ ├── manager │ │ └── mod.rs │ └── adapter │ │ ├── acl_stream.rs │ │ ├── mod.rs │ │ └── peripheral.rs ├── lib.rs └── api │ └── mod.rs ├── .travis.yml ├── Cargo.toml ├── LICENSE-MIT ├── examples └── lights.rs ├── README.md ├── LICENSE-APACHE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/**/*.xml 2 | .idea/ 3 | /target/ 4 | **/*.rs.bk 5 | -------------------------------------------------------------------------------- /src/bluez/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod hci; 2 | pub mod att; 3 | 4 | use nom::le_u8; 5 | 6 | named!(pub parse_uuid_128<&[u8], [u8; 16]>, count_fixed!(u8, le_u8, 16)); 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | matrix: 7 | allow_failures: 8 | - rust: nightly 9 | 10 | cache: cargo 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rumble" 3 | version = "0.3.0" 4 | authors = ["Micah Wylde "] 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/mwylde/rumble" 7 | homepage = "https://github.com/mwylde/rumble" 8 | 9 | description = """ 10 | A Rust Bluetooth Low Energy (BLE) central module 11 | library, currently supporting Linux/Bluez 12 | """ 13 | 14 | readme = "README.md" 15 | keywords = ["bluetooth", "BLE", "IOT", "bluez"] 16 | categories = ["network-programming", "hardware-support"] 17 | 18 | [dependencies] 19 | libc = "0.2.45" 20 | nix = "0.12.0" 21 | bytes = "0.4" 22 | num = "0.1.41" 23 | log = "0.3.8" 24 | enum_primitive = "0.1.1" 25 | bitflags = "1.0.1" 26 | failure = "0.1.1" 27 | failure_derive = "0.1.1" 28 | backtrace = "0.3.5" 29 | 30 | [dependencies.nom] 31 | version = "^4.0" 32 | features = ["verbose-errors"] 33 | 34 | [dev-dependencies] 35 | rand = "0.4.2" 36 | -------------------------------------------------------------------------------- /src/bluez/util/mod.rs: -------------------------------------------------------------------------------- 1 | use nix; 2 | use nix::errno::Errno; 3 | 4 | use ::Result; 5 | use Error; 6 | 7 | fn errno_to_error(errno: Errno) -> Error { 8 | match errno { 9 | Errno::EPERM => Error::PermissionDenied, 10 | Errno::ENODEV => Error::DeviceNotFound, 11 | Errno::ENOTCONN => Error::NotConnected, 12 | _ => Error::Other(errno.to_string()) 13 | } 14 | } 15 | 16 | impl From for Error { 17 | fn from(e: nix::Error) -> Self { 18 | match e { 19 | nix::Error::Sys(errno) => { 20 | errno_to_error(errno) 21 | }, 22 | _ => { 23 | Error::Other(e.to_string()) 24 | } 25 | } 26 | } 27 | } 28 | 29 | pub fn handle_error(v: i32) -> Result { 30 | if v < 0 { 31 | debug!("got error {}", Errno::last()); 32 | Err(errno_to_error(Errno::last())) 33 | } else { 34 | Ok(v) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/bluez/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod manager; 2 | pub mod adapter; 3 | mod protocol; 4 | mod util; 5 | mod constants; 6 | 7 | 8 | mod ioctl { 9 | use super::adapter; 10 | use super::manager; 11 | 12 | // #define HCIDEVUP _IOW('H', 201, int) 13 | ioctl_write_int!(hci_dev_up, b'H', 201); 14 | // #define HCIDEVDOWN _IOW('H', 202, int) 15 | ioctl_write_int!(hci_dev_down, b'H', 202); 16 | 17 | // #define HCIGETDEVLIST _IOR('H', 210, int) 18 | const HCI_GET_DEV_LIST_MAGIC: usize = (2u32 << 0i32 + 8i32 + 8i32 + 14i32 | 19 | (b'H' as (i32) << 0i32 + 8i32) as (u32) | (210i32 << 0i32) as (u32)) as 20 | (usize) | 4 /* (sizeof(i32)) */ << 0i32 + 8i32 + 8i32; 21 | ioctl_read_bad!(hci_get_dev_list, HCI_GET_DEV_LIST_MAGIC, manager::HCIDevListReq); 22 | 23 | 24 | // #define HCIGETDEVINFO _IOR('H', 211, int) 25 | const HCI_GET_DEV_MAGIC: usize = (2u32 << 0i32 + 8i32 + 8i32 + 14i32 | 26 | (b'H' as (i32) << 0i32 + 8i32) as (u32) | (211i32 << 0i32) as (u32)) as (usize) | 27 | 4 /* (sizeof(i32)) */ << 0i32 + 8i32 + 8i32; 28 | ioctl_read_bad!(hci_get_dev_info, HCI_GET_DEV_MAGIC, adapter::HCIDevInfo); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 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 | -------------------------------------------------------------------------------- /examples/lights.rs: -------------------------------------------------------------------------------- 1 | extern crate rumble; 2 | extern crate rand; 3 | 4 | use std::thread; 5 | use std::time::Duration; 6 | use rand::{Rng, thread_rng}; 7 | use rumble::bluez::manager::Manager; 8 | use rumble::api::{UUID, Central, Peripheral}; 9 | 10 | pub fn main() { 11 | let manager = Manager::new().unwrap(); 12 | 13 | // get the first bluetooth adapter 14 | let adapters = manager.adapters().unwrap(); 15 | let mut adapter = adapters.into_iter().nth(0).unwrap(); 16 | 17 | // reset the adapter -- clears out any errant state 18 | adapter = manager.down(&adapter).unwrap(); 19 | adapter = manager.up(&adapter).unwrap(); 20 | 21 | // connect to the adapter 22 | let central = adapter.connect().unwrap(); 23 | 24 | // start scanning for devices 25 | central.start_scan().unwrap(); 26 | // instead of waiting, you can use central.on_event to be notified of 27 | // new devices 28 | thread::sleep(Duration::from_secs(2)); 29 | 30 | // find the device we're interested in 31 | let light = central.peripherals().into_iter() 32 | .find(|p| p.properties().local_name.iter() 33 | .any(|name| name.contains("LEDBlue"))).unwrap(); 34 | 35 | // connect to the device 36 | light.connect().unwrap(); 37 | 38 | // discover characteristics 39 | light.discover_characteristics().unwrap(); 40 | 41 | // find the characteristic we want 42 | let chars = light.characteristics(); 43 | let cmd_char = chars.iter().find(|c| c.uuid == UUID::B16(0xFFE9)).unwrap(); 44 | 45 | // dance party 46 | let mut rng = thread_rng(); 47 | for _ in 0..20 { 48 | let color_cmd = vec![0x56, rng.gen(), rng.gen(), rng.gen(), 0x00, 0xF0, 0xAA]; 49 | light.command(&cmd_char, &color_cmd).unwrap(); 50 | thread::sleep(Duration::from_millis(200)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rumble 2 | 3 | [![Build Status](https://travis-ci.org/mwylde/rumble.svg?branch=master)](https://travis-ci.org/mwylde/rumble) 4 | [![Crates.io Version](https://img.shields.io/crates/v/rumble.svg)](https://crates.io/crates/rumble) 5 | [![docs](https://docs.rs/rumble/badge.svg)](https://docs.rs/rumble) 6 | 7 | Rumble is a Bluetooth Low Energy (BLE) central module library for Rust. 8 | Currently only Linux (with the BlueZ bluetooth library) is supported, although 9 | other operating systems may be supported in the future. Rumble interfaces with 10 | BlueZ using its socket interface rather than DBus. This offers much more control 11 | and reliability over the DBus interface, and does not require running BlueZ in 12 | experimental mode for BLE. 13 | 14 | As of version 0.2, the API is becoming more stable and the library itself more 15 | useful. You should still expect to encounter bugs, limitations, and odd behaviors. 16 | Pull requests (and wireshark traces) welcome! 17 | 18 | ## Usage 19 | 20 | An example of how to use the library to control some BLE smart lights: 21 | 22 | ```rust 23 | extern crate rumble; 24 | extern crate rand; 25 | 26 | use std::thread; 27 | use std::time::Duration; 28 | use rand::{Rng, thread_rng}; 29 | use rumble::bluez::manager::Manager; 30 | use rumble::api::{UUID, Central, Peripheral}; 31 | 32 | pub fn main() { 33 | let manager = Manager::new().unwrap(); 34 | 35 | // get the first bluetooth adapter 36 | let adapters = manager.adapters().unwrap(); 37 | let mut adapter = adapters.into_iter().nth(0).unwrap(); 38 | 39 | // reset the adapter -- clears out any errant state 40 | adapter = manager.down(&adapter).unwrap(); 41 | adapter = manager.up(&adapter).unwrap(); 42 | 43 | // connect to the adapter 44 | let central = adapter.connect().unwrap(); 45 | 46 | // start scanning for devices 47 | central.start_scan().unwrap(); 48 | // instead of waiting, you can use central.on_event to be notified of 49 | // new devices 50 | thread::sleep(Duration::from_secs(2)); 51 | 52 | // find the device we're interested in 53 | let light = central.peripherals().into_iter() 54 | .find(|p| p.properties().local_name.iter() 55 | .any(|name| name.contains("LEDBlue"))).unwrap(); 56 | 57 | // connect to the device 58 | light.connect().unwrap(); 59 | 60 | // discover characteristics 61 | light.discover_characteristics().unwrap(); 62 | 63 | // find the characteristic we want 64 | let chars = light.characteristics(); 65 | let cmd_char = chars.iter().find(|c| c.uuid == UUID::B16(0xFFE9)).unwrap(); 66 | 67 | // dance party 68 | let mut rng = thread_rng(); 69 | for _ in 0..20 { 70 | let color_cmd = vec![0x56, rng.gen(), rng.gen(), rng.gen(), 0x00, 0xF0, 0xAA]; 71 | light.command(&cmd_char, &color_cmd).unwrap(); 72 | thread::sleep(Duration::from_millis(200)); 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /src/bluez/constants.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub const HCI_COMMAND_PKT: u8 = 0x01; 4 | 5 | // hci.h 6 | pub const HCI_FILTER: i32 = 2; 7 | pub const HCI_EVENT_PKT: i32 = 0x04; 8 | pub const HCI_ACLDATA_PKT: i32 = 0x02; 9 | pub const HCI_LE_META_EVENT: i32 = 0x3E; 10 | 11 | pub const HCI_CHANNEL_RAW: u16 = 0; 12 | pub const HCI_CHANNEL_USER: u16 = 1; 13 | pub const HCI_CHANNEL_MONITOR: u16 = 2; 14 | pub const HCI_CHANNEL_CONTROL: u16 = 3; 15 | pub const HCI_CHANNEL_LOGGING: u16 = 4; 16 | 17 | pub const ACL_START_NO_FLUSH: u16 = 0x00; 18 | pub const ACL_CONT: u16 = 0x01; 19 | pub const ACL_START: u16 = 0x02; 20 | pub const HCI_OE_USER_ENDED_CONNECTION: u8 = 0x13; 21 | 22 | // bluetooth.h 23 | pub const SOL_HCI: i32 = 0; 24 | 25 | pub const ATT_CID: u16 = 4; 26 | pub const ATT_OP_ERROR_RESP: u8 = 0x01; 27 | pub const ATT_OP_EXCHANGE_MTU_REQ: u8 = 0x02; 28 | pub const ATT_OP_EXCHANGE_MTU_RESP: u8 = 0x03; 29 | pub const ATT_OP_READ_BY_TYPE_REQ: u8 = 0x08; 30 | pub const ATT_OP_READ_BY_TYPE_RESP: u8 = 0x09; 31 | pub const ATT_OP_READ_REQ: u8 = 0x0a; 32 | pub const ATT_OP_READ_RESP: u8 = 0x0b; 33 | pub const ATT_OP_READ_BY_GROUP_REQ: u8 = 0x10; 34 | pub const ATT_OP_READ_BY_GROUP_RESP: u8 = 0x11; 35 | pub const ATT_OP_WRITE_REQ: u8 = 0x12; 36 | pub const ATT_OP_WRITE_RESP: u8 = 0x13; 37 | pub const ATT_OP_VALUE_NOTIFICATION: u8 = 0x1b; 38 | pub const ATT_OP_WRITE_CMD: u8 = 0x52; 39 | 40 | pub const GATT_CHARAC_UUID: u16 = 0x2803; 41 | 42 | pub const GATT_CLIENT_CHARAC_CFG_UUID: u16 = 0x2902; 43 | pub const GATT_SERVER_CHARAC_CFG_UUID: u16 = 0x2903; 44 | 45 | pub const EVT_DISCONN_COMPLETE: u8 = 0x05; 46 | pub const EVT_ENCRYPT_CHANGE: u8 = 0x08; 47 | pub const EVT_CMD_COMPLETE: u8 = 0x0e; 48 | pub const EVT_CMD_STATUS: u8 = 0x0f; 49 | pub const EVT_LE_META_EVENT: u8 = 0x3e; 50 | 51 | pub const EVT_LE_CONN_COMPLETE: u8 = 0x01; 52 | pub const EVT_LE_ADVERTISING_REPORT: u8 = 0x02; 53 | pub const EVT_LE_CONN_UPDATE_COMPLETE: u8 = 0x03; 54 | 55 | pub const OGF_HOST_CTL: u8 = 0x03; 56 | pub const OCF_SET_EVENT_MASK: u16 = 0x0001; 57 | pub const OCF_RESET: u16 = 0x0003; 58 | pub const OCF_READ_LE_HOST_SUPPORTED: u16 = 0x006C; 59 | pub const OCF_WRITE_LE_HOST_SUPPORTED: u16 = 0x006D; 60 | 61 | pub const OGF_LINK_CTL: u8 = 0x01; 62 | pub const OCF_DISCONNECT: u16 = 0x0006; 63 | 64 | pub const OGF_INFO_PARAM: u8 = 0x04; 65 | pub const OCF_READ_LOCAL_VERSION: u16 = 0x0001; 66 | pub const OCF_READ_BD_ADDR: u16 = 0x0009; 67 | 68 | pub const OGF_STATUS_PARAM: u8 = 0x05; 69 | pub const OCF_READ_RSSI: u16 = 0x0005; 70 | 71 | pub const OGF_LE_CTL: u8 = 0x08; 72 | pub const OCF_LE_SET_EVENT_MASK: u16 = 0x0001; 73 | pub const OCF_LE_SET_SCAN_PARAMETERS: u16 = 0x000b; 74 | pub const OCF_LE_SET_SCAN_ENABLE: u16 = 0x000c; 75 | pub const OCF_LE_CREATE_CONN: u16 = 0x000d; 76 | pub const OCF_LE_CONN_UPDATE: u16 = 0x0013; 77 | pub const OCF_LE_START_ENCRYPTION: u16 = 0x0019; 78 | 79 | pub const LE_SET_SCAN_PARAMETERS_CMD: u16 = 80 | OCF_LE_SET_SCAN_PARAMETERS | (OGF_LE_CTL as u16) << 10; 81 | pub const LE_SET_SCAN_ENABLE_CMD: u16 = OCF_LE_SET_SCAN_ENABLE | 82 | (OGF_LE_CTL as u16) << 10; 83 | pub const LE_CREATE_CONN_CMD: u16 = OCF_LE_CREATE_CONN | ((OGF_LE_CTL as u16) << 10); 84 | pub const DISCONNECT_CMD: u16 = OCF_DISCONNECT | (OGF_LINK_CTL as u16) << 10; 85 | 86 | pub const BTPROTO_HCI: i32 = 1; 87 | 88 | -------------------------------------------------------------------------------- /src/bluez/manager/mod.rs: -------------------------------------------------------------------------------- 1 | use std::slice::Iter; 2 | use std::iter::Take; 3 | use std::sync::Mutex; 4 | 5 | use libc; 6 | use libc::{SOCK_RAW, AF_BLUETOOTH}; 7 | use nix::sys::ioctl::ioctl_param_type; 8 | use std::mem; 9 | 10 | use bluez::util::handle_error; 11 | use bluez::adapter::{Adapter, ConnectedAdapter}; 12 | use bluez::constants::*; 13 | use bluez::ioctl; 14 | use ::Result; 15 | 16 | #[derive(Debug, Copy)] 17 | #[repr(C)] 18 | pub struct HCIDevReq { 19 | pub dev_id: u16, 20 | pub dev_opt: u32, 21 | } 22 | 23 | impl Clone for HCIDevReq { 24 | fn clone(&self) -> Self { *self } 25 | } 26 | 27 | impl Default for HCIDevReq { 28 | fn default() -> Self { 29 | HCIDevReq { 30 | dev_id: 0, 31 | dev_opt: 0, 32 | } 33 | } 34 | } 35 | 36 | #[derive(Copy)] 37 | #[repr(C)] 38 | pub struct HCIDevListReq { 39 | dev_num: u16, 40 | dev_reqs: [HCIDevReq; 16], 41 | } 42 | 43 | impl HCIDevListReq { 44 | pub fn iter(&self) -> Take> { 45 | self.dev_reqs.iter().take(self.dev_num as usize) 46 | } 47 | } 48 | 49 | impl Clone for HCIDevListReq { 50 | fn clone(&self) -> Self { *self } 51 | } 52 | 53 | impl Default for HCIDevListReq { 54 | fn default() -> Self { 55 | HCIDevListReq { 56 | dev_num: 16u16, 57 | dev_reqs: unsafe { mem::zeroed() }, 58 | } 59 | } 60 | } 61 | 62 | /// This struct is the interface into BlueZ. It can be used to list, manage, and connect to bluetooth 63 | /// adapters. 64 | pub struct Manager { 65 | ctl_fd: Mutex 66 | } 67 | 68 | impl Manager { 69 | /// Constructs a new manager to communicate with the BlueZ system. Only one Manager should be 70 | /// created by your application. 71 | pub fn new() -> Result { 72 | let fd = handle_error(unsafe { libc::socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) })?; 73 | Ok(Manager { ctl_fd: Mutex::new(fd) }) 74 | } 75 | 76 | /// Returns the list of adapters available on the system. 77 | pub fn adapters(&self) -> Result> { 78 | let mut result: Vec = vec![]; 79 | 80 | let ctl = self.ctl_fd.lock().unwrap(); 81 | 82 | let mut dev_list = HCIDevListReq::default(); 83 | 84 | unsafe { 85 | ioctl::hci_get_dev_list(*ctl, &mut dev_list)?; 86 | } 87 | 88 | for dev_req in dev_list.iter() { 89 | let adapter = Adapter::from_dev_id(*ctl, dev_req.dev_id)?; 90 | result.push(adapter); 91 | } 92 | 93 | Ok(result) 94 | } 95 | 96 | /// Updates the state of an adapter. 97 | pub fn update(&self, adapter: &Adapter) -> Result { 98 | let ctl = self.ctl_fd.lock().unwrap(); 99 | Adapter::from_dev_id(*ctl, adapter.dev_id) 100 | } 101 | 102 | /// Disables an adapter. 103 | pub fn down(&self, adapter: &Adapter) -> Result { 104 | let ctl = self.ctl_fd.lock().unwrap(); 105 | unsafe { ioctl::hci_dev_down(*ctl, adapter.dev_id as ioctl_param_type)? }; 106 | Adapter::from_dev_id(*ctl, adapter.dev_id) 107 | } 108 | 109 | /// Enables an adapater. 110 | pub fn up(&self, adapter: &Adapter) -> Result { 111 | let ctl = self.ctl_fd.lock().unwrap(); 112 | unsafe { 113 | ioctl::hci_dev_up(*ctl, adapter.dev_id as ioctl_param_type)?; 114 | } 115 | Adapter::from_dev_id(*ctl, adapter.dev_id) 116 | } 117 | 118 | /// Establishes a connection to an adapter. Returns a `ConnectedAdapter`, which is the 119 | /// [`Central`](../../api/trait.Central.html) implementation for BlueZ. 120 | pub fn connect(&self, adapter: &Adapter) -> Result { 121 | ConnectedAdapter::new(adapter) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Rumble is a Bluetooth Low Energy (BLE) central module library for Rust. 2 | //! Currently only Linux (with the BlueZ bluetooth library) is supported, although 3 | //! other operating systems may be supported in the future. Rumble interfaces with 4 | //! BlueZ using its socket interface rather than DBus. This offers much more control 5 | //! and reliability over the DBus interface, and does not require running BlueZ in 6 | //! experimental mode for BLE. 7 | //! 8 | //! As of version 0.2, the API is becoming more stable and the library itself more 9 | //! useful. You should still expect to encounter bugs, limitations, and odd behaviors. 10 | //! Pull requests (and wireshark traces) welcome! 11 | //! 12 | //! ## Usage 13 | //! 14 | //! An example of how to use the library to control some BLE smart lights: 15 | //! 16 | //! ```rust,no_run 17 | //! extern crate rumble; 18 | //! extern crate rand; 19 | //! 20 | //! use std::thread; 21 | //! use std::time::Duration; 22 | //! use rand::{Rng, thread_rng}; 23 | //! use rumble::bluez::manager::Manager; 24 | //! use rumble::api::{UUID, Central, Peripheral}; 25 | //! 26 | //! pub fn main() { 27 | //! let manager = Manager::new().unwrap(); 28 | //! 29 | //! // get the first bluetooth adapter 30 | //! let adapters = manager.adapters().unwrap(); 31 | //! let mut adapter = adapters.into_iter().nth(0).unwrap(); 32 | //! 33 | //! // reset the adapter -- clears out any errant state 34 | //! adapter = manager.down(&adapter).unwrap(); 35 | //! adapter = manager.up(&adapter).unwrap(); 36 | //! 37 | //! // connect to the adapter 38 | //! let central = adapter.connect().unwrap(); 39 | //! 40 | //! // start scanning for devices 41 | //! central.start_scan().unwrap(); 42 | //! // instead of waiting, you can use central.on_event to be notified of 43 | //! // new devices 44 | //! thread::sleep(Duration::from_secs(2)); 45 | //! 46 | //! // find the device we're interested in 47 | //! let light = central.peripherals().into_iter() 48 | //! .find(|p| p.properties().local_name.iter() 49 | //! .any(|name| name.contains("LEDBlue"))).unwrap(); 50 | //! 51 | //! // connect to the device 52 | //! light.connect().unwrap(); 53 | //! 54 | //! // discover characteristics 55 | //! light.discover_characteristics().unwrap(); 56 | //! 57 | //! // find the characteristic we want 58 | //! let chars = light.characteristics(); 59 | //! let cmd_char = chars.iter().find(|c| c.uuid == UUID::B16(0xFFE9)).unwrap(); 60 | //! 61 | //! // dance party 62 | //! let mut rng = thread_rng(); 63 | //! for _ in 0..20 { 64 | //! let color_cmd = vec![0x56, rng.gen(), rng.gen(), rng.gen(), 0x00, 0xF0, 0xAA]; 65 | //! light.command(&cmd_char, &color_cmd).unwrap(); 66 | //! thread::sleep(Duration::from_millis(200)); 67 | //! } 68 | //! } 69 | //! ``` 70 | 71 | extern crate libc; 72 | 73 | #[macro_use] 74 | extern crate log; 75 | 76 | #[macro_use] 77 | extern crate nix; 78 | 79 | extern crate bytes; 80 | #[macro_use] extern crate enum_primitive; 81 | extern crate num; 82 | 83 | #[macro_use] 84 | extern crate nom; 85 | 86 | #[macro_use] 87 | extern crate bitflags; 88 | 89 | extern crate failure; 90 | #[macro_use] 91 | extern crate failure_derive; 92 | 93 | use std::result; 94 | use std::time::Duration; 95 | 96 | pub mod bluez; 97 | pub mod api; 98 | 99 | #[derive(Debug, Fail, Clone)] 100 | pub enum Error { 101 | #[fail(display = "Permission denied")] 102 | PermissionDenied, 103 | 104 | #[fail(display = "Device not found")] 105 | DeviceNotFound, 106 | 107 | #[fail(display = "Not connected")] 108 | NotConnected, 109 | 110 | #[fail(display = "The operation is not supported: {}", _0)] 111 | NotSupported(String), 112 | 113 | #[fail(display = "Timed out after {:?}", _0)] 114 | TimedOut(Duration), 115 | 116 | #[fail(display = "{}", _0)] 117 | Other(String), 118 | } 119 | 120 | // Rumble Result type 121 | pub type Result = result::Result; 122 | -------------------------------------------------------------------------------- /src/bluez/protocol/att.rs: -------------------------------------------------------------------------------- 1 | use nom::{le_u8, le_u16, IResult}; 2 | 3 | use ::api::{Characteristic, UUID, CharPropFlags, ValueNotification}; 4 | 5 | use bluez::constants::*; 6 | use bluez::protocol::*; 7 | use bytes::{BytesMut, BufMut}; 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | use super::*; 12 | 13 | #[test] 14 | fn test_characteristics() { 15 | let buf = [9, 7, 2, 0, 2, 3, 0, 0, 42, 4, 0, 2, 5, 0, 1, 42, 6, 0, 10, 7, 0, 2, 42]; 16 | assert_eq!(characteristics(&buf), Ok(( 17 | &[][..], 18 | Ok(vec![ 19 | Characteristic { 20 | start_handle: 2, 21 | value_handle: 3, 22 | end_handle: 0xFFFF, 23 | uuid: UUID::B16(0x2A00), 24 | properties: CharPropFlags::READ 25 | }, 26 | Characteristic { 27 | start_handle: 4, 28 | value_handle: 5, 29 | end_handle: 0xFFFF, 30 | uuid: UUID::B16(0x2A01), 31 | properties: CharPropFlags::READ 32 | }, 33 | Characteristic { 34 | start_handle: 6, 35 | value_handle: 7, 36 | end_handle: 0xFFFF, 37 | uuid: UUID::B16(0x2A02), 38 | properties: CharPropFlags::READ | CharPropFlags::WRITE 39 | }, 40 | ] 41 | )))) 42 | } 43 | 44 | #[test] 45 | fn test_value_notification() { 46 | let buf = [27, 46, 0, 165, 17, 5, 0, 0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 47 | assert_eq!(value_notification(&buf), Ok(( 48 | &[][..], 49 | ValueNotification { 50 | handle: 46, 51 | value: vec![165, 17, 5, 0, 0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 52 | }) 53 | )); 54 | } 55 | 56 | #[test] 57 | fn test_error() { 58 | let buf = [1, 8, 32, 0, 10]; 59 | assert_eq!(characteristics(&buf), Ok(( 60 | &[][..], 61 | Err(ErrorResponse { 62 | request_opcode: 0x08, 63 | handle: 0x20, 64 | error_code: 0x0a, 65 | }) 66 | ))) 67 | } 68 | 69 | #[test] 70 | fn test_read_req() { 71 | let expected: Vec = vec![0x0A, 0x25, 0x00]; 72 | assert_eq!(expected, read_req(0x0025)); 73 | } 74 | } 75 | 76 | #[derive(Debug, PartialEq)] 77 | pub struct NotifyResponse { 78 | pub typ: u8, 79 | pub handle: u16, 80 | pub value: u16, 81 | } 82 | 83 | named!(pub notify_response<&[u8], NotifyResponse>, 84 | do_parse!( 85 | _op: tag!(&[ATT_OP_READ_BY_TYPE_RESP]) >> 86 | typ: le_u8 >> 87 | handle: le_u16 >> 88 | value: le_u16 >> 89 | ( 90 | NotifyResponse { typ, handle, value } 91 | ) 92 | )); 93 | 94 | #[derive(Debug, PartialEq)] 95 | pub struct ExchangeMTURequest { 96 | pub client_rx_mtu: u16, 97 | } 98 | 99 | named!(pub mtu_request<&[u8], ExchangeMTURequest>, 100 | do_parse!( 101 | _op: tag!(&[ATT_OP_EXCHANGE_MTU_REQ]) >> 102 | client_rx_mtu: le_u16 >> 103 | ( 104 | ExchangeMTURequest { client_rx_mtu } 105 | ) 106 | )); 107 | 108 | #[derive(Debug, PartialEq)] 109 | pub struct ErrorResponse { 110 | request_opcode: u8, 111 | handle: u16, 112 | error_code: u8, 113 | } 114 | 115 | named!(pub error_response<&[u8], ErrorResponse>, 116 | do_parse!( 117 | request_opcode: le_u8 >> 118 | handle: le_u16 >> 119 | error_code: le_u8 >> 120 | ( 121 | ErrorResponse { request_opcode, handle, error_code } 122 | ) 123 | )); 124 | 125 | named!(pub value_notification<&[u8], ValueNotification>, 126 | do_parse!( 127 | _op: tag!(&[ATT_OP_VALUE_NOTIFICATION]) >> 128 | handle: le_u16 >> 129 | value: many1!(complete!(le_u8)) >> 130 | ( 131 | ValueNotification { handle, value } 132 | ) 133 | )); 134 | 135 | fn characteristic(i: &[u8], b16_uuid: bool) -> IResult<&[u8], Characteristic> { 136 | let (i, start_handle) = try_parse!(i, le_u16); 137 | let (i, properties) = try_parse!(i, le_u8); 138 | let (i, value_handle) = try_parse!(i, le_u16); 139 | let (i, uuid) = if b16_uuid { 140 | try_parse!(i, map!(le_u16, |b| UUID::B16(b))) 141 | } else { 142 | try_parse!(i, map!(parse_uuid_128, |b| UUID::B128(b))) 143 | }; 144 | 145 | Ok((i, Characteristic { 146 | start_handle, 147 | value_handle, 148 | end_handle: 0xFFFF, 149 | uuid, 150 | properties: CharPropFlags::from_bits_truncate(properties), 151 | })) 152 | } 153 | 154 | pub fn characteristics(i: &[u8]) -> IResult<&[u8], Result, ErrorResponse>> { 155 | let (i, opcode) = try_parse!(i, le_u8); 156 | 157 | let (i, result) = match opcode { 158 | ATT_OP_ERROR_RESP => { 159 | try_parse!(i, map!(error_response, |r| Err(r))) 160 | } 161 | ATT_OP_READ_BY_TYPE_RESP => { 162 | let (i, rec_len) = try_parse!(i, le_u8); 163 | let num = i.len() / rec_len as usize; 164 | let b16_uuid = rec_len == 7; 165 | try_parse!(i, map!(count!(apply!(characteristic, b16_uuid), num), |r| Ok(r))) 166 | } 167 | x => { 168 | warn!("unhandled characteristics op type {} for {:?}", x, i); 169 | (&[][..], Ok(vec![])) 170 | } 171 | }; 172 | 173 | Ok((i, result)) 174 | } 175 | 176 | pub fn read_by_type_req(start_handle: u16, end_handle: u16, uuid: UUID) -> Vec { 177 | let mut buf = BytesMut::with_capacity(3 + uuid.size()); 178 | buf.put_u8(ATT_OP_READ_BY_TYPE_REQ); 179 | buf.put_u16_le(start_handle); 180 | buf.put_u16_le(end_handle); 181 | match uuid { 182 | UUID::B16(u) => buf.put_u16_le(u), 183 | UUID::B128(u) => buf.put_slice(&u), 184 | } 185 | buf.to_vec() 186 | } 187 | 188 | pub fn read_req(handle: u16) -> Vec { 189 | let mut buf = BytesMut::with_capacity(3); 190 | buf.put_u8(ATT_OP_READ_REQ); 191 | buf.put_u16_le(handle); 192 | buf.to_vec() 193 | } 194 | -------------------------------------------------------------------------------- /src/bluez/adapter/acl_stream.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::Arc; 3 | use std::time::Duration; 4 | 5 | use libc; 6 | 7 | use ::Result; 8 | 9 | use bluez::constants::*; 10 | use bluez::util::handle_error; 11 | 12 | use std::fmt; 13 | use std::fmt::{Debug, Formatter}; 14 | use std::sync::mpsc::{channel, Sender, Receiver}; 15 | use std::sync::atomic::{AtomicBool, Ordering}; 16 | use std::sync::Mutex; 17 | use bluez::protocol::hci::ACLData; 18 | use bluez::protocol::att; 19 | 20 | use self::StreamMessage::*; 21 | use api::BDAddr; 22 | use Error; 23 | use api::CommandCallback; 24 | use api::RequestCallback; 25 | use bluez::adapter::Adapter; 26 | use bytes::BytesMut; 27 | use bytes::BufMut; 28 | use api::NotificationHandler; 29 | 30 | enum StreamMessage { 31 | Command(Vec, Option), 32 | Request(Vec, Option), 33 | Data(Vec), 34 | } 35 | 36 | impl Debug for StreamMessage { 37 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 38 | match self { 39 | &Command(ref data, ref _cb) => write!(f, "Command({:?})", data), 40 | &Request(ref data, ref cb) => write!(f, "Request({:?}, cb: {})", data, cb.is_some()), 41 | &Data(ref data) => write!(f, "Data({:?})", data), 42 | } 43 | } 44 | } 45 | 46 | #[derive(Clone)] 47 | pub struct ACLStream { 48 | adapter: Adapter, 49 | pub address: BDAddr, 50 | pub handle: u16, 51 | fd: i32, 52 | should_stop: Arc, 53 | sender: Arc>>, 54 | notification_handlers: Arc>>, 55 | } 56 | 57 | impl ACLStream { 58 | pub fn new(adapter: Adapter, address: BDAddr, handle: u16, fd: i32) -> ACLStream { 59 | info!("Creating new ACLStream for {}, {}, {}", address, handle, fd); 60 | let (tx, rx) = channel(); 61 | let acl_stream = ACLStream { 62 | adapter, 63 | address, 64 | handle, 65 | fd, 66 | should_stop: Arc::new(AtomicBool::new(false)), 67 | sender: Arc::new(Mutex::new(tx)), 68 | notification_handlers: Arc::new(Mutex::new(vec![])), 69 | }; 70 | 71 | { 72 | let should_stop = acl_stream.should_stop.clone(); 73 | let stream = acl_stream.clone(); 74 | thread::spawn(move || { 75 | let mut msg = rx.recv().unwrap(); 76 | while !should_stop.load(Ordering::Relaxed) { 77 | match stream.handle_iteration(&mut msg, &rx) { 78 | Ok(_) => msg = rx.recv().unwrap(), 79 | Err(Error::NotConnected) => { 80 | // retry message 81 | thread::sleep(Duration::from_millis(50)); 82 | continue; 83 | } 84 | Err(e) => { 85 | error!("Unhandled error {}", e); 86 | } 87 | } 88 | } 89 | 90 | if let Err(err) = handle_error(unsafe { libc::close(fd) }) { 91 | warn!("Failed to close socket {}: {}", fd, err); 92 | }; 93 | }); 94 | } 95 | 96 | acl_stream 97 | } 98 | 99 | fn write_socket(&self, value: &mut [u8], command: bool, 100 | receiver: &Receiver) -> Result> { 101 | debug!("writing {:?}", value); 102 | handle_error(unsafe { 103 | libc::write(self.fd, value.as_mut_ptr() as *mut libc::c_void, value.len()) as i32 104 | })?; 105 | 106 | let mut skipped = vec![]; 107 | loop { 108 | let message = receiver.recv().unwrap(); 109 | debug!("waiting for confirmation... {:?}", message); 110 | if let Data(rec) = message { 111 | if rec != value { 112 | skipped.into_iter().for_each(|m| 113 | self.send(m)); 114 | return Ok(rec); 115 | } else if command { 116 | return Ok(vec![]); 117 | } 118 | } else { 119 | skipped.push(message); 120 | } 121 | } 122 | } 123 | 124 | fn handle_iteration(&self, msg: &mut StreamMessage, 125 | receiver: &Receiver) -> Result<()> { 126 | match *msg { 127 | Command(ref mut value, ref handler) => { 128 | debug!("sending command {:?} to {}", value, self.fd); 129 | 130 | let result = self.write_socket(value, true, receiver) 131 | .map(|_v| ()); 132 | if let &Some(ref f) = handler { 133 | f(result); 134 | } 135 | }, 136 | Request(ref mut value, ref handler) => { 137 | debug!("sending request {:?} to {}", value, self.fd); 138 | 139 | let result = self.write_socket(value, false, receiver); 140 | if let &Some(ref f) = handler { 141 | f(result); 142 | } 143 | }, 144 | Data(ref value) => { 145 | debug!("Received data {:?}", value); 146 | } 147 | } 148 | 149 | Ok(()) 150 | } 151 | 152 | fn send(&self, message: StreamMessage) { 153 | let l = self.sender.lock().unwrap(); 154 | l.send(message).unwrap(); 155 | } 156 | 157 | pub fn write(&self, data: &mut [u8], handler: Option) { 158 | // let mut packet = Protocol::acl(self.handle, ATT_CID, data); 159 | self.send(Request(data.to_owned(), handler)); 160 | } 161 | 162 | pub fn write_cmd(&self, data: &mut [u8], on_done: Option) { 163 | self.send(Command(data.to_owned(), on_done)); 164 | } 165 | 166 | pub fn on_notification(&self, handler: NotificationHandler) { 167 | let mut list = self.notification_handlers.lock().unwrap(); 168 | list.push(handler); 169 | } 170 | 171 | pub fn receive(&self, message: &ACLData) { 172 | debug!("receive message: {:?}", message); 173 | // message.data 174 | // TODO: handle partial packets 175 | if message.cid == ATT_CID { 176 | let value = message.data.to_vec(); 177 | if !value.is_empty() { 178 | match value[0] { 179 | ATT_OP_EXCHANGE_MTU_REQ => { 180 | let request = att::mtu_request(&value).unwrap().1; 181 | // is the client MTU smaller than ours? 182 | if request.client_rx_mtu <= self.adapter.info.acl_mtu { 183 | debug!("sending MTU: {}", self.adapter.info.acl_mtu); 184 | // it is, send confirmation 185 | let mut buf = BytesMut::with_capacity(3); 186 | buf.put_u8(ATT_OP_EXCHANGE_MTU_RESP); 187 | buf.put_u16_le(self.adapter.info.acl_mtu); 188 | self.write_cmd(&mut buf, None); 189 | } else { 190 | // TODO: reduce our MTU to client's 191 | error!("client's MTU is larger than ours"); 192 | self.write_cmd(&mut [0x01, 0x02, 0x00, 0x00, 0x06], None); 193 | } 194 | } 195 | ATT_OP_VALUE_NOTIFICATION => { 196 | debug!("value notification: {:?}", value); 197 | match att::value_notification(&value) { 198 | Ok(notification) => { 199 | let handlers = self.notification_handlers.lock().unwrap(); 200 | handlers.iter().for_each(|h| h(notification.1.clone())); 201 | } 202 | Err(err) => { 203 | error!("failed to parse notification: {:?}", err); 204 | } 205 | } 206 | } 207 | _ => { 208 | self.send(Data(value)); 209 | } 210 | } 211 | } 212 | } 213 | } 214 | } 215 | 216 | impl Drop for ACLStream { 217 | fn drop(&mut self) { 218 | self.should_stop.clone().store(true, Ordering::Relaxed); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::{Display, Formatter, Debug}; 3 | 4 | use ::Result; 5 | use std::collections::BTreeSet; 6 | use api::UUID::B16; 7 | use api::UUID::B128; 8 | 9 | #[derive(Debug, Clone, Eq, PartialEq)] 10 | pub enum AddressType { 11 | Random, 12 | Public, 13 | } 14 | 15 | impl Default for AddressType { 16 | fn default() -> Self { AddressType::Public } 17 | } 18 | 19 | impl AddressType { 20 | pub fn from_u8(v: u8) -> Option { 21 | match v { 22 | 0 => Some(AddressType::Public), 23 | 1 => Some(AddressType::Random), 24 | _ => None, 25 | } 26 | } 27 | 28 | pub fn num(&self) -> u8 { 29 | match *self { 30 | AddressType::Public => 0, 31 | AddressType::Random => 1 32 | } 33 | } 34 | } 35 | 36 | /// Stores the 6 byte address used to identify Bluetooth devices. 37 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Default)] 38 | #[repr(C)] 39 | pub struct BDAddr { 40 | pub address: [ u8 ; 6usize ] 41 | } 42 | 43 | impl Display for BDAddr { 44 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 45 | let a = self.address; 46 | write!(f, "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", 47 | a[5], a[4], a[3], a[2], a[1], a[0]) 48 | } 49 | } 50 | 51 | impl Debug for BDAddr { 52 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 53 | (self as &Display).fmt(f) 54 | } 55 | } 56 | 57 | /// A notification sent from a peripheral due to a change in a value. 58 | #[derive(Clone, Debug, Eq, PartialEq)] 59 | pub struct ValueNotification { 60 | /// The handle that has changed. 61 | pub handle: u16, 62 | /// The new value of the handle. 63 | pub value: Vec, 64 | } 65 | 66 | pub type Callback = Box) + Send>; 67 | pub type CommandCallback = Callback<()>; 68 | pub type RequestCallback = Callback>; 69 | 70 | pub type NotificationHandler = Box; 71 | 72 | /// A Bluetooth UUID. These can either be 2 bytes or 16 bytes long. UUIDs uniquely identify various 73 | /// objects in the Bluetooth universe. 74 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)] 75 | pub enum UUID { 76 | B16(u16), 77 | B128([u8; 16]), 78 | } 79 | 80 | impl UUID { 81 | pub fn size(&self) -> usize { 82 | match *self { 83 | B16(_) => 2, 84 | B128(_) => 16, 85 | } 86 | } 87 | } 88 | 89 | impl Display for UUID { 90 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 91 | match *self { 92 | B16(u) => write!(f, "{:02X}:{:02X}", u >> 8, u & 0xFF), 93 | B128(a) => { 94 | for i in (1..a.len()).rev() { 95 | write!(f, "{:02X}:", a[i])?; 96 | } 97 | write!(f, "{:02X}", a[0]) 98 | } 99 | } 100 | } 101 | } 102 | 103 | impl Debug for UUID { 104 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 105 | (self as &Display).fmt(f) 106 | } 107 | } 108 | 109 | bitflags! { 110 | /// A set of properties that indicate what operations are supported by a Characteristic. 111 | pub struct CharPropFlags: u8 { 112 | const BROADCAST = 0x01; 113 | const READ = 0x02; 114 | const WRITE_WITHOUT_RESPONSE = 0x04; 115 | const WRITE = 0x08; 116 | const NOTIFY = 0x10; 117 | const INDICATE = 0x20; 118 | const AUTHENTICATED_SIGNED_WRITES = 0x40; 119 | const EXTENDED_PROPERTIES = 0x80; 120 | } 121 | } 122 | 123 | /// A Bluetooth characteristic. Characteristics are the main way you will interact with other 124 | /// bluetooth devices. Characteristics are identified by a UUID which may be standardized 125 | /// (like 0x2803, which identifies a characteristic for reading heart rate measurements) but more 126 | /// often are specific to a particular device. The standard set of characteristics can be found 127 | /// [here](https://www.bluetooth.com/specifications/gatt/characteristics). 128 | /// 129 | /// A characteristic may be interacted with in various ways depending on its properties. You may be 130 | /// able to write to it, read from it, set its notify or indicate status, or send a command to it. 131 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 132 | pub struct Characteristic { 133 | /// The start of the handle range that contains this characteristic. 134 | pub start_handle: u16, 135 | /// The end of the handle range that contains this characteristic. 136 | pub end_handle: u16, 137 | /// The value handle of the characteristic. 138 | pub value_handle: u16, 139 | /// The UUID for this characteristic. This uniquely identifies its behavior. 140 | pub uuid: UUID, 141 | /// The set of properties for this characteristic, which indicate what functionality it 142 | /// supports. If you attempt an operation that is not supported by the characteristics (for 143 | /// example setting notify on one without the NOTIFY flag), that operation will fail. 144 | pub properties: CharPropFlags, 145 | } 146 | 147 | impl Display for Characteristic { 148 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 149 | write!(f, "handle: 0x{:04X}, char properties: 0x{:02X}, \ 150 | char value handle: 0x{:04X}, end handle: 0x{:04X}, uuid: {:?}", 151 | self.start_handle, self.properties, 152 | self.value_handle, self.end_handle, self.uuid) 153 | } 154 | } 155 | 156 | /// The properties of this peripheral, as determined by the advertising reports we've received for 157 | /// it. 158 | #[derive(Debug, Default, Clone)] 159 | pub struct PeripheralProperties { 160 | /// The address of this peripheral 161 | pub address: BDAddr, 162 | /// The type of address (either random or public) 163 | pub address_type: AddressType, 164 | /// The local name. This is generally a human-readable string that identifies the type of device. 165 | pub local_name: Option, 166 | /// The transmission power level for the device 167 | pub tx_power_level: Option, 168 | /// Unstructured data set by the device manufacturer 169 | pub manufacturer_data: Option>, 170 | /// Number of times we've seen advertising reports for this device 171 | pub discovery_count: u32, 172 | /// True if we've discovered the device before 173 | pub has_scan_response: bool, 174 | } 175 | 176 | /// Peripheral is the device that you would like to communicate with (the "server" of BLE). This 177 | /// struct contains both the current state of the device (its properties, characteristics, etc.) 178 | /// as well as functions for communication. 179 | pub trait Peripheral: Send + Sync + Clone + Debug { 180 | /// Returns the address of the peripheral. 181 | fn address(&self) -> BDAddr; 182 | 183 | /// Returns the set of properties associated with the peripheral. These may be updated over time 184 | /// as additional advertising reports are received. 185 | fn properties(&self) -> PeripheralProperties; 186 | 187 | /// The set of characteristics we've discovered for this device. This will be empty until 188 | /// `discover_characteristics` or `discover_characteristics_in_range` is called. 189 | fn characteristics(&self) -> BTreeSet; 190 | 191 | /// Returns true iff we are currently connected to the device. 192 | fn is_connected(&self) -> bool; 193 | 194 | /// Creates a connection to the device. This is a synchronous operation; if this method returns 195 | /// Ok there has been successful connection. Note that peripherals allow only one connection at 196 | /// a time. Operations that attempt to communicate with a device will fail until it is connected. 197 | fn connect(&self) -> Result<()>; 198 | 199 | /// Terminates a connection to the device. This is a synchronous operation. 200 | fn disconnect(&self) -> Result<()>; 201 | 202 | /// Discovers all characteristics for the device. This is a synchronous operation. 203 | fn discover_characteristics(&self) -> Result>; 204 | 205 | /// Discovers characteristics within the specified range of handles. This is a synchronous 206 | /// operation. 207 | fn discover_characteristics_in_range(&self, start: u16, end: u16) -> Result>; 208 | 209 | /// Sends a command (`write-without-response`) to the characteristic. Takes an optional callback 210 | /// that will be notified in case of error or when the command has been successfully acked by the 211 | /// device. 212 | fn command_async(&self, characteristic: &Characteristic, data: &[u8], handler: Option); 213 | 214 | /// Sends a command (write without response) to the characteristic. Synchronously returns a 215 | /// `Result` with an error set if the command was not accepted by the device. 216 | fn command(&self, characteristic: &Characteristic, data: &[u8]) -> Result<()>; 217 | 218 | /// Sends a request (write) to the device. Takes an optional callback with either an error if 219 | /// the request was not accepted or the response from the device. 220 | fn request_async(&self, characteristic: &Characteristic, 221 | data: &[u8], handler: Option); 222 | 223 | /// Sends a request (write) to the device. Synchronously returns either an error if the request 224 | /// was not accepted or the response from the device. 225 | fn request(&self, characteristic: &Characteristic, 226 | data: &[u8]) -> Result>; 227 | 228 | /// Sends a request (read) to the device. Takes an optional callback with either an error if 229 | /// the request was not accepted or the response from the device. 230 | fn read_async(&self, characteristic: &Characteristic, handler: Option); 231 | 232 | /// Sends a request (read) to the device. Synchronously returns either an error if the request 233 | /// was not accepted or the response from the device. 234 | fn read(&self, characteristic: &Characteristic) -> Result>; 235 | 236 | /// Sends a read-by-type request to device for the range of handles covered by the 237 | /// characteristic and for the specified declaration UUID. See 238 | /// [here](https://www.bluetooth.com/specifications/gatt/declarations) for valid UUIDs. 239 | /// Takes an optional callback that will be called with an error or the device response. 240 | fn read_by_type_async(&self, characteristic: &Characteristic, 241 | uuid: UUID, handler: Option); 242 | 243 | /// Sends a read-by-type request to device for the range of handles covered by the 244 | /// characteristic and for the specified declaration UUID. See 245 | /// [here](https://www.bluetooth.com/specifications/gatt/declarations) for valid UUIDs. 246 | /// Synchronously returns either an error or the device response. 247 | fn read_by_type(&self, characteristic: &Characteristic, 248 | uuid: UUID) -> Result>; 249 | 250 | /// Enables either notify or indicate (depending on support) for the specified characteristic. 251 | /// This is a synchronous call. 252 | fn subscribe(&self, characteristic: &Characteristic) -> Result<()>; 253 | 254 | /// Disables either notify or indicate (depending on support) for the specified characteristic. 255 | /// This is a synchronous call. 256 | fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()>; 257 | 258 | /// Registers a handler that will be called when value notification messages are received from 259 | /// the device. This method should only be used after a connection has been established. Note 260 | /// that the handler will be called in a common thread, so it should not block. 261 | fn on_notification(&self, handler: NotificationHandler); 262 | } 263 | 264 | #[derive(Debug, Copy, Clone)] 265 | pub enum CentralEvent { 266 | DeviceDiscovered(BDAddr), 267 | DeviceLost(BDAddr), 268 | DeviceUpdated(BDAddr), 269 | DeviceConnected(BDAddr), 270 | DeviceDisconnected(BDAddr), 271 | } 272 | 273 | pub type EventHandler = Box; 274 | 275 | /// Central is the "client" of BLE. It's able to scan for and establish connections to peripherals. 276 | pub trait Central

: Send + Sync + Clone { 277 | /// Registers a function that will receive notifications when events occur for this Central 278 | /// module. See [`Event`](enum.CentralEvent.html) for the full set of events. Note that the 279 | /// handler will be called in a common thread, so it should not block. 280 | fn on_event(&self, handler: EventHandler); 281 | 282 | /// Starts a scan for BLE devices. This scan will generally continue until explicitly stopped, 283 | /// although this may depend on your bluetooth adapter. Discovered devices will be announced 284 | /// to subscribers of `on_event` and will be available via `peripherals()`. 285 | fn start_scan(&self) -> Result<()>; 286 | 287 | /// Control whether to use active or passive scan mode to find BLE devices. Active mode scan 288 | /// notifies advertises about the scan, whereas passive scan only receives data from the 289 | /// advertiser. Defaults to use active mode. 290 | fn active(&self, enabled: bool); 291 | 292 | /// Control whether to filter multiple advertisements by the same peer device. Receving 293 | // can be useful for some applications. E.g. when using scan to collect information from 294 | /// beacons that update data frequently. Defaults to filter duplicate advertisements. 295 | fn filter_duplicates(&self, enabled: bool); 296 | 297 | /// Stops scanning for BLE devices. 298 | fn stop_scan(&self) -> Result<()>; 299 | 300 | /// Returns the list of [`Peripherals`](trait.Peripheral.html) that have been discovered so far. 301 | /// Note that this list may contain peripherals that are no longer available. 302 | fn peripherals(&self) -> Vec

; 303 | 304 | /// Returns a particular [`Peripheral`](trait.Peripheral.html) by its address if it has been 305 | /// discovered. 306 | fn peripheral(&self, address: BDAddr) -> Option

; 307 | } 308 | -------------------------------------------------------------------------------- /src/bluez/adapter/mod.rs: -------------------------------------------------------------------------------- 1 | mod acl_stream; 2 | mod peripheral; 3 | 4 | use libc; 5 | use std; 6 | use std::ffi::CStr; 7 | use nom; 8 | use bytes::{BytesMut, BufMut}; 9 | 10 | use std::collections::{HashSet, HashMap}; 11 | use std::sync::{Arc, Mutex}; 12 | use std::sync::atomic::{AtomicBool, Ordering}; 13 | use std::thread; 14 | 15 | use ::Result; 16 | use api::{CentralEvent, BDAddr, Central}; 17 | 18 | use bluez::util::handle_error; 19 | use bluez::protocol::hci; 20 | use bluez::adapter::peripheral::Peripheral; 21 | use bluez::constants::*; 22 | use bluez::ioctl; 23 | use api::EventHandler; 24 | 25 | 26 | #[derive(Copy, Debug)] 27 | #[repr(C)] 28 | pub struct HCIDevStats { 29 | pub err_rx : u32, 30 | pub err_tx : u32, 31 | pub cmd_tx : u32, 32 | pub evt_rx : u32, 33 | pub acl_tx : u32, 34 | pub acl_rx : u32, 35 | pub sco_tx : u32, 36 | pub sco_rx : u32, 37 | pub byte_rx : u32, 38 | pub byte_tx : u32, 39 | } 40 | 41 | impl Clone for HCIDevStats{ 42 | fn clone(&self) -> Self { *self } 43 | } 44 | 45 | impl HCIDevStats { 46 | fn default() -> HCIDevStats { 47 | HCIDevStats { 48 | err_rx: 0u32, 49 | err_tx: 0u32, 50 | cmd_tx: 0u32, 51 | evt_rx: 0u32, 52 | acl_tx: 0u32, 53 | acl_rx: 0u32, 54 | sco_tx: 0u32, 55 | sco_rx: 0u32, 56 | byte_rx: 0u32, 57 | byte_tx: 0u32 58 | } 59 | } 60 | } 61 | 62 | #[derive(Copy, Debug)] 63 | #[repr(C)] 64 | pub struct HCIDevInfo { 65 | pub dev_id : u16, 66 | pub name : [libc::c_char; 8], 67 | pub bdaddr : BDAddr, 68 | pub flags : u32, 69 | pub type_ : u8, 70 | pub features : [u8; 8], 71 | pub pkt_type : u32, 72 | pub link_policy : u32, 73 | pub link_mode : u32, 74 | pub acl_mtu : u16, 75 | pub acl_pkts : u16, 76 | pub sco_mtu : u16, 77 | pub sco_pkts : u16, 78 | pub stat : HCIDevStats, 79 | } 80 | 81 | impl Clone for HCIDevInfo { 82 | fn clone(&self) -> Self { *self } 83 | } 84 | 85 | impl HCIDevInfo { 86 | pub fn default() -> HCIDevInfo { 87 | HCIDevInfo { 88 | dev_id: 0, 89 | name: [0 as libc::c_char; 8], 90 | bdaddr: BDAddr { address: [0u8; 6] }, 91 | flags: 0u32, 92 | type_: 0u8, 93 | features: [0u8; 8], 94 | pkt_type: 0u32, 95 | link_policy: 0u32, 96 | link_mode: 0u32, 97 | acl_mtu: 0u16, 98 | acl_pkts: 0u16, 99 | sco_mtu: 0u16, 100 | sco_pkts: 0u16, 101 | stat: HCIDevStats::default() 102 | } 103 | } 104 | } 105 | 106 | #[derive(Copy, Debug)] 107 | #[repr(C)] 108 | struct SockaddrHCI { 109 | hci_family: libc::sa_family_t, 110 | hci_dev: u16, 111 | hci_channel: u16, 112 | } 113 | 114 | impl Clone for SockaddrHCI { 115 | fn clone(&self) -> Self { *self } 116 | } 117 | 118 | 119 | #[derive(Debug, Copy, Clone)] 120 | pub enum AdapterType { 121 | BrEdr, 122 | Amp, 123 | Unknown(u8) 124 | } 125 | 126 | impl AdapterType { 127 | fn parse(typ: u8) -> AdapterType { 128 | match typ { 129 | 0 => AdapterType::BrEdr, 130 | 1 => AdapterType::Amp, 131 | x => AdapterType::Unknown(x), 132 | } 133 | } 134 | 135 | fn num(&self) -> u8 { 136 | match *self { 137 | AdapterType::BrEdr => 0, 138 | AdapterType::Amp => 1, 139 | AdapterType::Unknown(x) => x, 140 | } 141 | } 142 | } 143 | 144 | #[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)] 145 | pub enum AdapterState { 146 | Up, Init, Running, Raw, PScan, IScan, Inquiry, Auth, Encrypt 147 | } 148 | 149 | impl AdapterState { 150 | fn parse(flags: u32) -> HashSet { 151 | use self::AdapterState::*; 152 | 153 | let states = [Up, Init, Running, Raw, PScan, IScan, Inquiry, Auth, Encrypt]; 154 | 155 | let mut set = HashSet::new(); 156 | for (i, f) in states.iter().enumerate() { 157 | if flags & (1 << (i & 31)) != 0 { 158 | set.insert(f.clone()); 159 | } 160 | } 161 | 162 | set 163 | } 164 | } 165 | 166 | /// The [`Central`](../../api/trait.Central.html) implementation for BlueZ. 167 | #[derive(Clone)] 168 | pub struct ConnectedAdapter { 169 | pub adapter: Adapter, 170 | adapter_fd: i32, 171 | should_stop: Arc, 172 | pub scan_enabled: Arc, 173 | pub active: Arc, 174 | pub filter_duplicates: Arc, 175 | peripherals: Arc>>, 176 | handle_map: Arc>>, 177 | event_handlers: Arc>>, 178 | } 179 | 180 | impl ConnectedAdapter { 181 | pub fn new(adapter: &Adapter) -> Result { 182 | let adapter_fd = handle_error(unsafe { 183 | libc::socket(libc::AF_BLUETOOTH, libc::SOCK_RAW | libc::SOCK_CLOEXEC, 1) 184 | })?; 185 | 186 | let addr = SockaddrHCI { 187 | hci_family: libc::AF_BLUETOOTH as u16, 188 | hci_dev: adapter.dev_id, 189 | hci_channel: 0, 190 | }; 191 | 192 | handle_error(unsafe { 193 | libc::bind(adapter_fd, &addr as *const SockaddrHCI as *const libc::sockaddr, 194 | std::mem::size_of::() as u32) 195 | })?; 196 | 197 | let should_stop = Arc::new(AtomicBool::new(false)); 198 | 199 | let connected = ConnectedAdapter { 200 | adapter: adapter.clone(), 201 | adapter_fd, 202 | active: Arc::new(AtomicBool::new(false)), 203 | filter_duplicates: Arc::new(AtomicBool::new(false)), 204 | should_stop, 205 | scan_enabled: Arc::new(AtomicBool::new(false)), 206 | event_handlers: Arc::new(Mutex::new(vec![])), 207 | peripherals: Arc::new(Mutex::new(HashMap::new())), 208 | handle_map: Arc::new(Mutex::new(HashMap::new())), 209 | }; 210 | 211 | connected.add_raw_socket_reader(adapter_fd); 212 | 213 | connected.set_socket_filter()?; 214 | 215 | Ok(connected) 216 | } 217 | 218 | fn set_socket_filter(&self) -> Result<()> { 219 | let mut filter = BytesMut::with_capacity(14); 220 | let type_mask = (1 << HCI_COMMAND_PKT) | (1 << HCI_EVENT_PKT) | (1 << HCI_ACLDATA_PKT); 221 | let event_mask1 = (1 << EVT_DISCONN_COMPLETE) | (1 << EVT_ENCRYPT_CHANGE) | 222 | (1 << EVT_CMD_COMPLETE) | (1 << EVT_CMD_STATUS); 223 | let event_mask2 = 1 << (EVT_LE_META_EVENT - 32); 224 | let opcode = 0; 225 | 226 | filter.put_u32_le(type_mask); 227 | filter.put_u32_le(event_mask1); 228 | filter.put_u32_le(event_mask2); 229 | filter.put_u16_le(opcode); 230 | 231 | handle_error(unsafe { 232 | libc::setsockopt(self.adapter_fd, SOL_HCI, HCI_FILTER, 233 | filter.as_mut_ptr() as *mut _ as *mut libc::c_void, 234 | filter.len() as u32) 235 | })?; 236 | Ok(()) 237 | } 238 | 239 | fn add_raw_socket_reader(&self, fd: i32) { 240 | let should_stop = self.should_stop.clone(); 241 | let connected = self.clone(); 242 | 243 | thread::spawn(move || { 244 | let mut buf = [0u8; 2048]; 245 | let mut cur: Vec = vec![]; 246 | 247 | while !should_stop.load(Ordering::Relaxed) { 248 | // debug!("reading"); 249 | let len = handle_error(unsafe { 250 | libc::read(fd, buf.as_mut_ptr() as *mut _ as *mut libc::c_void, buf.len()) as i32 251 | }).unwrap_or(0) as usize; 252 | if len == 0 { 253 | continue; 254 | } 255 | 256 | cur.put_slice(&buf[0..len]); 257 | 258 | let mut new_cur: Option> = Some(vec![]); 259 | { 260 | let result = { 261 | hci::message(&cur) 262 | }; 263 | 264 | match result { 265 | Ok((left, result)) => { 266 | ConnectedAdapter::handle(&connected, result); 267 | if !left.is_empty() { 268 | new_cur = Some(left.to_owned()); 269 | }; 270 | } 271 | Err(nom::Err::Incomplete(_)) => { 272 | new_cur = None; 273 | }, 274 | Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => { 275 | error!("parse error {:?}\nfrom: {:?}", err, cur); 276 | } 277 | } 278 | }; 279 | 280 | cur = new_cur.unwrap_or(cur); 281 | } 282 | }); 283 | } 284 | 285 | fn emit(&self, event: CentralEvent) { 286 | debug!("emitted {:?}", event); 287 | let handlers = self.event_handlers.clone(); 288 | let vec = handlers.lock().unwrap(); 289 | for handler in (*vec).iter() { 290 | handler(event.clone()); 291 | } 292 | } 293 | 294 | fn handle(&self, message: hci::Message) { 295 | debug!("got message {:?}", message); 296 | 297 | match message { 298 | hci::Message::LEAdvertisingReport(info) => { 299 | let mut new = false; 300 | let address = info.bdaddr.clone(); 301 | 302 | { 303 | let mut peripherals = self.peripherals.lock().unwrap(); 304 | let peripheral = peripherals.entry(info.bdaddr) 305 | .or_insert_with(|| { 306 | new = true; 307 | Peripheral::new(self.clone(), info.bdaddr) 308 | }); 309 | 310 | 311 | peripheral.handle_device_message(&hci::Message::LEAdvertisingReport(info)); 312 | } 313 | 314 | if new { 315 | self.emit(CentralEvent::DeviceDiscovered(address.clone())) 316 | } else { 317 | self.emit(CentralEvent::DeviceUpdated(address.clone())) 318 | } 319 | } 320 | hci::Message::LEConnComplete(info) => { 321 | info!("connected to {:?}", info); 322 | let address = info.bdaddr.clone(); 323 | let handle = info.handle.clone(); 324 | match self.peripheral(address) { 325 | Some(peripheral) => { 326 | peripheral.handle_device_message(&hci::Message::LEConnComplete(info)) 327 | } 328 | // todo: there's probably a better way to handle this case 329 | None => warn!("Got connection for unknown device {}", info.bdaddr) 330 | } 331 | 332 | let mut handles = self.handle_map.lock().unwrap(); 333 | handles.insert(handle, address); 334 | 335 | self.emit(CentralEvent::DeviceConnected(address)); 336 | } 337 | hci::Message::ACLDataPacket(data) => { 338 | let message = hci::Message::ACLDataPacket(data); 339 | 340 | // TODO this is a bit risky from a deadlock perspective (note mutexes are not 341 | // reentrant in rust!) 342 | let peripherals = self.peripherals.lock().unwrap(); 343 | 344 | for peripheral in peripherals.values() { 345 | // we don't know the handler => device mapping, so send to all and let them filter 346 | peripheral.handle_device_message(&message); 347 | } 348 | }, 349 | hci::Message::DisconnectComplete { handle, .. } => { 350 | let mut handles = self.handle_map.lock().unwrap(); 351 | match handles.remove(&handle) { 352 | Some(addr) => { 353 | match self.peripheral(addr) { 354 | Some(peripheral) => peripheral.handle_device_message(&message), 355 | None => warn!("got disconnect for unknown device {}", addr), 356 | }; 357 | self.emit(CentralEvent::DeviceDisconnected(addr)); 358 | } 359 | None => { 360 | warn!("got disconnect for unknown handle {}", handle); 361 | } 362 | } 363 | } 364 | _ => { 365 | // skip 366 | } 367 | } 368 | } 369 | 370 | fn write(&self, message: &mut [u8]) -> Result<()> { 371 | debug!("writing({}) {:?}", self.adapter_fd, message); 372 | let ptr = message.as_mut_ptr(); 373 | handle_error(unsafe { 374 | libc::write(self.adapter_fd, ptr as *mut _ as *mut libc::c_void, message.len()) as i32 375 | })?; 376 | Ok(()) 377 | } 378 | 379 | fn set_scan_params(&self) -> Result<()> { 380 | let mut data = BytesMut::with_capacity(7); 381 | data.put_u8(if self.active.load(Ordering::Relaxed) { 1 } else { 0 }); // scan_type = active or passive 382 | data.put_u16_le(0x0010); // interval ms 383 | data.put_u16_le(0x0010); // window ms 384 | data.put_u8(0); // own_type = public 385 | data.put_u8(0); // filter_policy = public 386 | let mut buf = hci::hci_command(LE_SET_SCAN_PARAMETERS_CMD, &*data); 387 | self.write(&mut *buf) 388 | } 389 | 390 | fn set_scan_enabled(&self, enabled: bool) -> Result<()> { 391 | let mut data = BytesMut::with_capacity(2); 392 | data.put_u8(if enabled { 1 } else { 0 }); // enabled 393 | data.put_u8(if self.filter_duplicates.load(Ordering::Relaxed) { 1 } else { 0 }); // filter duplicates 394 | 395 | self.scan_enabled.clone().store(enabled, Ordering::Relaxed); 396 | let mut buf = hci::hci_command(LE_SET_SCAN_ENABLE_CMD, &*data); 397 | self.write(&mut *buf) 398 | } 399 | } 400 | 401 | impl Central for ConnectedAdapter { 402 | fn on_event(&self, handler: EventHandler) { 403 | let list = self.event_handlers.clone(); 404 | list.lock().unwrap().push(handler); 405 | } 406 | 407 | fn active(&self, enabled: bool) { 408 | self.active.clone().store(enabled, Ordering::Relaxed); 409 | } 410 | 411 | fn filter_duplicates(&self, enabled: bool) { 412 | self.filter_duplicates.clone().store(enabled, Ordering::Relaxed); 413 | } 414 | 415 | fn start_scan(&self) -> Result<()> { 416 | self.set_scan_params()?; 417 | self.set_scan_enabled(true) 418 | } 419 | 420 | fn stop_scan(&self) -> Result<()> { 421 | self.set_scan_enabled(false) 422 | } 423 | 424 | fn peripherals(&self) -> Vec { 425 | let l = self.peripherals.lock().unwrap(); 426 | l.values().map(|p| p.clone()).collect() 427 | } 428 | 429 | fn peripheral(&self, address: BDAddr) -> Option { 430 | let l = self.peripherals.lock().unwrap(); 431 | l.get(&address).map(|p| p.clone()) 432 | } 433 | } 434 | 435 | /// Adapter represents a physical bluetooth interface in your system, for example a bluetooth 436 | /// dongle. 437 | #[derive(Debug, Clone)] 438 | pub struct Adapter { 439 | /// The name of the adapter. 440 | pub name: String, 441 | 442 | /// The device id of the adapter. 443 | pub dev_id: u16, 444 | 445 | /// The address of the adapter. 446 | pub addr: BDAddr, 447 | 448 | /// The type of the adapater. 449 | pub typ: AdapterType, 450 | 451 | /// The set of states that the adapater is in. 452 | pub states: HashSet, 453 | 454 | /// Properties of the adapter. 455 | pub info: HCIDevInfo, 456 | } 457 | 458 | impl Adapter { 459 | pub fn from_device_info(di: &HCIDevInfo) -> Adapter { 460 | info!("DevInfo: {:?}", di); 461 | Adapter { 462 | name: String::from(unsafe { CStr::from_ptr(di.name.as_ptr()).to_str().unwrap() }), 463 | dev_id: 0, 464 | addr: di.bdaddr, 465 | typ: AdapterType::parse((di.type_ & 0x30) >> 4), 466 | states: AdapterState::parse(di.flags), 467 | info: di.clone(), 468 | } 469 | } 470 | 471 | pub fn from_dev_id(ctl: i32, dev_id: u16) -> Result { 472 | let mut di = HCIDevInfo::default(); 473 | di.dev_id = dev_id; 474 | 475 | unsafe { 476 | ioctl::hci_get_dev_info(ctl, &mut di)?; 477 | } 478 | 479 | Ok(Adapter::from_device_info(&di)) 480 | } 481 | 482 | pub fn is_up(&self) -> bool { 483 | self.states.contains(&AdapterState::Up) 484 | } 485 | 486 | pub fn connect(&self) -> Result { 487 | ConnectedAdapter::new(self) 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "autocfg" 3 | version = "0.1.1" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "backtrace" 8 | version = "0.3.13" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 13 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 14 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "backtrace-sys" 21 | version = "0.1.28" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 26 | ] 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.0.4" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "byteorder" 35 | version = "1.2.7" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | [[package]] 39 | name = "bytes" 40 | version = "0.4.11" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | dependencies = [ 43 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 44 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 45 | ] 46 | 47 | [[package]] 48 | name = "cc" 49 | version = "1.0.28" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "cfg-if" 54 | version = "0.1.6" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "enum_primitive" 59 | version = "0.1.1" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | dependencies = [ 62 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 63 | ] 64 | 65 | [[package]] 66 | name = "failure" 67 | version = "0.1.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | dependencies = [ 70 | "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "failure_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "failure_derive" 76 | version = "0.1.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "fuchsia-zircon" 87 | version = "0.3.3" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | dependencies = [ 90 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "fuchsia-zircon-sys" 96 | version = "0.3.3" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | 99 | [[package]] 100 | name = "iovec" 101 | version = "0.1.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | dependencies = [ 104 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "libc" 110 | version = "0.2.45" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | 113 | [[package]] 114 | name = "log" 115 | version = "0.3.9" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | dependencies = [ 118 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 119 | ] 120 | 121 | [[package]] 122 | name = "log" 123 | version = "0.4.6" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | dependencies = [ 126 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 127 | ] 128 | 129 | [[package]] 130 | name = "memchr" 131 | version = "2.1.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | dependencies = [ 134 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 137 | ] 138 | 139 | [[package]] 140 | name = "nix" 141 | version = "0.12.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | dependencies = [ 144 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 147 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 148 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "nom" 153 | version = "4.1.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | dependencies = [ 156 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 157 | ] 158 | 159 | [[package]] 160 | name = "num" 161 | version = "0.1.42" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | dependencies = [ 164 | "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 167 | "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 170 | ] 171 | 172 | [[package]] 173 | name = "num-bigint" 174 | version = "0.1.44" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | dependencies = [ 177 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 178 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 180 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 181 | ] 182 | 183 | [[package]] 184 | name = "num-complex" 185 | version = "0.1.43" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | dependencies = [ 188 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 190 | ] 191 | 192 | [[package]] 193 | name = "num-integer" 194 | version = "0.1.39" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "num-iter" 202 | version = "0.1.37" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | dependencies = [ 205 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 207 | ] 208 | 209 | [[package]] 210 | name = "num-rational" 211 | version = "0.1.42" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "num-traits" 222 | version = "0.1.43" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | dependencies = [ 225 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 226 | ] 227 | 228 | [[package]] 229 | name = "num-traits" 230 | version = "0.2.6" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | 233 | [[package]] 234 | name = "proc-macro2" 235 | version = "0.4.24" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | dependencies = [ 238 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 239 | ] 240 | 241 | [[package]] 242 | name = "quote" 243 | version = "0.6.10" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | dependencies = [ 246 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 247 | ] 248 | 249 | [[package]] 250 | name = "rand" 251 | version = "0.4.3" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | dependencies = [ 254 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 255 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 257 | ] 258 | 259 | [[package]] 260 | name = "rumble" 261 | version = "0.3.0" 262 | dependencies = [ 263 | "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 267 | "failure 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "failure_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 275 | ] 276 | 277 | [[package]] 278 | name = "rustc-demangle" 279 | version = "0.1.11" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | 282 | [[package]] 283 | name = "rustc-serialize" 284 | version = "0.3.24" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | 287 | [[package]] 288 | name = "syn" 289 | version = "0.15.23" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 295 | ] 296 | 297 | [[package]] 298 | name = "synstructure" 299 | version = "0.10.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | dependencies = [ 302 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 306 | ] 307 | 308 | [[package]] 309 | name = "unicode-xid" 310 | version = "0.1.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | 313 | [[package]] 314 | name = "version_check" 315 | version = "0.1.5" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | 318 | [[package]] 319 | name = "void" 320 | version = "1.0.2" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | 323 | [[package]] 324 | name = "winapi" 325 | version = "0.2.8" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | 328 | [[package]] 329 | name = "winapi" 330 | version = "0.3.6" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | dependencies = [ 333 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 335 | ] 336 | 337 | [[package]] 338 | name = "winapi-i686-pc-windows-gnu" 339 | version = "0.4.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | 342 | [[package]] 343 | name = "winapi-x86_64-pc-windows-gnu" 344 | version = "0.4.0" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | 347 | [metadata] 348 | "checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" 349 | "checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" 350 | "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" 351 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 352 | "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 353 | "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" 354 | "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" 355 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" 356 | "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" 357 | "checksum failure 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e945b93ec214c6e97b520ec6c5d80267fc97af327658ee5b9f35984626e51fbf" 358 | "checksum failure_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c395a14ab27b42704e85bf2435c5c51f334ad7a96e16fe23c6e63a1cad6cc12" 359 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 360 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 361 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 362 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" 363 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 364 | "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" 365 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" 366 | "checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f" 367 | "checksum nom 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" 368 | "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" 369 | "checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" 370 | "checksum num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" 371 | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" 372 | "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" 373 | "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" 374 | "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 375 | "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" 376 | "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 377 | "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 378 | "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" 379 | "checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" 380 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 381 | "checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" 382 | "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 383 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 384 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 385 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 386 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 387 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 388 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 389 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 390 | -------------------------------------------------------------------------------- /src/bluez/adapter/peripheral.rs: -------------------------------------------------------------------------------- 1 | use ::Result; 2 | 3 | use api::{Characteristic, CharPropFlags, Callback, PeripheralProperties, BDAddr, Central, 4 | Peripheral as ApiPeripheral}; 5 | use std::mem::size_of; 6 | use std::collections::BTreeSet; 7 | use std::sync::Arc; 8 | use std::sync::Mutex; 9 | use std::sync::atomic::Ordering; 10 | 11 | use libc; 12 | 13 | use bluez::adapter::acl_stream::{ACLStream}; 14 | use bluez::adapter::ConnectedAdapter; 15 | use bluez::util::handle_error; 16 | use bluez::constants::*; 17 | use ::Error; 18 | use bluez::protocol::hci; 19 | use api::AddressType; 20 | use std::sync::mpsc; 21 | use std::sync::mpsc::Sender; 22 | use std::sync::mpsc::Receiver; 23 | use std::sync::mpsc::channel; 24 | use std::time::Duration; 25 | use bytes::{BytesMut, BufMut}; 26 | use bluez::protocol::att; 27 | use std::fmt::Debug; 28 | use std::fmt::Formatter; 29 | use std::fmt; 30 | use std::sync::RwLock; 31 | use std::collections::VecDeque; 32 | use bluez::protocol::hci::ACLData; 33 | use std::sync::Condvar; 34 | use api::RequestCallback; 35 | use api::CommandCallback; 36 | use api::UUID; 37 | use api::UUID::B16; 38 | use api::NotificationHandler; 39 | use std::fmt::Display; 40 | 41 | #[derive(Copy, Debug)] 42 | #[repr(C)] 43 | pub struct SockaddrL2 { 44 | l2_family: libc::sa_family_t, 45 | l2_psm: u16, 46 | l2_bdaddr: BDAddr, 47 | l2_cid: u16, 48 | l2_bdaddr_type: u8, 49 | } 50 | impl Clone for SockaddrL2 { 51 | fn clone(&self) -> Self { *self } 52 | } 53 | 54 | #[derive(Copy, Debug, Default)] 55 | #[repr(C)] 56 | struct L2CapOptions { 57 | omtu: u16, 58 | imtu: u16, 59 | flush_to: u16, 60 | mode: u8, 61 | fcs : u8, 62 | max_tx: u8, 63 | txwin_size: u16, 64 | } 65 | impl Clone for L2CapOptions { 66 | fn clone(&self) -> Self { *self } 67 | } 68 | 69 | #[derive(Clone)] 70 | pub struct Peripheral { 71 | c_adapter: ConnectedAdapter, 72 | address: BDAddr, 73 | properties: Arc>, 74 | characteristics: Arc>>, 75 | stream: Arc>>, 76 | connection_tx: Arc>>, 77 | connection_rx: Arc>>, 78 | message_queue: Arc>>, 79 | } 80 | 81 | impl Display for Peripheral { 82 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 83 | let connected = if self.is_connected() { " connected" } else { "" }; 84 | let properties = self.properties.lock().unwrap(); 85 | write!(f, "{} {}{}", self.address, properties.local_name.clone() 86 | .unwrap_or("(unknown)".to_string()), connected) 87 | } 88 | } 89 | 90 | impl Debug for Peripheral { 91 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 92 | let connected = if self.is_connected() { " connected" } else { "" }; 93 | let properties = self.properties.lock().unwrap(); 94 | let characteristics = self.characteristics.lock().unwrap(); 95 | write!(f, "{} properties: {:?}, characteristics: {:?} {}", self.address, *properties, 96 | *characteristics, connected) 97 | } 98 | } 99 | 100 | impl Peripheral { 101 | pub fn new(c_adapter: ConnectedAdapter, address: BDAddr) -> Peripheral { 102 | let (connection_tx, connection_rx) = channel(); 103 | Peripheral { 104 | c_adapter, address, 105 | properties: Arc::new(Mutex::new(PeripheralProperties::default())), 106 | characteristics: Arc::new(Mutex::new(BTreeSet::new())), 107 | stream: Arc::new(RwLock::new(Option::None)), 108 | connection_tx: Arc::new(Mutex::new(connection_tx)), 109 | connection_rx: Arc::new(Mutex::new(connection_rx)), 110 | message_queue: Arc::new(Mutex::new(VecDeque::new())), 111 | } 112 | } 113 | 114 | pub fn handle_device_message(&self, message: &hci::Message) { 115 | match message { 116 | &hci::Message::LEAdvertisingReport(ref info) => { 117 | assert_eq!(self.address, info.bdaddr, "received message for wrong device"); 118 | use bluez::protocol::hci::LEAdvertisingData::*; 119 | 120 | let mut properties = self.properties.lock().unwrap(); 121 | 122 | properties.discovery_count += 1; 123 | properties.address_type = if info.bdaddr_type == 1 { 124 | AddressType::Random 125 | } else { 126 | AddressType::Public 127 | }; 128 | 129 | properties.address = info.bdaddr; 130 | 131 | if info.evt_type == 4 { 132 | // discover event 133 | properties.has_scan_response = true; 134 | } else { 135 | // TODO: reset service data 136 | } 137 | 138 | for datum in info.data.iter() { 139 | match datum { 140 | &LocalName(ref name) => { 141 | properties.local_name = Some(name.clone()); 142 | } 143 | &TxPowerLevel(ref power) => { 144 | properties.tx_power_level = Some(power.clone()); 145 | } 146 | &ManufacturerSpecific(ref data) => { 147 | properties.manufacturer_data = Some(data.clone()); 148 | } 149 | _ => { 150 | // skip for now 151 | } 152 | } 153 | } 154 | } 155 | &hci::Message::LEConnComplete(ref info) => { 156 | assert_eq!(self.address, info.bdaddr, "received message for wrong device"); 157 | 158 | debug!("got le conn complete {:?}", info); 159 | self.connection_tx.lock().unwrap().send(info.handle.clone()).unwrap(); 160 | } 161 | &hci::Message::ACLDataPacket(ref data) => { 162 | let handle = data.handle.clone(); 163 | match self.stream.try_read() { 164 | Ok(stream) => { 165 | stream.iter().for_each(|stream| { 166 | if stream.handle == handle { 167 | debug!("got data packet for {}: {:?}", self.address, data); 168 | stream.receive(data); 169 | } 170 | }); 171 | } 172 | Err(_e) => { 173 | // we can't access the stream right now because we're still connecting, so 174 | // we'll push the message onto a queue for now 175 | let mut queue = self.message_queue.lock().unwrap(); 176 | queue.push_back(data.clone()); 177 | } 178 | } 179 | }, 180 | &hci::Message::DisconnectComplete {..} => { 181 | // destroy our stream 182 | debug!("removing stream for {} due to disconnect", self.address); 183 | let mut stream = self.stream.write().unwrap(); 184 | *stream = None; 185 | // TODO clean up our sockets 186 | }, 187 | msg => { 188 | debug!("ignored message {:?}", msg); 189 | } 190 | } 191 | } 192 | 193 | fn request_raw_async(&self, data: &mut[u8], handler: Option) { 194 | let l = self.stream.read().unwrap(); 195 | match l.as_ref().ok_or(Error::NotConnected) { 196 | Ok(stream) => { 197 | stream.write(&mut *data, handler); 198 | } 199 | Err(err) => { 200 | if let Some(h) = handler { 201 | h(Err(err)); 202 | } 203 | } 204 | } 205 | } 206 | 207 | fn request_raw(&self, data: &mut [u8]) -> Result> { 208 | Peripheral::wait_until_done(|done: RequestCallback| { 209 | // TODO this copy can be avoided 210 | let mut data = data.to_vec(); 211 | self.request_raw_async(&mut data, Some(done)); 212 | }) 213 | } 214 | 215 | fn request_by_handle(&self, handle: u16, data: &[u8], handler: Option) { 216 | let mut buf = BytesMut::with_capacity(3 + data.len()); 217 | buf.put_u8(ATT_OP_WRITE_REQ); 218 | buf.put_u16_le(handle); 219 | buf.put(data); 220 | self.request_raw_async(&mut buf, handler); 221 | } 222 | 223 | fn notify(&self, characteristic: &Characteristic, enable: bool) -> Result<()> { 224 | info!("setting notify for {}/{:?} to {}", self.address, characteristic.uuid, enable); 225 | let mut buf = att::read_by_type_req( 226 | characteristic.start_handle, characteristic.end_handle, B16(GATT_CLIENT_CHARAC_CFG_UUID)); 227 | 228 | let data = self.request_raw(&mut buf)?; 229 | 230 | match att::notify_response(&data) { 231 | Ok(resp) => { 232 | let use_notify = characteristic.properties.contains(CharPropFlags::NOTIFY); 233 | let use_indicate = characteristic.properties.contains(CharPropFlags::INDICATE); 234 | 235 | let mut value = resp.1.value; 236 | 237 | if enable { 238 | if use_notify { 239 | value |= 0x0001; 240 | } else if use_indicate { 241 | value |= 0x0002; 242 | } 243 | } else { 244 | if use_notify { 245 | value &= 0xFFFE; 246 | } else if use_indicate { 247 | value &= 0xFFFD; 248 | } 249 | } 250 | 251 | let mut value_buf = BytesMut::with_capacity(2); 252 | value_buf.put_u16_le(value); 253 | let data = Peripheral::wait_until_done(|done: RequestCallback| { 254 | self.request_by_handle(resp.1.handle, &*value_buf, Some(done)) 255 | })?; 256 | 257 | if data.len() > 0 && data[0] == ATT_OP_WRITE_RESP { 258 | debug!("Got response from notify: {:?}", data); 259 | return Ok(()); 260 | } else { 261 | warn!("Unexpected notify response: {:?}", data); 262 | return Err(Error::Other("Failed to set notify".to_string())); 263 | } 264 | } 265 | Err(err) => { 266 | debug!("failed to parse notify response: {:?}", err); 267 | return Err(Error::Other("failed to get characteristic state".to_string())); 268 | } 269 | }; 270 | } 271 | 272 | fn wait_until_done(operation: F) -> Result where F: for<'a> Fn(Callback) { 273 | let pair = Arc::new((Mutex::new(None), Condvar::new())); 274 | let pair2 = pair.clone(); 275 | let on_finish = Box::new(move|result: Result| { 276 | let &(ref lock, ref cvar) = &*pair2; 277 | let mut done = lock.lock().unwrap(); 278 | *done = Some(result.clone()); 279 | cvar.notify_one(); 280 | }); 281 | 282 | operation(on_finish); 283 | 284 | // wait until we're done 285 | let &(ref lock, ref cvar) = &*pair; 286 | 287 | let mut done = lock.lock().unwrap(); 288 | while (*done).is_none() { 289 | done = cvar.wait(done).unwrap(); 290 | } 291 | 292 | // TODO: this copy is avoidable 293 | (*done).clone().unwrap() 294 | } 295 | 296 | fn setup_connection(&self, fd: i32) -> Result { 297 | let local_addr = SockaddrL2 { 298 | l2_family: libc::AF_BLUETOOTH as libc::sa_family_t, 299 | l2_psm: 0, 300 | l2_bdaddr: self.c_adapter.adapter.addr, 301 | l2_cid: ATT_CID, 302 | l2_bdaddr_type: self.c_adapter.adapter.typ.num() as u8, 303 | }; 304 | 305 | // bind to the socket 306 | handle_error(unsafe { 307 | libc::bind(fd, &local_addr as *const SockaddrL2 as *const libc::sockaddr, 308 | size_of::() as u32) 309 | })?; 310 | debug!("bound to socket {}", fd); 311 | 312 | // configure it as a bluetooth socket 313 | let mut opt = [1u8, 0]; 314 | handle_error(unsafe { 315 | libc::setsockopt(fd, libc::SOL_BLUETOOTH, 4, opt.as_mut_ptr() as *mut libc::c_void, 2) 316 | })?; 317 | debug!("configured socket {}", fd); 318 | 319 | let addr = SockaddrL2 { 320 | l2_family: libc::AF_BLUETOOTH as u16, 321 | l2_psm: 0, 322 | l2_bdaddr: self.address, 323 | l2_cid: ATT_CID, 324 | l2_bdaddr_type: 1, 325 | }; 326 | 327 | // connect to the device 328 | handle_error(unsafe { 329 | libc::connect(fd, &addr as *const SockaddrL2 as *const libc::sockaddr, 330 | size_of::() as u32) 331 | })?; 332 | debug!("connected to device {} over socket {}", self.address, fd); 333 | 334 | // restart scanning if we were already, as connecting to a device seems to kill it 335 | if self.c_adapter.scan_enabled.load(Ordering::Relaxed) { 336 | self.c_adapter.start_scan()?; 337 | debug!("restarted scanning"); 338 | } 339 | 340 | // wait until we get the connection notice 341 | let timeout = Duration::from_secs(20); 342 | match self.connection_rx.lock().unwrap().recv_timeout(timeout) { 343 | Ok(handle) => { 344 | return Ok(handle); 345 | } 346 | Err(mpsc::RecvTimeoutError::Timeout) => { 347 | return Err(Error::TimedOut(timeout.clone())); 348 | } 349 | err => { 350 | // unexpected error 351 | err.unwrap(); 352 | unreachable!(); 353 | } 354 | }; 355 | } 356 | } 357 | 358 | impl ApiPeripheral for Peripheral { 359 | fn address(&self) -> BDAddr { 360 | self.address.clone() 361 | } 362 | 363 | fn properties(&self) -> PeripheralProperties { 364 | let l = self.properties.lock().unwrap(); 365 | l.clone() 366 | } 367 | 368 | fn characteristics(&self) -> BTreeSet { 369 | let l = self.characteristics.lock().unwrap(); 370 | l.clone() 371 | } 372 | 373 | fn is_connected(&self) -> bool { 374 | let l = self.stream.try_read(); 375 | return l.is_ok() && l.unwrap().is_some(); 376 | } 377 | 378 | fn connect(&self) -> Result<()> { 379 | // take lock on stream 380 | let mut stream = self.stream.write().unwrap(); 381 | 382 | if stream.is_some() { 383 | // we're already connected, just return 384 | return Ok(()); 385 | } 386 | 387 | // create the socket on which we'll communicate with the device 388 | let fd = handle_error(unsafe { 389 | libc::socket(libc::AF_BLUETOOTH, libc::SOCK_SEQPACKET, 0) 390 | })?; 391 | debug!("created socket {} to communicate with device", fd); 392 | 393 | match self.setup_connection(fd) { 394 | Ok(handle) => { 395 | // create the acl stream that will communicate with the device 396 | let s = ACLStream::new(self.c_adapter.adapter.clone(), 397 | self.address, handle, fd); 398 | 399 | // replay missed messages 400 | let mut queue = self.message_queue.lock().unwrap(); 401 | while !queue.is_empty() { 402 | let msg = queue.pop_back().unwrap(); 403 | if s.handle == msg.handle { 404 | s.receive(&msg); 405 | } 406 | } 407 | 408 | *stream = Some(s); 409 | } 410 | Err(e) => { 411 | // close the socket we opened 412 | debug!("Failed to connect ({}), closing socket {}", e, fd); 413 | handle_error(unsafe { libc::close(fd) })?; 414 | return Err(e) 415 | } 416 | } 417 | 418 | Ok(()) 419 | } 420 | 421 | 422 | 423 | fn disconnect(&self) -> Result<()> { 424 | let mut l = self.stream.write().unwrap(); 425 | 426 | if l.is_none() { 427 | // we're already disconnected 428 | return Ok(()); 429 | } 430 | 431 | let handle = l.as_ref().unwrap().handle; 432 | 433 | let mut data = BytesMut::with_capacity(3); 434 | data.put_u16_le(handle); 435 | data.put_u8(HCI_OE_USER_ENDED_CONNECTION); 436 | let mut buf = hci::hci_command(DISCONNECT_CMD, &*data); 437 | self.c_adapter.write(&mut *buf)?; 438 | 439 | *l = None; 440 | Ok(()) 441 | } 442 | 443 | fn discover_characteristics(&self) -> Result> { 444 | self.discover_characteristics_in_range(0x0001, 0xFFFF) 445 | } 446 | 447 | fn discover_characteristics_in_range(&self, start: u16, end: u16) -> Result> { 448 | let mut results = vec![]; 449 | let mut start = start; 450 | loop { 451 | debug!("discovering chars in range [{}, {}]", start, end); 452 | 453 | let mut buf = att::read_by_type_req(start, end, B16(GATT_CHARAC_UUID)); 454 | let data = self.request_raw(&mut buf)?; 455 | 456 | match att::characteristics(&data) { 457 | Ok(result) => { 458 | match result.1 { 459 | Ok(chars) => { 460 | debug!("Chars: {:#?}", chars); 461 | 462 | // TODO this copy can be removed 463 | results.extend(chars.clone()); 464 | 465 | if let Some(ref last) = chars.iter().last() { 466 | if last.start_handle < end - 1 { 467 | start = last.start_handle + 1; 468 | continue; 469 | } 470 | } 471 | break; 472 | } 473 | Err(err) => { 474 | // this generally means we should stop iterating 475 | debug!("got error: {:?}", err); 476 | break; 477 | } 478 | } 479 | } 480 | Err(err) => { 481 | error!("failed to parse chars: {:?}", err); 482 | return Err(Error::Other(format!("failed to parse characteristics response {:?}", 483 | err))); 484 | } 485 | } 486 | } 487 | 488 | // fix the end handles (we don't get them directly from device, so we have to infer) 489 | for i in 0..results.len() { 490 | (*results.get_mut(i).unwrap()).end_handle = 491 | results.get(i + 1).map(|c| c.end_handle).unwrap_or(end); 492 | } 493 | 494 | // update our cache 495 | let mut lock = self.characteristics.lock().unwrap(); 496 | results.iter().for_each(|c| { lock.insert(c.clone());}); 497 | 498 | Ok(results) 499 | } 500 | 501 | fn command_async(&self, characteristic: &Characteristic, data: &[u8], handler: Option) { 502 | let l = self.stream.read().unwrap(); 503 | match l.as_ref() { 504 | Some(stream) => { 505 | let mut buf = BytesMut::with_capacity(3 + data.len()); 506 | buf.put_u8(ATT_OP_WRITE_CMD); 507 | buf.put_u16_le(characteristic.value_handle); 508 | buf.put(data); 509 | 510 | stream.write_cmd(&mut *buf, handler); 511 | } 512 | None => { 513 | handler.iter().for_each(|h| h(Err(Error::NotConnected))); 514 | } 515 | } 516 | } 517 | 518 | fn command(&self, characteristic: &Characteristic, data: &[u8]) -> Result<()> { 519 | Peripheral::wait_until_done(|done: CommandCallback| { 520 | self.command_async(characteristic, data, Some(done)); 521 | }) 522 | } 523 | 524 | fn request_async(&self, characteristic: &Characteristic, data: &[u8], handler: Option) { 525 | self.request_by_handle(characteristic.value_handle, data, handler); 526 | } 527 | 528 | fn request(&self, characteristic: &Characteristic, data: &[u8]) -> Result> { 529 | Peripheral::wait_until_done(|done: RequestCallback| { 530 | self.request_async(characteristic, data, Some(done)); 531 | }) 532 | } 533 | 534 | fn read_async(&self, characteristic: &Characteristic, handler: Option) { 535 | let mut buf = att::read_req(characteristic.value_handle); 536 | self.request_raw_async(&mut buf, handler); 537 | } 538 | 539 | fn read(&self, characteristic: &Characteristic) -> Result> { 540 | Peripheral::wait_until_done(|done: RequestCallback| { 541 | self.read_async(characteristic, Some(done)); 542 | }) 543 | } 544 | 545 | fn read_by_type_async(&self, characteristic: &Characteristic, uuid: UUID, 546 | handler: Option) { 547 | let mut buf = att::read_by_type_req(characteristic.start_handle, characteristic.end_handle, uuid); 548 | self.request_raw_async(&mut buf, handler); 549 | } 550 | 551 | fn read_by_type(&self, characteristic: &Characteristic, uuid: UUID) -> Result> { 552 | Peripheral::wait_until_done(|done: RequestCallback| { 553 | self.read_by_type_async(characteristic, uuid, Some(done)); 554 | }) 555 | } 556 | 557 | 558 | fn subscribe(&self, characteristic: &Characteristic) -> Result<()> { 559 | self.notify(characteristic, true) 560 | } 561 | 562 | fn unsubscribe(&self, characteristic: &Characteristic) -> Result<()> { 563 | self.notify(characteristic, false) 564 | } 565 | 566 | fn on_notification(&self, handler: NotificationHandler) { 567 | // TODO handle the disconnected case better 568 | let l = self.stream.read().unwrap(); 569 | match l.as_ref() { 570 | Some(stream) => { 571 | stream.on_notification(handler); 572 | } 573 | None => { 574 | error!("tried to subscribe to notifications, but not yet connected") 575 | } 576 | } 577 | } 578 | } 579 | -------------------------------------------------------------------------------- /src/bluez/protocol/hci.rs: -------------------------------------------------------------------------------- 1 | use nom::{le_u8, le_u16, le_u32, le_u64, le_i8, IResult, Err, ErrorKind}; 2 | use num::FromPrimitive; 3 | use bytes::{BytesMut, BufMut}; 4 | 5 | 6 | use ::api::{BDAddr, AddressType}; 7 | use bluez::constants::*; 8 | use bluez::protocol::*; 9 | 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use ::api::BDAddr; 14 | use super::*; 15 | use super::LEAdvertisingData::*; 16 | use super::HCIStatus; 17 | 18 | #[test] 19 | fn test_decode_device_discovery() { 20 | let buf = [4,62,40,2,1,4,0,192,74,150,234,218,116,28,18,9,76,69,68,66, 21 | 108,117,101,45,69,65,57,54,52,65,67,48,32,5,18,16,0,20,0,2,10,4,190]; 22 | 23 | let expected = Message::LEAdvertisingReport( 24 | LEAdvertisingInfo { 25 | evt_type: 4, 26 | bdaddr_type: 0, 27 | bdaddr: BDAddr { 28 | address: [192, 74, 150, 234, 218, 116], 29 | }, 30 | data: vec![ 31 | LocalName(String::from("LEDBlue-EA964AC0 ")), 32 | SlaveConnectionIntervalRange(16, 20), 33 | TxPowerLevel(4), 34 | ] 35 | } 36 | ); 37 | 38 | assert_eq!(message(&buf), Ok((&[][..], expected))); 39 | } 40 | 41 | #[test] 42 | fn test_decode_device_discovery2() { 43 | let buf = [4, 62, 23, 2, 1, 0, 0, 192, 74, 150, 234, 218, 116, 11, 2, 44 | 1, 6, 7, 2, 240, 255, 229, 255, 224, 255, 194]; 45 | 46 | let expected = Message::LEAdvertisingReport( 47 | LEAdvertisingInfo { 48 | evt_type: 0, 49 | bdaddr_type: 0, 50 | bdaddr: BDAddr { 51 | address: [192, 74, 150, 234, 218, 116], 52 | }, 53 | data: vec![ 54 | Flags(AdvertisingFlags::BR_EDR_NOT_SUPPORTED | 55 | AdvertisingFlags::LE_GENERAL_DISCOVERABLE_MODE), 56 | ServiceClassUUID16(0xFFF0), 57 | ServiceClassUUID16(0xFFE5), 58 | ServiceClassUUID16(0xFFE0), 59 | ] 60 | } 61 | ); 62 | 63 | assert_eq!(message(&buf), Ok((&[][..], expected))); 64 | } 65 | 66 | #[test] 67 | fn test_bd_addr() { 68 | let buf = [192u8,74,150,234,218,116]; 69 | assert_eq!(bd_addr(&buf), Ok((&[][..],BDAddr { 70 | address: [192, 74, 150, 234, 218, 116]}))) 71 | } 72 | 73 | #[test] 74 | fn test_le_advertising_info() { 75 | let buf = [1,4,0,192,74,150,234,218,116,11,2,1,6,7,2,240,255,229,255,224,255]; 76 | 77 | assert_eq!(le_advertising_info(&buf), Ok((&[][..], LEAdvertisingInfo { 78 | evt_type: 4, 79 | bdaddr_type: 0, 80 | bdaddr: BDAddr { 81 | address: [192,74,150,234,218,116], 82 | }, 83 | data: vec![ 84 | Flags(AdvertisingFlags::BR_EDR_NOT_SUPPORTED | 85 | AdvertisingFlags::LE_GENERAL_DISCOVERABLE_MODE), 86 | ServiceClassUUID16(65520), 87 | ServiceClassUUID16(65509), 88 | ServiceClassUUID16(65504)], 89 | }))); 90 | } 91 | 92 | #[test] 93 | fn test_le_advertising_data() { 94 | let buf = [7, 2, 240, 255, 229, 255, 224, 255]; 95 | 96 | assert_eq!(le_advertising_data(&buf), Ok((&[][..], 97 | vec![ServiceClassUUID16(65520), 98 | ServiceClassUUID16(65509), 99 | ServiceClassUUID16(65504)]))); 100 | 101 | let buf = [18,9,76,69,68,66,108,117,101,45,69,65,57,55,66,55,65,51,32]; 102 | assert_eq!(le_advertising_data(&buf), Ok((&[][..], vec![ 103 | LocalName(String::from("LEDBlue-EA97B7A3 "))]))); 104 | } 105 | 106 | 107 | #[test] 108 | fn test_broken_le_advertising_data() { 109 | let buf = [0, 0x21, 240, 255, 229, 255, 224, 255]; 110 | assert!(le_advertising_data(&buf).is_err()); 111 | let buf = [1, 0x16, 240, 255, 229, 255, 224, 255]; 112 | assert!(le_advertising_data(&buf).is_err()); 113 | let buf = [3, 0x20, 240, 255, 229, 255, 224, 255]; 114 | assert!(le_advertising_data(&buf).is_err()); 115 | let buf = [3, 0x21, 240, 255, 229, 255, 224, 255]; 116 | assert!(le_advertising_data(&buf).is_err()); 117 | let buf = [3, 0xFE]; 118 | assert!(le_advertising_data(&buf).is_err()); 119 | } 120 | 121 | #[test] 122 | fn test_acl_data_packet() { 123 | let buf = [2, 64, 32, 9, 0, 5, 0, 4, 0, 1, 16, 1, 0, 16]; 124 | assert_eq!(message(&buf), Ok(( 125 | &[][..], 126 | Message::ACLDataPacket(ACLData { 127 | handle: 64, 128 | cid: 4, 129 | data: vec![1, 16, 1, 0, 16], 130 | len: 5, 131 | }), 132 | ))) 133 | } 134 | 135 | #[test] 136 | fn test_cmd_status() { 137 | let buf = [4, 15, 4, 0, 1, 22, 32]; 138 | assert_eq!(message(&buf), Ok(( 139 | &[][..], 140 | Message::CommandStatus { 141 | command: CommandType::LEReadRemoteUsedFeatures, 142 | status: HCIStatus::Success, 143 | } 144 | ))); 145 | } 146 | 147 | #[test] 148 | fn test_recv_le_meta() { 149 | let buf = [4, 62, 12, 4, 0, 64, 0, 1, 0, 0, 0, 0, 0, 0, 0]; 150 | assert_eq!(message(&buf), Ok(( 151 | &[][..], 152 | Message::LEReadRemoteUsedFeaturesComplete { 153 | status: HCIStatus::Success, 154 | handle: 64, 155 | flags: LEFeatureFlags::LE_ENCRYPTION, 156 | } 157 | ))) 158 | } 159 | } 160 | 161 | #[derive(Debug, PartialEq, Clone)] 162 | pub struct ACLData { 163 | pub handle: u16, 164 | pub cid: u16, 165 | pub data: Vec, 166 | pub len: u16, 167 | } 168 | 169 | bitflags! { 170 | pub struct LEFeatureFlags: u64 { 171 | const LE_ENCRYPTION = 0x0001; 172 | const CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 0x0002; 173 | const EXTENDED_REJECT_INDICATION = 0x0004; 174 | const SLAVE_INITIATED_FEATURES_EXCHANGE = 0x0008; 175 | const PING = 0x0010; 176 | const DATA_PACKET_LENGTH_EXTENSION = 0x0020; 177 | const LL_PRIVACY = 0x0040; 178 | const EXTENDED_SCANNER_FILTER_POLICIES = 0x0080; 179 | const LE_2M_PHY = 0x0100; 180 | const STABLE_MODULATION_INDEX_TX = 0x0200; 181 | const STABLE_MODULATION_INDEX_RX = 0x0400; 182 | const LE_CODED_PHY = 0x0800; 183 | const LE_EXTENDED_ADVERTISING = 0x1000; 184 | const LE_PERIODIC_ADVERTISING = 0x2000; 185 | const CHANNEL_SELECTION_ALGORITHM_2 = 0x4000; 186 | const POWER_CLASS_1 = 0x8000; 187 | const MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE = 0x10000; 188 | } 189 | } 190 | 191 | #[derive(Debug, PartialEq)] 192 | pub enum Message { 193 | LEAdvertisingReport(LEAdvertisingInfo), 194 | LEConnComplete(LEConnInfo), 195 | LEConnUpdate(LEConnUpdateInfo), 196 | LEReadRemoteUsedFeaturesComplete { 197 | status: HCIStatus, 198 | handle: u16, 199 | flags: LEFeatureFlags, 200 | }, 201 | HCICommandComplete(CommandComplete), 202 | LEScanEnableCommand { 203 | enable: bool, 204 | filter_duplicates: bool, 205 | }, 206 | HCICommand { 207 | command: CommandType, 208 | data: Vec, 209 | }, 210 | DisconnectComplete { 211 | status: HCIStatus, 212 | handle: u16, 213 | reason: HCIStatus, 214 | }, 215 | CommandStatus { 216 | command: CommandType, 217 | status: HCIStatus, 218 | }, 219 | ACLDataPacket(ACLData), 220 | ACLDataContinuation { 221 | handle: u16, 222 | data: Vec, 223 | } 224 | } 225 | 226 | bitflags! { 227 | pub struct AdvertisingFlags: u8 { 228 | const LE_LIMITED_DISCOVERABLE_MODE = 0x01; 229 | const LE_GENERAL_DISCOVERABLE_MODE = 0x02; 230 | const BR_EDR_NOT_SUPPORTED = 0x04; 231 | const SIMULTANEOUS_LE_BR_EDR_TO_SAME_DEVICE_CAPABLE_CONTROLLER = 0x08; 232 | const SIMULTANEOUS_LE_BR_EDR_TO_SAME_DEVICE_CAPABLE_HOST = 0x10; 233 | const RESERVED3 = 0x20; 234 | const RESERVED2 = 0x40; 235 | const RESERVED1 = 0x80; 236 | } 237 | } 238 | 239 | #[derive(Debug, PartialEq)] 240 | pub enum LEAdvertisingData { 241 | Flags(AdvertisingFlags), 242 | ServiceClassUUID16(u16), 243 | ServiceClassUUID128([u8; 16]), 244 | LocalName(String), 245 | TxPowerLevel(i8), 246 | SlaveConnectionIntervalRange(u16, u16), 247 | SolicitationUUID16(u16), 248 | SolicitationUUID128([u8; 16]), 249 | ServiceData16(u16, Vec), 250 | ServiceData32(u32, Vec), 251 | ServiceData128([u8; 16], Vec), 252 | SolicitationUUID32(u32), 253 | ManufacturerSpecific(Vec), 254 | } 255 | 256 | #[derive(Debug, PartialEq)] 257 | pub struct LEAdvertisingInfo { 258 | pub evt_type: u8, 259 | pub bdaddr_type: u8, 260 | pub bdaddr: BDAddr, 261 | pub data: Vec 262 | } 263 | 264 | #[derive(Debug, PartialEq)] 265 | pub struct LEConnInfo { 266 | pub handle: u16, 267 | pub role: u8, 268 | pub bdaddr: BDAddr, 269 | pub bdaddr_type: u8, 270 | pub interval: u16, 271 | pub latency: u16, 272 | pub supervision_timeout: u16, 273 | pub master_clock_accuracy: u8, 274 | } 275 | 276 | #[derive(Debug, PartialEq)] 277 | pub struct LEConnUpdateInfo { 278 | pub status: HCIStatus, 279 | pub handle: u16, 280 | pub interval: u16, 281 | pub latency: u16, 282 | pub supervision_timeout: u16, 283 | } 284 | 285 | 286 | enum_from_primitive! { 287 | #[derive(Debug, PartialEq)] 288 | #[repr(u8)] 289 | pub enum HCIStatus { 290 | ACLConnectionAlreadyExists = 0x0B, 291 | AuthenticationFialure = 0x05, 292 | ChannelAssessmentNotSupported = 0x2E, 293 | CommandDisallowed = 0x0C, 294 | CoarseClockAdjustRejected = 0x40, 295 | ConnectionAcceptanceTimeoutExceeded = 0x10, 296 | ConnectionFailuedtoEstablish = 0x3E, 297 | ConnectionLimitExceeded = 0x09, 298 | ConnectionRejectedDuetoLimitedResources = 0x0D, 299 | ConnectionRejectedNoSuitableChannelFound = 0x39, 300 | ConnectionRejectedForSecurityReasons = 0x0E, 301 | ConnectionRejectedDuetoUnacceptableBDADDR = 0x0F, 302 | ConnectionTerminatedByLocalHost = 0x16, 303 | ConnectionTerminatedDuetoMICFailure = 0x3D, 304 | ConnectionTimeout = 0x08, 305 | ControllerBusy = 0x3A, 306 | DifferentTransactionCollision = 0x2A, 307 | DirectedAdvertisingTimeout = 0x3C, 308 | EncryptModeNotAcceptable = 0x25, 309 | ExtendedInquiryResponseTooLarge = 0x36, 310 | HostBusyPairing = 0x38, 311 | HardwareFailure = 0x03, 312 | InstantPassed = 0x28, 313 | InsufficientSecurity = 0x2F, 314 | InvalidHCICommandParameters = 0x12, 315 | InvalidLMPParamaters = 0x1E, 316 | LinkKeyCanNotBeChanged = 0x26, 317 | LMPErrorTransactionCollision = 0x23, 318 | LMPLLResponseTimeout = 0x22, 319 | LMPDUNotAllowed = 0x24, 320 | MACConnectionFailed = 0x3F, 321 | MemoryCapabilityExceeded = 0x07, 322 | PageTimeout = 0x04, 323 | PairingNotAllowed = 0x18, 324 | PairingWithUnitKeyNotSupported = 0x29, 325 | ParamaterOutOfMandatoryRange = 0x30, 326 | PinKeyMissing = 0x06, 327 | QOSReject = 0x2D, 328 | QOSUnacceptableParameter = 0x2C, 329 | RemoteDeviceTerminatedConnectionDueToLowResources = 0x14, 330 | RemoteDeviceTerminatedConnectionDuetoPowerOff = 0x15, 331 | RemoteUserTerminatedConnection = 0x13, 332 | RepeatedAttempts = 0x17, 333 | RequestQOSNotSupported = 0x27, 334 | Reserved2B = 0x2B, 335 | Reserved31 = 0x31, 336 | Reserved33 = 0x33, 337 | ReservedSlotViolation = 0x34, 338 | RoleChangeNotAllowed = 0x21, 339 | RoleSwitchFailed = 0x35, 340 | RoleSwitchPending = 0x32, 341 | SCOAirModeRejected = 0x1D, 342 | SCOIntervalRejected = 0x1C, 343 | SCOOffsetRejected = 0x1B, 344 | SimplePairingNotSupportedByHost = 0x37, 345 | SynchonousConnectionLimitExceeded = 0x0A, 346 | UnacceptableConnectionParameters = 0x3B, 347 | UnknownConnectionID = 0x02, 348 | UnknownHCICommand = 0x01, 349 | UnknownLMPPDU = 0x19, 350 | UnspecifiedError = 0x1F, 351 | UnsupportedParamter = 0x11, 352 | UnsupportedLMPParameterValue = 0x20, 353 | UnsupportedRemoteFeature = 0x1A, 354 | Success = 0x00, 355 | } 356 | } 357 | 358 | enum_from_primitive! { 359 | #[derive(Debug, PartialEq)] 360 | #[repr(u8)] 361 | enum EventType { 362 | HCICommandPkt = 1, 363 | HCIAclDataPkt = 2, 364 | HCIEventPkt = 4, 365 | } 366 | } 367 | 368 | enum_from_primitive! { 369 | #[derive(Debug, PartialEq)] 370 | #[repr(u8)] 371 | enum HCIEventSubType { 372 | DisconnComplete = 0x05, 373 | EncryptChange = 0x08, 374 | CmdComplete = 0x0e, 375 | CmdStatus = 0x0f, 376 | LEMetaEvent = 0x3e, 377 | } 378 | } 379 | 380 | enum_from_primitive! { 381 | #[derive(Debug, PartialEq)] 382 | #[repr(u8)] 383 | enum LEEventType { 384 | LEConnComplete = 1, 385 | LEAdvertisingReport = 2, 386 | LEConnUpdateComplete = 3, 387 | LEReadRemoteUsedFeaturesComplete = 4, 388 | }} 389 | 390 | enum_from_primitive! { 391 | #[derive(Debug, PartialEq)] 392 | #[repr(u16)] 393 | pub enum CommandType { 394 | Reset = OCF_RESET as u16 | (OGF_HOST_CTL as u16) << 10, 395 | ReadLEHostSupported = OCF_READ_LE_HOST_SUPPORTED | (OGF_HOST_CTL as u16) << 10, 396 | WriteLEHostSupported = OCF_WRITE_LE_HOST_SUPPORTED | (OGF_HOST_CTL as u16) << 10, 397 | ReadLocalVersion = OCF_READ_LOCAL_VERSION | (OGF_INFO_PARAM as u16) << 10, 398 | ReadBDAddr = OCF_READ_BD_ADDR | (OGF_INFO_PARAM as u16) << 10, 399 | ReadRSSI = OCF_READ_RSSI | (OGF_STATUS_PARAM as u16) << 10, 400 | 401 | ChangeLocalName = 0x0C13, 402 | WriteExtendedInquiryResponse = 0x0C52, 403 | 404 | LESetEventMask = OCF_LE_SET_EVENT_MASK | (OGF_LE_CTL as u16) << 10, 405 | LESetScanParameters = OCF_LE_SET_SCAN_PARAMETERS | (OGF_LE_CTL as u16) << 10, 406 | LESetScanEnabled = OCF_LE_SET_SCAN_ENABLE | (OGF_LE_CTL as u16) << 10, 407 | LECreateConnection = OCF_LE_CREATE_CONN | (OGF_LE_CTL as u16) << 10, 408 | LEConnectionUpdate = OCF_LE_CONN_UPDATE | (OGF_LE_CTL as u16) << 10, 409 | LEStartEncryption = OCF_LE_START_ENCRYPTION | (OGF_LE_CTL as u16) << 10, 410 | 411 | LESetAdvertisingData = 0x2008, 412 | LESetScanResponseData = 0x2009, 413 | LEAddDeviceToWhiteList = 0x2011, 414 | LERemoveDeviceFromWhiteList = 0x2012, 415 | LEReadRemoteUsedFeatures = 0x2016, 416 | 417 | Disconnect = 0x0406, 418 | }} 419 | 420 | #[allow(dead_code)] 421 | #[derive(Debug, PartialEq)] 422 | pub enum CommandComplete { 423 | Reset, 424 | ReadLEHostSupported { le: u8, simul: u8 }, 425 | ReadLocalVersion { 426 | hci_version: u8, 427 | hci_revision: u16, 428 | lmp_version: i8, 429 | manufacturer: u16, 430 | lmp_sub_version: u8, 431 | }, 432 | ReadBDAddr { 433 | address_type: AddressType, 434 | address: BDAddr, 435 | }, 436 | LESetScanParameters, 437 | LESetScanEnabled { 438 | enabled: bool, 439 | }, 440 | ReadRSSI { 441 | handle: u16, 442 | rssi: u8 443 | }, 444 | Other { 445 | command: CommandType, 446 | status: u8, 447 | data: Vec 448 | } 449 | } 450 | 451 | fn le_advertising_data(i: &[u8]) -> IResult<&[u8], Vec> { 452 | use self::LEAdvertisingData::*; 453 | let (i, len) = try_parse!(i, le_u8); 454 | let (i, typ) = try_parse!(i, le_u8); 455 | 456 | if len < 1 { 457 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 458 | } 459 | 460 | let len = len as usize - 1; 461 | // let mut result = vec![]; 462 | let (i, result)= match typ { 463 | 0x1 => { 464 | try_parse!(i, map!(le_u8, |u| vec![Flags(AdvertisingFlags::from_bits_truncate(u))])) 465 | } 466 | 0x02|0x03 => { 467 | try_parse!(i, count!(map!(le_u16, |u| ServiceClassUUID16(u)), len / 2)) 468 | } 469 | 0x06|0x07 => { 470 | try_parse!(i, count!(map!(parse_uuid_128, 471 | |b| ServiceClassUUID128(b)), len / 16)) 472 | } 473 | 0x08|0x09 => { 474 | try_parse!(i, map!(take!(len), 475 | |b| vec![LocalName(String::from_utf8_lossy(b).into_owned())])) 476 | } 477 | 0x0A => { 478 | try_parse!(i, map!(le_i8, |b| vec![TxPowerLevel(b)])) 479 | } 480 | 0x12 => { 481 | try_parse!(i, do_parse!( 482 | min: le_u16 >> 483 | max: le_u16 >> 484 | (vec![SlaveConnectionIntervalRange(min, max)]) 485 | )) 486 | } 487 | 0x14 => { 488 | try_parse!(i, count!(map!(le_u16, |u| SolicitationUUID16(u)), len / 2)) 489 | } 490 | 0x15 => { 491 | try_parse!(i, count!(map!(parse_uuid_128, 492 | |b| SolicitationUUID128(b)), len / 16)) 493 | } 494 | 0x16 => { 495 | if len < 2 { 496 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 497 | } 498 | 499 | try_parse!(i, do_parse!( 500 | uuid: le_u16 >> 501 | data: count!(le_u8, len - 2) >> 502 | (vec![ServiceData16(uuid, data)]))) 503 | } 504 | 0x20 => { 505 | if len < 4 { 506 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 507 | } 508 | try_parse!(i, do_parse!( 509 | uuid: le_u32 >> 510 | data: count!(le_u8, len - 4) >> 511 | (vec![ServiceData32(uuid, data)]))) 512 | } 513 | 0x21 => { 514 | if len < 16 { 515 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 516 | } 517 | try_parse!(i, do_parse!( 518 | uuid: parse_uuid_128 >> 519 | data: count!(le_u8, len - 16) >> 520 | (vec![ServiceData128(uuid, data)]))) 521 | } 522 | 0x1F => { 523 | try_parse!(i, count!(map!(le_u32, 524 | |b| SolicitationUUID32(b)), len / 4)) 525 | } 526 | 0xFF => { 527 | try_parse!(i, map!(count!(le_u8, len), |b| vec![ManufacturerSpecific(b)])) 528 | } 529 | _ => { 530 | // skip this field 531 | debug!("Unknown field type {} in {:?}", typ, i); 532 | if len < i.len() { 533 | (&i[len as usize..], vec![]) 534 | } else { 535 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 536 | } 537 | } 538 | }; 539 | Ok((i, result)) 540 | } 541 | 542 | named!(le_advertising_info<&[u8], LEAdvertisingInfo>, 543 | do_parse!( 544 | // TODO: support counts other than 1 545 | _count: le_u8 >> 546 | evt_type: le_u8 >> 547 | bdaddr_type: le_u8 >> 548 | bdaddr: bd_addr >> 549 | data: length_value!(le_u8, fold_many0!(complete!(le_advertising_data), Vec::new(), |mut acc: Vec<_>, x| { 550 | acc.extend(x); 551 | acc 552 | })) >> 553 | ( 554 | LEAdvertisingInfo { 555 | evt_type, bdaddr_type, bdaddr, data: data 556 | } 557 | ) 558 | )); 559 | 560 | named!(bd_addr<&[u8], BDAddr>, 561 | do_parse!( 562 | addr: take!(6) >> ( 563 | BDAddr { 564 | address: [addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]], 565 | }) 566 | )); 567 | 568 | named!(le_conn_complete<&[u8], LEConnInfo>, 569 | do_parse!( 570 | _skip: le_u8 >> 571 | handle: le_u16 >> 572 | role: le_u8 >> 573 | bdaddr_type: le_u8 >> 574 | bdaddr: bd_addr >> 575 | interval: le_u16 >> 576 | latency: le_u16 >> 577 | supervision_timeout: le_u16 >> 578 | master_clock_accuracy: le_u8 >> 579 | ( 580 | LEConnInfo { 581 | handle, role, bdaddr_type, bdaddr, interval, latency, 582 | supervision_timeout, master_clock_accuracy 583 | } 584 | ))); 585 | 586 | named!(le_read_remote_used_features_complete<&[u8], Message>, 587 | do_parse!( 588 | status: map_opt!(le_u8, |b| HCIStatus::from_u8(b)) >> 589 | handle: le_u16 >> 590 | flags: le_u64 >> 591 | ( 592 | Message::LEReadRemoteUsedFeaturesComplete { 593 | status, handle, 594 | flags: LEFeatureFlags::from_bits_truncate(flags), 595 | } 596 | ) 597 | ) 598 | ); 599 | 600 | named!(le_conn_update_complete<&[u8], Message>, 601 | do_parse!( 602 | status: map_opt!(le_u8, |b| HCIStatus::from_u8(b)) >> 603 | handle: le_u16 >> 604 | interval: le_u16 >> 605 | latency: le_u16 >> 606 | supervision_timeout: le_u16 >> 607 | ( 608 | Message::LEConnUpdate(LEConnUpdateInfo { 609 | status, handle, interval, latency, supervision_timeout 610 | }) 611 | ) 612 | )); 613 | 614 | fn le_meta_event(i: &[u8]) -> IResult<&[u8], Message> { 615 | let (i, le_type) = try_parse!(i, map_opt!(le_u8, |b| LEEventType::from_u8(b))); 616 | let (i, result) = match le_type { 617 | LEEventType::LEAdvertisingReport => { 618 | try_parse!(i, map!(le_advertising_info, |x| Message::LEAdvertisingReport(x))) 619 | } 620 | LEEventType::LEConnComplete => { 621 | try_parse!(i, map!(le_conn_complete, |x| Message::LEConnComplete(x))) 622 | } 623 | LEEventType::LEReadRemoteUsedFeaturesComplete => { 624 | try_parse!(i, le_read_remote_used_features_complete) 625 | } 626 | LEEventType::LEConnUpdateComplete => { 627 | try_parse!(i, le_conn_update_complete) 628 | } 629 | }; 630 | Ok((i, result)) 631 | } 632 | 633 | fn cmd_complete(i: &[u8]) -> IResult<&[u8], Message> { 634 | use self::CommandComplete::*; 635 | 636 | let (i, _skip) = try_parse!(i, le_u8); 637 | let (i, cmd) = try_parse!(i, map_opt!(le_u16, |b| CommandType::from_u16(b))); 638 | let (i, status) = try_parse!(i, le_u8); 639 | let result = match cmd { 640 | CommandType::Reset => Reset, 641 | CommandType::ReadLEHostSupported => { 642 | let (i, le) = try_parse!(i, le_u8); 643 | let (_, simul) = try_parse!(i, le_u8); 644 | ReadLEHostSupported { le, simul } 645 | }, 646 | CommandType::ReadBDAddr => { 647 | let (i, address_type) = try_parse!(i, map_opt!(le_u8, |b| AddressType::from_u8(b))); 648 | let (_, address) = try_parse!(i, bd_addr); 649 | 650 | ReadBDAddr { address_type, address } 651 | }, 652 | CommandType::LESetScanParameters => LESetScanParameters, 653 | CommandType::LESetScanEnabled => { 654 | LESetScanEnabled { enabled: status == 1 } 655 | }, 656 | CommandType::ReadRSSI => { 657 | let (i, handle) = try_parse!(i, le_u16); 658 | let (_, rssi) = try_parse!(i, le_u8); 659 | ReadRSSI { handle, rssi } 660 | }, 661 | x => { 662 | Other { 663 | command: x, 664 | status, 665 | data: i.clone().to_owned() 666 | } 667 | } 668 | }; 669 | 670 | Ok((&i, Message::HCICommandComplete(result))) 671 | } 672 | 673 | named!(disconnect_complete<&[u8], Message>, 674 | do_parse!( 675 | status: map_opt!(le_u8, |b| HCIStatus::from_u8(b)) >> 676 | handle: le_u16 >> 677 | reason: map_opt!(le_u8, |b| HCIStatus::from_u8(b)) >> 678 | ( 679 | Message::DisconnectComplete { 680 | status, handle, reason 681 | } 682 | ) 683 | ) 684 | ); 685 | 686 | fn hci_event_pkt(i: &[u8]) -> IResult<&[u8], Message> { 687 | use self::HCIEventSubType::*; 688 | let (i, sub_type) = try_parse!(i, map_opt!(le_u8, |b| HCIEventSubType::from_u8(b))); 689 | let (i, data) = try_parse!(i, length_data!(le_u8)); 690 | let result = match sub_type { 691 | LEMetaEvent => try_parse!(data, le_meta_event).1, 692 | CmdComplete => try_parse!(data, cmd_complete).1, 693 | CmdStatus => { 694 | let (data, status) = try_parse!(data, map_opt!(le_u8, |b| HCIStatus::from_u8(b))); 695 | let (data, _) = try_parse!(data, le_u8); 696 | let (_, command) = try_parse!(data, map_opt!(le_u16, |b| CommandType::from_u16(b))); 697 | Message::CommandStatus { 698 | command, status, 699 | } 700 | }, 701 | DisconnComplete => try_parse!(data, disconnect_complete).1, 702 | _ => { 703 | warn!("Unhandled HCIEventPkt subtype {:?}", sub_type); 704 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(4)))); 705 | } 706 | }; 707 | Ok((i, result)) 708 | } 709 | 710 | fn hci_command_pkt(i: &[u8]) -> IResult<&[u8], Message> { 711 | let (i, cmd) = try_parse!(i, map_opt!(le_u16, CommandType::from_u16)); 712 | let (i, data) = try_parse!(i, length_data!(le_u8)); 713 | let result = match cmd { 714 | CommandType::LESetScanEnabled => { 715 | let (data, enable) = try_parse!(data, le_u8); 716 | let (_, filter_duplicates) = try_parse!(data, le_u8); 717 | Message::LEScanEnableCommand { 718 | enable: enable == 1, 719 | filter_duplicates: filter_duplicates == 1, 720 | } 721 | }, 722 | other => { 723 | Message::HCICommand { 724 | command: other, 725 | data: data.to_owned(), 726 | } 727 | } 728 | }; 729 | Ok((i, result)) 730 | } 731 | 732 | fn hci_acldata_pkt(i: &[u8]) -> IResult<&[u8], Message> { 733 | let (i, head) = try_parse!(i, le_u16); // 2 734 | let flags = head >> 12; 735 | let handle = head & 0x0FFF; 736 | let (i, message) = match flags { 737 | ACL_START | ACL_START_NO_FLUSH => { 738 | // the length of this packet 739 | let (i, dlen) = try_parse!(i, le_u16); 740 | // the length of the message, which may span multiple packets 741 | let (i, plen) = try_parse!(i, le_u16); 742 | let (i, cid) = try_parse!(i, le_u16); 743 | let (i, data) = try_parse!(i, take!(dlen - 4)); 744 | (i, Message::ACLDataPacket(ACLData { 745 | handle, 746 | cid, 747 | data: data.to_owned(), 748 | len: plen, 749 | })) 750 | } 751 | ACL_CONT => { 752 | (&[][..], Message::ACLDataContinuation { 753 | handle, 754 | data: i.clone().to_owned(), 755 | }) 756 | }, 757 | x => { 758 | warn!("unknown flag type: {}", x); 759 | return Err(Err::Error(error_position!(i, ErrorKind::Custom(11)))); 760 | } 761 | }; 762 | Ok((i, message)) 763 | } 764 | 765 | pub fn message(i: &[u8]) -> IResult<&[u8], Message> { 766 | use self::EventType::*; 767 | 768 | let (i, typ) = try_parse!(i, map_opt!(le_u8, |b| EventType::from_u8(b))); 769 | match typ { 770 | HCICommandPkt => hci_command_pkt(i), // 1 771 | HCIAclDataPkt => hci_acldata_pkt(i), // 2 772 | HCIEventPkt => hci_event_pkt(i), // 4 773 | } 774 | } 775 | 776 | pub fn hci_command(command: u16, data: &[u8]) -> BytesMut { 777 | let mut buf = BytesMut::with_capacity(4 + data.len()); 778 | 779 | // header 780 | buf.put_u8(HCI_COMMAND_PKT); 781 | buf.put_u16_le(command); 782 | 783 | // len 784 | buf.put_u8(data.len() as u8); 785 | 786 | // data 787 | buf.put(data); 788 | buf 789 | } 790 | --------------------------------------------------------------------------------