Bluetooth Low Energy Design

This document describes the design of the BLE stack in Tock.

System call interface

The system call interface is modeled after the HCI interface defined in the Bluetooth specification.

Device address

The kernel assigns the device address. The process may read the device address using an allow system call.

Advertising

For advertising, the system call interface allows a process to configure an advertising payload, advertising event type, scan response payload, interval and tx power. Permissible advertising types include:

  • Connectable undirected
  • Connectable directed
  • Non-connectable undirected
  • Scannable undirected

The driver is not responsible for validating that the payload for these advertising types follows any particular specification. Advertising event types that require particular interactions at the link-layer with peer devices (e.g. scanning or establishing connections) are not permissible:

  • Scan request
  • Scan response
  • Connect request

Scan response are sent automatically if a scan response payload is configured. Scan request and connection requests are handled by other parts of the system call interface.

To set up an advertisement:

  1. Configure the advertisement payload, type, interval, tx power and, optionally, scan response payload.

    • Advertisement payload allow
    • Advertisement type command
    • If the advertising type is scannable, you SHOULD configure a scan response payload using allow
    • Advertisement interval command
    • Advertisement tx power command
  2. Start periodic advertising using a command

Any changes to the configuration while periodic advertising is happening will take effect in a future advertising event. The kernel will use best effort to reconfigure advertising in as few events as possible.

To stop advertising

  1. Stop periodic advertising a command

Scanning

Connection-oriented communication

Hardware Interface Layer (HIL)

The Bluetooth Low Energy Radio HIL defines a cross-platform interface for interacting with on-chip BLE radios (i.e. it does not necessarily work for radios on a dedicated IC connected over a bus).

The goal of this interface is to expose low-level details of the radio that are common across platforms, except in cases where abstraction is needed for common cases to meet timing constraints.

#![allow(unused)]
fn main() {
pub trait BleRadio {
    /// Sets the channel on which to transmit or receive packets.
    ///
    /// Returns Err(ErrorCode::BUSY) if the radio is currently transmitting or
    /// receiving, otherwise Ok(()).
    fn set_channel(&self, channel: RadioChannel) -> Result<(), ErrorCode>;

    /// Sets the transmit power
    ///
    /// Returns Err(ErrorCode::BUSY) if the radio is currently transmitting or
    /// receiving, otherwise Ok(()).
    fn set_tx_power(&self, power: u8) -> Result<(), ErrorCode>;

    /// Transmits a packet over the radio
    ///
    /// Returns Err(ErrorCode::BUSY) if the radio is currently transmitting or
    /// receiving, otherwise Ok(()).
    fn transmit_packet(
        &self,
        buf: &'static mut [u8],
        disable: bool) -> Result<(), ErrorCode>;

    /// Receives a packet of at most `buf.len()` size
    ///
    /// Returns Err(ErrorCode::BUSY) if the radio is currently transmitting or
    /// receiving, otherwise Ok(()).
    fn receive_packet(&self, buf: &'static mut [u8]) -> Result<(), ErrorCode>;

    // Aborts an ongoing transmision
    //
    // Returns None if no transmission was ongoing, or the buffer that was
    // being transmitted.
    fn abort_tx(&self) -> Option<&'static mut [u8]>;

    // Aborts an ongoing reception
    //
    // Returns None if no transmission was ongoing, or the buffer that was //
    // being received into. The returned buffer may or may not have some populated
    // bytes.
    fn abort_rx(&self) -> Option<&'static mut [u8]>;

    // Disable periodic advertisements
    //
    // Returns always Ok(()) because it does not respect whether
    // the driver is actively advertising or not
    fn disable(&self) -> Result<(), ErrorCode>;
}

pub trait RxClient {
    fn receive_event(&self, buf: &'static mut [u8], len: u8, result: Result<(), ErrorCode>);
}

pub trait TxClient {
    fn transmit_event(&self, buf: &'static mut [u8], result: Result<(), ErrorCode>);
}

pub enum RadioChannel {
    DataChannel0 = 4,
    DataChannel1 = 6,
    DataChannel2 = 8,
    DataChannel3 = 10,
    DataChannel4 = 12,
    DataChannel5 = 14,
    DataChannel6 = 16,
    DataChannel7 = 18,
    DataChannel8 = 20,
    DataChannel9 = 22,
    DataChannel10 = 24,
    DataChannel11 = 28,
    DataChannel12 = 30,
    DataChannel13 = 32,
    DataChannel14 = 34,
    DataChannel15 = 36,
    DataChannel16 = 38,
    DataChannel17 = 40,
    DataChannel18 = 42,
    DataChannel19 = 44,
    DataChannel20 = 46,
    DataChannel21 = 48,
    DataChannel22 = 50,
    DataChannel23 = 52,
    DataChannel24 = 54,
    DataChannel25 = 56,
    DataChannel26 = 58,
    DataChannel27 = 60,
    DataChannel28 = 62,
    DataChannel29 = 64,
    DataChannel30 = 66,
    DataChannel31 = 68,
    DataChannel32 = 70,
    DataChannel33 = 72,
    DataChannel34 = 74,
    DataChannel35 = 76,
    DataChannel36 = 78,
    AdvertisingChannel37 = 2,
    AdvertisingChannel38 = 26,
    AdvertisingChannel39 = 80,
}
}