blob: 03aa747462b4643faba5496ad75ed1b7354dff4e [file] [log] [blame]
use alloc::String;
use alloc::Vec;
use callback::CallbackSubscription;
use callback::SubscribableCallback;
use result;
use shared_memory::SharedMemory;
use syscalls;
const DRIVER_NUMBER: usize = 0x30000;
pub const MAX_PAYLOAD_SIZE: usize = 9;
pub const BUFFER_SIZE_ADVERTISE: usize = 39;
pub const BUFFER_SIZE_SCAN: usize = 39;
mod ble_commands {
pub const START_ADVERTISING: usize = 0;
pub const SET_ADVERTISING_INTERVAL: usize = 3;
pub const ALLOW_ADVERTISMENT_BUFFER: usize = 0x32;
pub const REQ_ADV_ADDRESS: usize = 6;
pub const BLE_PASSIVE_SCAN_SUB: usize = 0;
pub const ALLOW_SCAN_BUFFER: usize = 0x31;
pub const PASSIVE_SCAN: usize = 5;
}
mod gap_flags {
pub const BLE_DISCOVERABLE: u8 = 0x02;
pub const BLE_NOT_DISCOVERABLE: u8 = 0x01;
pub const ONLY_LE: u8 = 0x04;
}
pub mod gap_data {
pub const COMPLETE_LIST_16BIT_SERVICE_IDS: usize = 0x03;
pub const COMPLETE_LOCAL_NAME: usize = 0x09;
pub const SET_FLAGS: usize = 1;
pub const SERVICE_DATA: usize = 0x16;
}
pub struct BleAdvertisingDriver;
impl BleAdvertisingDriver {
pub fn create_advertising_buffer() -> [u8; BUFFER_SIZE_ADVERTISE] {
[0; BUFFER_SIZE_ADVERTISE]
}
pub fn initialize<'a>(
interval: u16,
mut name: String,
uuid: &'a mut [u8; 2],
stay_visible: bool,
service_payload: &'a mut Vec<u8>,
advertising_buffer: &'a mut [u8; BUFFER_SIZE_ADVERTISE],
) -> Result<SharedMemory<'a>, isize> {
let mut flags: [u8; 1] = [gap_flags::ONLY_LE | (if stay_visible {
gap_flags::BLE_DISCOVERABLE
} else {
gap_flags::BLE_NOT_DISCOVERABLE
})];
let shared_memory = syscalls::allow(
DRIVER_NUMBER,
ble_commands::ALLOW_ADVERTISMENT_BUFFER,
advertising_buffer,
)?;
Self::set_advertising_interval(interval)?;
Self::request_adv_address()?;
let _name_handle = Self::set_local_name(&mut name)?;
let _uuid_handle = Self::set_uuid(uuid)?;
let _flags_handle = Self::set_flags(&mut flags)?;
let _payload_handle = Self::set_service_payload(service_payload)?;
Self::start_advertising()?;
Ok(shared_memory)
}
// TODO: Write generic error converter
fn set_advertising_interval(interval: u16) -> Result<(), isize> {
let result = unsafe {
syscalls::command(
DRIVER_NUMBER,
ble_commands::SET_ADVERTISING_INTERVAL,
interval as usize,
0,
)
};
convert_result(result)
}
fn request_adv_address() -> Result<(), isize> {
let result =
unsafe { syscalls::command(DRIVER_NUMBER, ble_commands::REQ_ADV_ADDRESS, 0, 0) };
convert_result(result)
}
fn set_local_name(name: &mut String) -> Result<SharedMemory, isize> {
unsafe {
syscalls::allow(
DRIVER_NUMBER,
gap_data::COMPLETE_LOCAL_NAME,
name.as_bytes_mut(),
)
}
}
fn set_uuid(uuid: &mut [u8]) -> Result<SharedMemory, isize> {
syscalls::allow(
DRIVER_NUMBER,
gap_data::COMPLETE_LIST_16BIT_SERVICE_IDS,
uuid,
)
}
fn set_flags(flags: &mut [u8; 1]) -> Result<SharedMemory, isize> {
syscalls::allow(DRIVER_NUMBER, gap_data::SET_FLAGS, flags)
}
fn set_service_payload(service_payload: &mut [u8]) -> Result<SharedMemory, isize> {
syscalls::allow(DRIVER_NUMBER, gap_data::SERVICE_DATA, service_payload)
}
fn start_advertising() -> Result<(), isize> {
let result =
unsafe { syscalls::command(DRIVER_NUMBER, ble_commands::START_ADVERTISING, 0, 0) };
convert_result(result)
}
}
pub struct BleCallback<CB> {
callback: CB,
}
impl<CB> BleCallback<CB> {
pub fn new(callback: CB) -> Self {
BleCallback { callback }
}
}
impl<CB: FnMut(usize, usize)> SubscribableCallback for BleCallback<CB> {
fn call_rust(&mut self, arg0: usize, arg1: usize, _: usize) {
(self.callback)(arg0, arg1);
}
}
pub struct BleDriver;
impl BleDriver {
pub fn create_scan_buffer() -> [u8; BUFFER_SIZE_SCAN] {
[0; BUFFER_SIZE_SCAN]
}
pub fn share_memory(scan_buffer: &mut [u8; BUFFER_SIZE_SCAN]) -> Result<SharedMemory, isize> {
syscalls::allow(DRIVER_NUMBER, ble_commands::ALLOW_SCAN_BUFFER, scan_buffer)
}
pub fn start<CB>(callback: &mut BleCallback<CB>) -> Result<CallbackSubscription, isize>
where
BleCallback<CB>: SubscribableCallback,
{
let subscription =
syscalls::subscribe(DRIVER_NUMBER, ble_commands::BLE_PASSIVE_SCAN_SUB, callback)?;
let result = unsafe { syscalls::command(DRIVER_NUMBER, ble_commands::PASSIVE_SCAN, 1, 0) };
convert_result(result)?;
Ok(subscription)
}
}
fn convert_result(code: isize) -> Result<(), isize> {
match code {
result::SUCCESS => Ok(()),
code => Err(code),
}
}