blob: 3206a85511732dce564db7b939fd20c1e6622054 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![deny(warnings)]
#![allow(missing_docs)]
use failure::{format_err, Error};
use rand::{self, Rng};
use std;
use std::ffi::{CString, OsStr, OsString};
use std::fs::{File, OpenOptions};
use std::mem;
use std::os::raw;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use fdio::{fdio_sys, ioctl, make_ioctl};
use fuchsia_zircon::{self as zircon, Handle};
pub const DEV_TEST: &str = "/dev/test/test";
pub const BTHCI_DRIVER_NAME: &str = "/system/driver/bthci-fake.so";
// Returns the name of the fake device and a File representing the device on success.
pub fn create_and_bind_device() -> Result<(File, String), Error> {
let mut rng = rand::thread_rng();
let id = format!("bt-hci-{}", rng.gen::<u8>());
let devpath = create_fake_device(DEV_TEST, id.as_str())?;
let mut retry = 0;
let mut dev = None;
{
while retry < 100 {
retry += 1;
if let Ok(d) = open_rdwr(&devpath) {
dev = Some(d);
break;
}
}
}
let dev = dev.ok_or_else(|| format_err!("could not open {:?}", devpath))?;
bind_fake_device(&dev)?;
Ok((dev, id))
}
pub fn create_fake_device(test_path: &str, dev_name: &str) -> Result<OsString, Error> {
let test_dev = open_rdwr(test_path)?;
let devname = CString::new(dev_name)?;
let mut devpath = [0; 1024];
// This is safe because the length of the output buffer is computed from the vector, and the
// callee does not retain the pointers.
let pathlen = unsafe {
ioctl(
&test_dev,
IOCTL_TEST_CREATE_DEVICE,
devname.as_ptr() as *const raw::c_void,
devname.as_bytes_with_nul().len(),
devpath.as_mut_ptr() as *mut raw::c_void,
devpath.len(),
)?
};
// Need to return an OsString with length equal to the return value of the ioctl, rather than
// the full length of the buffer.
let mut ospath = OsString::new();
ospath.push(OsStr::from_bytes(&devpath[0..(pathlen - 1) as usize]));
Ok(ospath)
}
pub fn bind_fake_device(device: &File) -> Result<(), Error> {
let devname = CString::new(BTHCI_DRIVER_NAME)?;
// This is safe because no memory ownership is transferred by this function and the length of
// the input buffer is computed from the CString.
unsafe {
ioctl(
device,
IOCTL_DEVICE_BIND,
devname.as_ptr() as *const raw::c_void,
devname.as_bytes_with_nul().len(),
::std::ptr::null_mut() as *mut raw::c_void,
0,
).map(|_| ())
.map_err(|e| e.into())
}
}
pub fn destroy_device(device: &File) -> Result<(), Error> {
// This is safe because no memory ownership is transferred by this function.
unsafe {
ioctl(
device,
IOCTL_TEST_DESTROY_DEVICE,
::std::ptr::null() as *const raw::c_void,
0,
::std::ptr::null_mut() as *mut raw::c_void,
0,
).map(|_| ())
.map_err(|e| e.into())
}
}
// Ioctl called used to get the driver name of the bluetooth hci device. This is used to ensure
// the driver is the right driver to be bound to the device.
// TODO(bwb): move out to a generic crate
pub fn get_device_driver_name(device: &File) -> Result<OsString, Error> {
let mut driver_name = [0; 1024];
// This is safe because the length of the output buffer is computed from the vector, and the
// callee does not retain the pointers.
let name_size = unsafe {
ioctl(
device,
IOCTL_DEVICE_GET_DRIVER_NAME,
::std::ptr::null_mut() as *mut raw::c_void,
0,
driver_name.as_mut_ptr() as *mut raw::c_void,
driver_name.len(),
)?
};
// Need to return an OsString with length equal to the return value of the ioctl, rather than
// the full length of the buffer.
let mut ospath = OsString::new();
ospath.push(OsStr::from_bytes(&driver_name[0..name_size as usize]));
Ok(ospath)
}
// Ioctl definitions for the above calls.
// TODO(bwb): move out to a generic crate
pub fn open_snoop_channel(device: &File) -> Result<zircon::Handle, Error> {
let mut handle = zircon::sys::ZX_HANDLE_INVALID;
unsafe {
ioctl(
device,
IOCTL_BT_HCI_GET_SNOOP_CHANNEL,
::std::ptr::null_mut() as *mut raw::c_void,
0,
&mut handle as *mut _ as *mut std::os::raw::c_void,
mem::size_of::<zircon::sys::zx_handle_t>(),
).map(|_| Handle::from_raw(handle))
.map_err(|e| e.into())
}
}
fn open_rdwr<P: AsRef<Path>>(path: P) -> Result<File, Error> {
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(|e| e.into())
}
// Ioctl definitions for the above calls.
// TODO(bwb): move out to a generic crate
const IOCTL_TEST_CREATE_DEVICE: raw::c_int =
make_ioctl!(fdio_sys::IOCTL_KIND_DEFAULT, fdio_sys::IOCTL_FAMILY_TEST, 0);
const IOCTL_TEST_DESTROY_DEVICE: raw::c_int =
make_ioctl!(fdio_sys::IOCTL_KIND_DEFAULT, fdio_sys::IOCTL_FAMILY_TEST, 1);
const IOCTL_DEVICE_BIND: raw::c_int = make_ioctl!(
fdio_sys::IOCTL_KIND_DEFAULT,
fdio_sys::IOCTL_FAMILY_DEVICE,
0
);
const IOCTL_DEVICE_GET_DRIVER_NAME: raw::c_int = make_ioctl!(
fdio_sys::IOCTL_KIND_DEFAULT,
fdio_sys::IOCTL_FAMILY_DEVICE,
2
);
const IOCTL_BT_HCI_GET_SNOOP_CHANNEL: raw::c_int = make_ioctl!(
fdio_sys::IOCTL_KIND_GET_HANDLE,
fdio_sys::IOCTL_FAMILY_BT_HCI,
2
);