From 847b8be81480b249a2e8b1ff502e6188d2dadf04 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 15:25:07 +0100
Subject: [PATCH 01/13] feat/implement ble radio on nrf

---
 embassy-nrf/Cargo.toml                        |   5 +
 embassy-nrf/src/chips/nrf52805.rs             |   6 +
 embassy-nrf/src/chips/nrf52810.rs             |   6 +
 embassy-nrf/src/chips/nrf52811.rs             |   6 +
 embassy-nrf/src/chips/nrf52820.rs             |   6 +
 embassy-nrf/src/chips/nrf52832.rs             |   6 +
 embassy-nrf/src/chips/nrf52833.rs             |   6 +
 embassy-nrf/src/chips/nrf52840.rs             |   6 +
 embassy-nrf/src/chips/nrf5340_net.rs          |   6 +
 embassy-nrf/src/lib.rs                        |   6 +
 embassy-nrf/src/radio/ble.rs                  | 432 ++++++++++++++++++
 embassy-nrf/src/radio/mod.rs                  |  89 ++++
 examples/nrf52840/Cargo.toml                  |   8 +-
 .../nrf52840/src/bin/radio_ble_advertising.rs |  42 ++
 14 files changed, 628 insertions(+), 2 deletions(-)
 create mode 100644 embassy-nrf/src/radio/ble.rs
 create mode 100644 embassy-nrf/src/radio/mod.rs
 create mode 100644 examples/nrf52840/src/bin/radio_ble_advertising.rs

diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 7e161df9b..dcdc7f313 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -57,6 +57,9 @@ unstable-pac = []
 ## Enable GPIO tasks and events
 gpiote = []
 
+## Enable radio driver
+radio = ["dep:jewel"]
+
 ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
 time-driver-rtc1 = ["_time-driver"]
 
@@ -150,6 +153,8 @@ embedded-storage-async = "0.4.0"
 cfg-if = "1.0.0"
 document-features = "0.2.7"
 
+jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true }
+
 nrf51-pac = { version = "0.12.0", optional = true }
 nrf52805-pac = { version = "0.12.0", optional = true }
 nrf52810-pac = { version = "0.12.0", optional = true }
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index 624d6613d..b97c85f9e 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -129,6 +129,9 @@ embassy_hal_internal::peripherals! {
 
     // QDEC
     QDEC,
+
+    // RADIO
+    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -209,6 +212,9 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
 impl_saadc_input!(P0_04, ANALOG_INPUT2);
 impl_saadc_input!(P0_05, ANALOG_INPUT3);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index 002feab3b..03548d03f 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
+
+    // Radio
+    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -235,6 +238,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 5952907f8..992fbd129 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
+
+    // Radio
+    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -237,6 +240,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index c2f792cb9..f241f4ea3 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -130,6 +130,9 @@ embassy_hal_internal::peripherals! {
 
     // QDEC
     QDEC,
+
+    // Radio
+    RADIO,
 }
 
 impl_usb!(USBD, USBD, USBD);
@@ -224,6 +227,9 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
 impl_ppi_channel!(PPI_CH30, 30 => static);
 impl_ppi_channel!(PPI_CH31, 31 => static);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 65d52364d..6bbdd9a63 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -150,6 +150,9 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
+
+    // Radio
+    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -264,6 +267,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 7c9b66d69..e137e4dc6 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -170,6 +170,9 @@ embassy_hal_internal::peripherals! {
 
     // I2S
     I2S,
+
+    // Radio
+    RADIO,
 }
 
 impl_usb!(USBD, USBD, USBD);
@@ -306,6 +309,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 51c55cd4d..2d805f871 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -173,6 +173,9 @@ embassy_hal_internal::peripherals! {
 
     // I2S
     I2S,
+
+    // Radio
+    RADIO,
 }
 
 impl_usb!(USBD, USBD, USBD);
@@ -311,6 +314,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs
index a7cf82872..3248bde52 100644
--- a/embassy-nrf/src/chips/nrf5340_net.rs
+++ b/embassy-nrf/src/chips/nrf5340_net.rs
@@ -248,6 +248,9 @@ embassy_hal_internal::peripherals! {
     P1_13,
     P1_14,
     P1_15,
+
+    // Radio
+    RADIO,
 }
 
 impl_uarte!(SERIAL0, UARTE0, SERIAL0);
@@ -345,6 +348,9 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
 impl_ppi_channel!(PPI_CH30, 30 => configurable);
 impl_ppi_channel!(PPI_CH31, 31 => configurable);
 
+#[cfg(feature = "radio")]
+impl_radio!(RADIO, RADIO, RADIO);
+
 embassy_hal_internal::interrupt_mod!(
     CLOCK_POWER,
     RADIO,
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 358a7cc27..961928d11 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -45,6 +45,12 @@ pub mod buffered_uarte;
 pub mod gpio;
 #[cfg(feature = "gpiote")]
 pub mod gpiote;
+
+#[cfg(feature = "radio")]
+pub mod radio;
+#[cfg(all(feature = "radio", feature = "_nrf9160"))]
+compile_error!("feature `radio` is not valid for nRF91 series chips.");
+
 #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
 pub mod i2s;
 pub mod nvmc;
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
new file mode 100644
index 000000000..a5d9f447b
--- /dev/null
+++ b/embassy-nrf/src/radio/ble.rs
@@ -0,0 +1,432 @@
+//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
+//!
+//! The radio can calculate the CRC, perform data whitening,
+//! automatically send the right preamble.
+//! Most of the configuration is done automatically when you choose the mode and this driver.
+//!
+//! Some configuration can just be done when de device is disabled,
+//! and the configuration varies depending if is a transmitter or a receiver.
+//! Because of that we have a state machine to keep track of the state of the radio.
+//! The Radio is the disable radio which configure the common parameters between
+//! the bluetooth protocols, like the package format, the CRC and the whitening.
+//! The TxRadio radio enable and configured as a transmitter with the specific parameters.
+
+use core::future::poll_fn;
+use core::sync::atomic::{compiler_fence, Ordering};
+use core::task::Poll;
+
+use embassy_hal_internal::drop::OnDrop;
+use embassy_hal_internal::{into_ref, PeripheralRef};
+use jewel::phy::{
+    AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT,
+    CRC_POLY, MAX_PDU_LENGTH,
+};
+use pac::radio::mode::MODE_A as PacMode;
+use pac::radio::pcnf0::PLEN_A as PreambleLength;
+// Re-export SVD variants to allow user to directly set values.
+pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower};
+
+use crate::interrupt::typelevel::Interrupt;
+use crate::radio::*;
+use crate::util::slice_in_ram_or;
+
+/// UART error.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[non_exhaustive]
+pub enum Error {
+    /// Buffer was too long.
+    BufferTooLong,
+    /// Buffer was to short.
+    BufferTooShort,
+    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
+    BufferNotInRAM,
+}
+
+/// Radio driver.
+pub struct Radio<'d, T: Instance> {
+    _p: PeripheralRef<'d, T>,
+}
+
+impl<'d, T: Instance> Radio<'d, T> {
+    /// Create a new radio driver.
+    pub fn new(
+        radio: impl Peripheral<P = T> + 'd,
+        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
+    ) -> Self {
+        // From 5.4.1 of the nRF52840 Product Specification:
+        // > The HFXO must be running to use the RADIO or  the calibration mechanism associated with the 32.768 kHz RC oscillator.
+        // Currently the jewel crate don't implement the calibration mechanism, so we need to ensure that the HFXO is running
+        utils::check_xtal();
+
+        into_ref!(radio);
+
+        let r = T::regs();
+
+        r.pcnf1.write(|w| unsafe {
+            // It is 0 bytes long in a standard BLE packet
+            w.statlen()
+                .bits(0)
+                // MaxLen configures the maximum packet payload plus add-on size in
+                // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
+                // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
+                // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
+                // packet larger than MAXLEN, the payload will be truncated at MAXLEN
+                //
+                // To simplify the implementation, I'm setting the max length to the maximum value
+                // and I'm using only the length field to truncate the payload
+                .maxlen()
+                .bits(255)
+                // Configure the length of the address field in the packet
+                // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
+                //  The base address is truncated from the least significant byte if the BALEN is less than 4
+                //
+                // BLE address is always 4 bytes long
+                .balen()
+                .bits(3) // 3 bytes base address (+ 1 prefix);
+                // Configure the endianess
+                // For BLE is always little endian (LSB first)
+                .endian()
+                .little()
+                // Data whitening is used to avoid long sequences of zeros or
+                // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
+                // The whitener and de-whitener are defined the same way,
+                // using a 7-bit linear feedback shift register with the
+                // polynomial x7 + x4 + 1.
+                //
+                // In BLE Whitening shall be applied on the PDU and CRC of all
+                // Link Layer packets and is performed after the CRC generation
+                // in the transmitter. No other parts of the packets are whitened.
+                // De-whitening is performed before the CRC checking in the receiver
+                // Before whitening or de-whitening, the shift register should be
+                // initialized based on the channel index.
+                .whiteen()
+                .set_bit() // Enable whitening
+        });
+
+        // Configure CRC
+        r.crccnf.write(|w| {
+            // In BLE the CRC shall be calculated on the PDU of all Link Layer
+            // packets (even if the packet is encrypted).
+            // So here we skip the address field
+            w.skipaddr()
+                .skip()
+                // In BLE  24-bit CRC = 3 bytes
+                .len()
+                .three()
+        });
+
+        r.crcpoly.write(|w| unsafe {
+            // Configure the CRC polynomial
+            // Each term in the CRC polynomial is mapped to a bit in this
+            // register which index corresponds to the term's exponent.
+            // The least significant term/bit is hard-wired internally to
+            // 1, and bit number 0 of the register content is ignored by
+            // the hardware. The following example is for an 8 bit CRC
+            // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
+            w.crcpoly().bits(CRC_POLY & 0xFFFFFF)
+        });
+        // The CRC initial value varies depending of the PDU type
+
+        // Ch map between 2400 MHZ .. 2500 MHz
+        // All modes use this range
+        r.frequency.write(|w| w.map().default());
+
+        // Configure shortcuts to simplify and speed up sending and receiving packets.
+        r.shorts.write(|w| {
+            // start transmission/recv immediately after ramp-up
+            // disable radio when transmission/recv is done
+            w.ready_start().enabled().end_disable().enabled()
+        });
+
+        // Enable NVIC interrupt
+        T::Interrupt::unpend();
+        unsafe { T::Interrupt::enable() };
+
+        let mut radio = Self { _p: radio };
+
+        // set defaults
+        radio.set_mode(Mode::Ble1mbit);
+        radio.set_tx_power(0);
+        radio.set_header_size(HeaderSize::TwoBytes);
+        radio.set_access_address(ADV_ADDRESS);
+        radio.set_crc_init(ADV_CRC_INIT);
+        radio.set_channel(AdvertisingChannel::Ch39.into());
+
+        radio
+    }
+
+    #[allow(dead_code)]
+    fn trace_state(&self) {
+        let r = T::regs();
+
+        match r.state.read().state().variant().unwrap() {
+            RadioState::DISABLED => trace!("radio:state:DISABLED"),
+            RadioState::RX_RU => trace!("radio:state:RX_RU"),
+            RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
+            RadioState::RX => trace!("radio:state:RX"),
+            RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
+            RadioState::TX_RU => trace!("radio:state:TX_RU"),
+            RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
+            RadioState::TX => trace!("radio:state:TX"),
+            RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
+        }
+    }
+
+    async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce() -> ()) {
+        //self.trace_state();
+
+        let r = T::regs();
+        let s = T::state();
+
+        // If the Future is dropped before the end of the transmission
+        // we need to disable the interrupt and stop the transmission
+        // to keep the state consistent
+        let drop = OnDrop::new(|| {
+            trace!("radio drop: stopping");
+
+            r.intenclr.write(|w| w.end().clear());
+            r.events_end.reset();
+
+            r.tasks_stop.write(|w| w.tasks_stop().set_bit());
+
+            // The docs don't explicitly mention any event to acknowledge the stop task
+            // So I guess it's the same as end
+            while r.events_end.read().events_end().bit_is_clear() {}
+
+            trace!("radio drop: stopped");
+        });
+
+        /* Config interrupt */
+        // trace!("radio:enable interrupt");
+        // Clear some remnant side-effects (I'm unsure if this is needed)
+        r.events_end.reset();
+
+        // Enable interrupt
+        r.intenset.write(|w| w.end().set());
+
+        compiler_fence(Ordering::SeqCst);
+
+        // Trigger the transmission
+        trigger();
+        // self.trace_state();
+
+        // On poll check if interrupt happen
+        poll_fn(|cx| {
+            s.end_waker.register(cx.waker());
+            if r.events_end.read().events_end().bit_is_set() {
+                // trace!("radio:end");
+                return core::task::Poll::Ready(());
+            }
+            Poll::Pending
+        })
+        .await;
+
+        compiler_fence(Ordering::SeqCst);
+        r.events_disabled.reset(); // ACK
+
+        // Everthing ends fine, so we can disable the drop
+        drop.defuse();
+    }
+
+    /// Disable the radio.
+    fn disable(&mut self) {
+        let r = T::regs();
+
+        compiler_fence(Ordering::SeqCst);
+        // If is already disabled, do nothing
+        if !r.state.read().state().is_disabled() {
+            trace!("radio:disable");
+            // Trigger the disable task
+            r.tasks_disable.write(|w| w.tasks_disable().set_bit());
+
+            // Wait until the radio is disabled
+            while r.events_disabled.read().events_disabled().bit_is_clear() {}
+
+            compiler_fence(Ordering::SeqCst);
+
+            // Acknowledge it
+            r.events_disabled.reset();
+        }
+    }
+}
+
+impl<'d, T: Instance> BleRadio for Radio<'d, T> {
+    type Error = Error;
+
+    fn set_mode(&mut self, mode: Mode) {
+        let r = T::regs();
+        r.mode.write(|w| {
+            w.mode().variant(match mode {
+                Mode::Ble1mbit => PacMode::BLE_1MBIT,
+                //Mode::Ble2mbit => PacMode::BLE_2MBIT,
+            })
+        });
+
+        r.pcnf0.write(|w| {
+            w.plen().variant(match mode {
+                Mode::Ble1mbit => PreambleLength::_8BIT,
+                //Mode::Ble2mbit => PreambleLength::_16BIT,
+            })
+        });
+    }
+
+    fn set_header_size(&mut self, header_size: HeaderSize) {
+        let r = T::regs();
+
+        let s1len: u8 = match header_size {
+            HeaderSize::TwoBytes => 0,
+            HeaderSize::ThreeBytes => 8, // bits
+        };
+
+        r.pcnf0.write(|w| unsafe {
+            w
+                // Configure S0 to 1 byte length, this will represent the Data/Adv header flags
+                .s0len()
+                .set_bit()
+                // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
+                // and also be used to know how many bytes to read/write from/to the buffer
+                .lflen()
+                .bits(8)
+                // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
+                .s1len()
+                .bits(s1len)
+        });
+    }
+
+    fn set_channel(&mut self, channel: Channel) {
+        let r = T::regs();
+
+        r.frequency
+            .write(|w| unsafe { w.frequency().bits((channel.central_frequency() - 2400) as u8) });
+        r.datawhiteiv
+            .write(|w| unsafe { w.datawhiteiv().bits(channel.whitening_init()) });
+    }
+
+    fn set_access_address(&mut self, access_address: u32) {
+        let r = T::regs();
+
+        // Configure logical address
+        // The byte ordering on air is always least significant byte first for the address
+        // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
+        // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
+        r.prefix0
+            .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) });
+
+        // The base address is truncated from the least significant byte (because the BALEN is less than 4)
+        // So we need to shift the address to the right
+        r.base0.write(|w| unsafe { w.bits(access_address << 8) });
+
+        // Don't match tx address
+        r.txaddress.write(|w| unsafe { w.txaddress().bits(0) });
+
+        // Match on logical address
+        // For what I understand, this config only filter the packets
+        // by the address, so only packages send to the previous address
+        // will finish the reception
+        r.rxaddresses.write(|w| {
+            w.addr0()
+                .enabled()
+                .addr1()
+                .enabled()
+                .addr2()
+                .enabled()
+                .addr3()
+                .enabled()
+                .addr4()
+                .enabled()
+        });
+    }
+
+    fn set_crc_init(&mut self, crc_init: u32) {
+        let r = T::regs();
+
+        r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
+    }
+
+    fn set_tx_power(&mut self, power_db: i8) {
+        let r = T::regs();
+
+        let tx_power: TxPower = match power_db {
+            8..=i8::MAX => TxPower::POS8D_BM,
+            7 => TxPower::POS7D_BM,
+            6 => TxPower::POS6D_BM,
+            5 => TxPower::POS5D_BM,
+            4 => TxPower::POS4D_BM,
+            3 => TxPower::POS3D_BM,
+            1..=2 => TxPower::POS2D_BM,
+            -3..=0 => TxPower::_0D_BM,
+            -7..=-4 => TxPower::NEG4D_BM,
+            -11..=-8 => TxPower::NEG8D_BM,
+            -15..=-12 => TxPower::NEG12D_BM,
+            -19..=-16 => TxPower::NEG16D_BM,
+            -29..=-20 => TxPower::NEG20D_BM,
+            -39..=-30 => TxPower::NEG30D_BM,
+            i8::MIN..=-40 => TxPower::NEG40D_BM,
+        };
+
+        r.txpower.write(|w| w.txpower().variant(tx_power));
+    }
+
+    fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
+        // Because we are serializing the buffer, we should always have the buffer in RAM
+        slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
+
+        if buffer.len() > MAX_PDU_LENGTH {
+            return Err(Error::BufferTooLong);
+        }
+
+        let r = T::regs();
+
+        // Here we are considering that the length of the packet is
+        // correctly set in the buffer, otherwise we will sending
+        // unowned regions of memory
+        let ptr = buffer.as_ptr();
+
+        // Configure the payload
+        r.packetptr.write(|w| unsafe { w.bits(ptr as u32) });
+
+        Ok(())
+    }
+
+    /// Send packet
+    async fn transmit(&mut self) {
+        let r = T::regs();
+
+        self.trigger_and_wait_end(move || {
+            // Initialize the transmission
+            // trace!("txen");
+            r.tasks_txen.write(|w| w.tasks_txen().set_bit());
+        })
+        .await;
+    }
+
+    /// Send packet
+    async fn receive(&mut self) {
+        let r = T::regs();
+
+        self.trigger_and_wait_end(move || {
+            // Initialize the transmission
+            // trace!("rxen");
+            r.tasks_rxen.write(|w| w.tasks_rxen().set_bit());
+
+            // Await until ready
+            while r.events_ready.read().events_ready().bit_is_clear() {}
+
+            compiler_fence(Ordering::SeqCst);
+
+            // Acknowledge it
+            r.events_ready.reset();
+
+            // trace!("radio:start");
+            r.tasks_start.write(|w| w.tasks_start().set_bit());
+        })
+        .await;
+    }
+}
+
+impl<'d, T: Instance> Drop for Radio<'d, T> {
+    fn drop(&mut self) {
+        self.disable();
+    }
+}
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs
new file mode 100644
index 000000000..91cc2c0a7
--- /dev/null
+++ b/embassy-nrf/src/radio/mod.rs
@@ -0,0 +1,89 @@
+//! Integrated 2.4 GHz Radio
+//!
+//! The 2.4 GHz radio transceiver is compatible with multiple radio standards
+//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy.
+
+#![macro_use]
+
+/// Bluetooth Low Energy Radio driver.
+pub mod ble;
+
+use core::marker::PhantomData;
+
+use crate::{interrupt, pac, Peripheral};
+
+/// Interrupt handler
+pub struct InterruptHandler<T: Instance> {
+    _phantom: PhantomData<T>,
+}
+
+impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
+    unsafe fn on_interrupt() {
+        let r = T::regs();
+        let s = T::state();
+
+        if r.events_end.read().events_end().bit_is_set() {
+            s.end_waker.wake();
+            r.intenclr.write(|w| w.end().clear());
+        }
+    }
+}
+
+pub(crate) mod utils {
+    use super::*;
+
+    // Check if the HFCLK is XTAL is enabled
+    pub fn check_xtal() {
+        // safe: only reading the value
+        let is_xtal = unsafe {
+            let r = &*pac::CLOCK::ptr();
+            r.hfclkstat.read().src().is_xtal()
+        };
+        assert!(is_xtal, "HFCLK must be XTAL");
+    }
+}
+
+pub(crate) mod sealed {
+    use embassy_sync::waitqueue::AtomicWaker;
+
+    pub struct State {
+        /// end packet transmission or reception
+        pub end_waker: AtomicWaker,
+    }
+    impl State {
+        pub const fn new() -> Self {
+            Self {
+                end_waker: AtomicWaker::new(),
+            }
+        }
+    }
+
+    pub trait Instance {
+        fn regs() -> &'static crate::pac::radio::RegisterBlock;
+        fn state() -> &'static State;
+    }
+}
+
+macro_rules! impl_radio {
+    ($type:ident, $pac_type:ident, $irq:ident) => {
+        impl crate::radio::sealed::Instance for peripherals::$type {
+            fn regs() -> &'static pac::radio::RegisterBlock {
+                unsafe { &*pac::$pac_type::ptr() }
+            }
+
+            fn state() -> &'static crate::radio::sealed::State {
+                static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new();
+                &STATE
+            }
+        }
+        impl crate::radio::Instance for peripherals::$type {
+            type Interrupt = crate::interrupt::typelevel::$irq;
+        }
+    };
+}
+
+/// Radio peripheral instance.
+pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
+    /// Interrupt for this peripheral.
+    type Interrupt: interrupt::typelevel::Interrupt;
+}
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index abb995be6..0239583cd 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
 embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
-embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
-embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
+embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] }
+embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]}
 embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
 embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 embedded-io = { version = "0.6.0", features = ["defmt-03"]  }
@@ -35,6 +35,10 @@ embedded-hal-async = { version = "1.0" }
 embedded-hal-bus = { version = "0.1", features = ["async"] }
 num-integer = { version = "0.1.45", default-features = false }
 microfft = "0.5.0"
+jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"}
+
+[patch.crates-io]
+embassy-time = { version = "0.3.0", path = "../../embassy-time"}
 
 [profile.release]
 debug = 2
diff --git a/examples/nrf52840/src/bin/radio_ble_advertising.rs b/examples/nrf52840/src/bin/radio_ble_advertising.rs
new file mode 100644
index 000000000..8898c2418
--- /dev/null
+++ b/examples/nrf52840/src/bin/radio_ble_advertising.rs
@@ -0,0 +1,42 @@
+#![no_std]
+#![no_main]
+
+use defmt::{info, unwrap};
+use embassy_executor::Spawner;
+use embassy_nrf::{bind_interrupts, peripherals, radio};
+use embassy_time::Timer;
+use jewel::phy::Radio;
+use {defmt_rtt as _, panic_probe as _};
+
+bind_interrupts!(struct Irqs {
+    RADIO => radio::InterruptHandler<peripherals::RADIO>;
+});
+
+// For a high-level API look on jewel examples
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = embassy_nrf::config::Config::default();
+    config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal;
+    let p = embassy_nrf::init(config);
+
+    info!("Starting BLE radio");
+    let mut radio = radio::ble::Radio::new(p.RADIO, Irqs);
+
+    let pdu = [
+        0x46u8, // ADV_NONCONN_IND, Random address,
+        0x18,   // Length of payload
+        0x27, 0xdc, 0xd0, 0xe8, 0xe1, 0xff, // Adress
+        0x02, 0x01, 0x06, // Flags
+        0x03, 0x03, 0x09, 0x18, // Complete list of 16-bit UUIDs available
+        0x0A, 0x09, // Length, Type: Device name
+        b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't',
+    ];
+
+    unwrap!(radio.set_buffer(pdu.as_ref()));
+
+    loop {
+        info!("Sending packet");
+        radio.transmit().await;
+        Timer::after_millis(500).await;
+    }
+}

From d408056a66be2183dbcde6840e69e53beb4a764b Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 17:16:46 +0100
Subject: [PATCH 02/13] remove default on radio

---
 embassy-nrf/src/radio/ble.rs | 13 +------------
 1 file changed, 1 insertion(+), 12 deletions(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index a5d9f447b..64428fff5 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -17,10 +17,7 @@ use core::task::Poll;
 
 use embassy_hal_internal::drop::OnDrop;
 use embassy_hal_internal::{into_ref, PeripheralRef};
-use jewel::phy::{
-    AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT,
-    CRC_POLY, MAX_PDU_LENGTH,
-};
+use jewel::phy::{Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, CRC_POLY, MAX_PDU_LENGTH};
 use pac::radio::mode::MODE_A as PacMode;
 use pac::radio::pcnf0::PLEN_A as PreambleLength;
 // Re-export SVD variants to allow user to directly set values.
@@ -145,14 +142,6 @@ impl<'d, T: Instance> Radio<'d, T> {
 
         let mut radio = Self { _p: radio };
 
-        // set defaults
-        radio.set_mode(Mode::Ble1mbit);
-        radio.set_tx_power(0);
-        radio.set_header_size(HeaderSize::TwoBytes);
-        radio.set_access_address(ADV_ADDRESS);
-        radio.set_crc_init(ADV_CRC_INIT);
-        radio.set_channel(AdvertisingChannel::Ch39.into());
-
         radio
     }
 

From 5f1b80d40b6d2ae1636fa46e8e5334c03adeb67d Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 18:04:29 +0100
Subject: [PATCH 03/13] remove jewel dependency

---
 embassy-nrf/Cargo.toml                        |   4 +-
 embassy-nrf/src/radio/ble.rs                  | 160 ++++++++++--------
 embassy-nrf/src/radio/mod.rs                  |  14 --
 examples/nrf52840/Cargo.toml                  |   4 -
 .../nrf52840/src/bin/radio_ble_advertising.rs |  42 -----
 5 files changed, 86 insertions(+), 138 deletions(-)
 delete mode 100644 examples/nrf52840/src/bin/radio_ble_advertising.rs

diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index dcdc7f313..9f35bd241 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -58,7 +58,7 @@ unstable-pac = []
 gpiote = []
 
 ## Enable radio driver
-radio = ["dep:jewel"]
+radio = []
 
 ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
 time-driver-rtc1 = ["_time-driver"]
@@ -153,8 +153,6 @@ embedded-storage-async = "0.4.0"
 cfg-if = "1.0.0"
 document-features = "0.2.7"
 
-jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true }
-
 nrf51-pac = { version = "0.12.0", optional = true }
 nrf52805-pac = { version = "0.12.0", optional = true }
 nrf52810-pac = { version = "0.12.0", optional = true }
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 64428fff5..def941796 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -17,11 +17,10 @@ use core::task::Poll;
 
 use embassy_hal_internal::drop::OnDrop;
 use embassy_hal_internal::{into_ref, PeripheralRef};
-use jewel::phy::{Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, CRC_POLY, MAX_PDU_LENGTH};
-use pac::radio::mode::MODE_A as PacMode;
+pub use pac::radio::mode::MODE_A as Mode;
 use pac::radio::pcnf0::PLEN_A as PreambleLength;
-// Re-export SVD variants to allow user to directly set values.
-pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower};
+use pac::radio::state::STATE_A as RadioState;
+pub use pac::radio::txpower::TXPOWER_A as TxPower;
 
 use crate::interrupt::typelevel::Interrupt;
 use crate::radio::*;
@@ -51,11 +50,6 @@ impl<'d, T: Instance> Radio<'d, T> {
         radio: impl Peripheral<P = T> + 'd,
         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
     ) -> Self {
-        // From 5.4.1 of the nRF52840 Product Specification:
-        // > The HFXO must be running to use the RADIO or  the calibration mechanism associated with the 32.768 kHz RC oscillator.
-        // Currently the jewel crate don't implement the calibration mechanism, so we need to ensure that the HFXO is running
-        utils::check_xtal();
-
         into_ref!(radio);
 
         let r = T::regs();
@@ -113,18 +107,6 @@ impl<'d, T: Instance> Radio<'d, T> {
                 .three()
         });
 
-        r.crcpoly.write(|w| unsafe {
-            // Configure the CRC polynomial
-            // Each term in the CRC polynomial is mapped to a bit in this
-            // register which index corresponds to the term's exponent.
-            // The least significant term/bit is hard-wired internally to
-            // 1, and bit number 0 of the register content is ignored by
-            // the hardware. The following example is for an 8 bit CRC
-            // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
-            w.crcpoly().bits(CRC_POLY & 0xFFFFFF)
-        });
-        // The CRC initial value varies depending of the PDU type
-
         // Ch map between 2400 MHZ .. 2500 MHz
         // All modes use this range
         r.frequency.write(|w| w.map().default());
@@ -140,9 +122,7 @@ impl<'d, T: Instance> Radio<'d, T> {
         T::Interrupt::unpend();
         unsafe { T::Interrupt::enable() };
 
-        let mut radio = Self { _p: radio };
-
-        radio
+        Self { _p: radio }
     }
 
     #[allow(dead_code)]
@@ -186,7 +166,6 @@ impl<'d, T: Instance> Radio<'d, T> {
             trace!("radio drop: stopped");
         });
 
-        /* Config interrupt */
         // trace!("radio:enable interrupt");
         // Clear some remnant side-effects (I'm unsure if this is needed)
         r.events_end.reset();
@@ -238,34 +217,34 @@ impl<'d, T: Instance> Radio<'d, T> {
             r.events_disabled.reset();
         }
     }
-}
 
-impl<'d, T: Instance> BleRadio for Radio<'d, T> {
-    type Error = Error;
-
-    fn set_mode(&mut self, mode: Mode) {
+    /// Set the radio mode
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_mode(&mut self, mode: Mode) {
         let r = T::regs();
-        r.mode.write(|w| {
-            w.mode().variant(match mode {
-                Mode::Ble1mbit => PacMode::BLE_1MBIT,
-                //Mode::Ble2mbit => PacMode::BLE_2MBIT,
-            })
-        });
+        r.mode.write(|w| w.mode().variant(mode));
 
         r.pcnf0.write(|w| {
             w.plen().variant(match mode {
-                Mode::Ble1mbit => PreambleLength::_8BIT,
-                //Mode::Ble2mbit => PreambleLength::_16BIT,
+                Mode::BLE_1MBIT => PreambleLength::_8BIT,
+                Mode::BLE_2MBIT => PreambleLength::_16BIT,
+                Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE,
+                _ => unimplemented!(),
             })
         });
     }
 
-    fn set_header_size(&mut self, header_size: HeaderSize) {
+    /// Set the header size changing the S1 field
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_header_expansion(&mut self, use_s1_field: bool) {
         let r = T::regs();
 
-        let s1len: u8 = match header_size {
-            HeaderSize::TwoBytes => 0,
-            HeaderSize::ThreeBytes => 8, // bits
+        // s1 len in bits
+        let s1len: u8 = match use_s1_field {
+            false => 0,
+            true => 8,
         };
 
         r.pcnf0.write(|w| unsafe {
@@ -283,16 +262,36 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> {
         });
     }
 
-    fn set_channel(&mut self, channel: Channel) {
+    /// Set initial data whitening value
+    /// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream
+    /// On BLE the initial value is the channel index | 0x40
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_whitening_init(&mut self, whitening_init: u8) {
+        let r = T::regs();
+
+        r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) });
+    }
+
+    /// Set the central frequency to be used
+    /// It should be in the range 2400..2500
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_frequency(&mut self, frequency: u32) {
+        assert!(2400 <= frequency && frequency <= 2500);
         let r = T::regs();
 
         r.frequency
-            .write(|w| unsafe { w.frequency().bits((channel.central_frequency() - 2400) as u8) });
-        r.datawhiteiv
-            .write(|w| unsafe { w.datawhiteiv().bits(channel.whitening_init()) });
+            .write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) });
     }
 
-    fn set_access_address(&mut self, access_address: u32) {
+    /// Set the acess address
+    /// This address is always constants for advertising
+    /// And a random value generate on each connection
+    /// It is used to filter the packages
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_access_address(&mut self, access_address: u32) {
         let r = T::regs();
 
         // Configure logical address
@@ -327,44 +326,55 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> {
         });
     }
 
-    fn set_crc_init(&mut self, crc_init: u32) {
+    /// Set the CRC polynomial
+    /// It only uses the 24 least significant bits
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_crc_poly(&mut self, crc_poly: u32) {
+        let r = T::regs();
+
+        r.crcpoly.write(|w| unsafe {
+            // Configure the CRC polynomial
+            // Each term in the CRC polynomial is mapped to a bit in this
+            // register which index corresponds to the term's exponent.
+            // The least significant term/bit is hard-wired internally to
+            // 1, and bit number 0 of the register content is ignored by
+            // the hardware. The following example is for an 8 bit CRC
+            // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
+            w.crcpoly().bits(crc_poly & 0xFFFFFF)
+        });
+    }
+
+    /// Set the CRC init value
+    /// It only uses the 24 least significant bits
+    /// The CRC initial value varies depending of the PDU type
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_crc_init(&mut self, crc_init: u32) {
         let r = T::regs();
 
         r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
     }
 
-    fn set_tx_power(&mut self, power_db: i8) {
+    /// Set the radio tx power
+    ///
+    /// The radio must be disabled before calling this function
+    pub fn set_tx_power(&mut self, tx_power: TxPower) {
         let r = T::regs();
 
-        let tx_power: TxPower = match power_db {
-            8..=i8::MAX => TxPower::POS8D_BM,
-            7 => TxPower::POS7D_BM,
-            6 => TxPower::POS6D_BM,
-            5 => TxPower::POS5D_BM,
-            4 => TxPower::POS4D_BM,
-            3 => TxPower::POS3D_BM,
-            1..=2 => TxPower::POS2D_BM,
-            -3..=0 => TxPower::_0D_BM,
-            -7..=-4 => TxPower::NEG4D_BM,
-            -11..=-8 => TxPower::NEG8D_BM,
-            -15..=-12 => TxPower::NEG12D_BM,
-            -19..=-16 => TxPower::NEG16D_BM,
-            -29..=-20 => TxPower::NEG20D_BM,
-            -39..=-30 => TxPower::NEG30D_BM,
-            i8::MIN..=-40 => TxPower::NEG40D_BM,
-        };
-
         r.txpower.write(|w| w.txpower().variant(tx_power));
     }
 
-    fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
+    /// Set buffer to read/write
+    ///
+    /// This method is unsound. You should guarantee that the buffer will live
+    /// for the life time of the transmission or if the buffer will be modified.
+    /// Also if the buffer is smaller than the packet length, the radio will
+    /// read/write memory out of the buffer bounds.
+    pub fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
         // Because we are serializing the buffer, we should always have the buffer in RAM
         slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
 
-        if buffer.len() > MAX_PDU_LENGTH {
-            return Err(Error::BufferTooLong);
-        }
-
         let r = T::regs();
 
         // Here we are considering that the length of the packet is
@@ -379,7 +389,7 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> {
     }
 
     /// Send packet
-    async fn transmit(&mut self) {
+    pub async fn transmit(&mut self) {
         let r = T::regs();
 
         self.trigger_and_wait_end(move || {
@@ -390,8 +400,8 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> {
         .await;
     }
 
-    /// Send packet
-    async fn receive(&mut self) {
+    /// Receive packet
+    pub async fn receive(&mut self) {
         let r = T::regs();
 
         self.trigger_and_wait_end(move || {
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs
index 91cc2c0a7..03f967f87 100644
--- a/embassy-nrf/src/radio/mod.rs
+++ b/embassy-nrf/src/radio/mod.rs
@@ -29,20 +29,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
     }
 }
 
-pub(crate) mod utils {
-    use super::*;
-
-    // Check if the HFCLK is XTAL is enabled
-    pub fn check_xtal() {
-        // safe: only reading the value
-        let is_xtal = unsafe {
-            let r = &*pac::CLOCK::ptr();
-            r.hfclkstat.read().src().is_xtal()
-        };
-        assert!(is_xtal, "HFCLK must be XTAL");
-    }
-}
-
 pub(crate) mod sealed {
     use embassy_sync::waitqueue::AtomicWaker;
 
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 0239583cd..78dabe347 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -35,10 +35,6 @@ embedded-hal-async = { version = "1.0" }
 embedded-hal-bus = { version = "0.1", features = ["async"] }
 num-integer = { version = "0.1.45", default-features = false }
 microfft = "0.5.0"
-jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"}
-
-[patch.crates-io]
-embassy-time = { version = "0.3.0", path = "../../embassy-time"}
 
 [profile.release]
 debug = 2
diff --git a/examples/nrf52840/src/bin/radio_ble_advertising.rs b/examples/nrf52840/src/bin/radio_ble_advertising.rs
deleted file mode 100644
index 8898c2418..000000000
--- a/examples/nrf52840/src/bin/radio_ble_advertising.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-#![no_std]
-#![no_main]
-
-use defmt::{info, unwrap};
-use embassy_executor::Spawner;
-use embassy_nrf::{bind_interrupts, peripherals, radio};
-use embassy_time::Timer;
-use jewel::phy::Radio;
-use {defmt_rtt as _, panic_probe as _};
-
-bind_interrupts!(struct Irqs {
-    RADIO => radio::InterruptHandler<peripherals::RADIO>;
-});
-
-// For a high-level API look on jewel examples
-#[embassy_executor::main]
-async fn main(_spawner: Spawner) {
-    let mut config = embassy_nrf::config::Config::default();
-    config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal;
-    let p = embassy_nrf::init(config);
-
-    info!("Starting BLE radio");
-    let mut radio = radio::ble::Radio::new(p.RADIO, Irqs);
-
-    let pdu = [
-        0x46u8, // ADV_NONCONN_IND, Random address,
-        0x18,   // Length of payload
-        0x27, 0xdc, 0xd0, 0xe8, 0xe1, 0xff, // Adress
-        0x02, 0x01, 0x06, // Flags
-        0x03, 0x03, 0x09, 0x18, // Complete list of 16-bit UUIDs available
-        0x0A, 0x09, // Length, Type: Device name
-        b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't',
-    ];
-
-    unwrap!(radio.set_buffer(pdu.as_ref()));
-
-    loop {
-        info!("Sending packet");
-        radio.transmit().await;
-        Timer::after_millis(500).await;
-    }
-}

From add78943146cb929d51830c1f1c74c51dd3a5d57 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 18:09:10 +0100
Subject: [PATCH 04/13] remove radio feature

---
 embassy-nrf/Cargo.toml               |  3 ---
 embassy-nrf/src/chips/nrf52805.rs    |  1 -
 embassy-nrf/src/chips/nrf52810.rs    |  1 -
 embassy-nrf/src/chips/nrf52811.rs    |  1 -
 embassy-nrf/src/chips/nrf52820.rs    |  1 -
 embassy-nrf/src/chips/nrf52832.rs    |  1 -
 embassy-nrf/src/chips/nrf52833.rs    |  1 -
 embassy-nrf/src/chips/nrf52840.rs    |  1 -
 embassy-nrf/src/chips/nrf5340_net.rs |  1 -
 embassy-nrf/src/lib.rs               | 13 ++++++++++---
 examples/nrf52840/Cargo.toml         |  4 ++--
 11 files changed, 12 insertions(+), 16 deletions(-)

diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 9f35bd241..7e161df9b 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -57,9 +57,6 @@ unstable-pac = []
 ## Enable GPIO tasks and events
 gpiote = []
 
-## Enable radio driver
-radio = []
-
 ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
 time-driver-rtc1 = ["_time-driver"]
 
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index b97c85f9e..c172dd107 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -212,7 +212,6 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
 impl_saadc_input!(P0_04, ANALOG_INPUT2);
 impl_saadc_input!(P0_05, ANALOG_INPUT3);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index 03548d03f..c607586db 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -238,7 +238,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 992fbd129..5f70365b4 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -240,7 +240,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index f241f4ea3..82d097407 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -227,7 +227,6 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
 impl_ppi_channel!(PPI_CH30, 30 => static);
 impl_ppi_channel!(PPI_CH31, 31 => static);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 6bbdd9a63..67b32fe5f 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -267,7 +267,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index e137e4dc6..20f14e2d6 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -309,7 +309,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 2d805f871..d3272b2e8 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -314,7 +314,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs
index 3248bde52..65e8f9653 100644
--- a/embassy-nrf/src/chips/nrf5340_net.rs
+++ b/embassy-nrf/src/chips/nrf5340_net.rs
@@ -348,7 +348,6 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
 impl_ppi_channel!(PPI_CH30, 30 => configurable);
 impl_ppi_channel!(PPI_CH31, 31 => configurable);
 
-#[cfg(feature = "radio")]
 impl_radio!(RADIO, RADIO, RADIO);
 
 embassy_hal_internal::interrupt_mod!(
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 961928d11..0f64f30f2 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -46,10 +46,17 @@ pub mod gpio;
 #[cfg(feature = "gpiote")]
 pub mod gpiote;
 
-#[cfg(feature = "radio")]
+#[cfg(any(
+    feature = "nrf52805",
+    feature = "nrf52810",
+    feature = "nrf52811",
+    feature = "nrf52820",
+    feature = "nrf52832",
+    feature = "nrf52833",
+    feature = "nrf52840",
+    feature = "_nrf5340-net"
+))]
 pub mod radio;
-#[cfg(all(feature = "radio", feature = "_nrf9160"))]
-compile_error!("feature `radio` is not valid for nRF91 series chips.");
 
 #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
 pub mod i2s;
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 78dabe347..abb995be6 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
 embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
-embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] }
-embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]}
+embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
+embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
 embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
 embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 embedded-io = { version = "0.6.0", features = ["defmt-03"]  }

From 3ad45655ecea36d54f8024b5fbdad461dc07ed69 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 18:22:47 +0100
Subject: [PATCH 05/13] ci rerun

---
 embassy-nrf/src/radio/ble.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index def941796..369b49c55 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -35,7 +35,7 @@ pub enum Error {
     BufferTooLong,
     /// Buffer was to short.
     BufferTooShort,
-    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
+    /// The buffer is not in data RAM. It is most likely in flash, and nRF's DMA cannot access flash.
     BufferNotInRAM,
 }
 

From ea8bfb4f382bddda2ca5fbe5ce4c595cd0421bd1 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 18:25:06 +0100
Subject: [PATCH 06/13] remove some supports

---
 embassy-nrf/src/lib.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 0f64f30f2..767293230 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -47,11 +47,11 @@ pub mod gpio;
 pub mod gpiote;
 
 #[cfg(any(
-    feature = "nrf52805",
-    feature = "nrf52810",
+    //feature = "nrf52805",
+    //feature = "nrf52810",
     feature = "nrf52811",
     feature = "nrf52820",
-    feature = "nrf52832",
+    //feature = "nrf52832",
     feature = "nrf52833",
     feature = "nrf52840",
     feature = "_nrf5340-net"

From 9527d1d934a46a50bfc8f26bd942c6f6e5c5b31b Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 7 Feb 2024 18:31:05 +0100
Subject: [PATCH 07/13] remove radio implementation on chips not tested

---
 embassy-nrf/src/chips/nrf52805.rs    |  5 -----
 embassy-nrf/src/chips/nrf52810.rs    |  5 -----
 embassy-nrf/src/chips/nrf52811.rs    |  5 -----
 embassy-nrf/src/chips/nrf52820.rs    |  5 -----
 embassy-nrf/src/chips/nrf52832.rs    |  5 -----
 embassy-nrf/src/chips/nrf52833.rs    |  5 -----
 embassy-nrf/src/chips/nrf5340_net.rs |  5 -----
 embassy-nrf/src/lib.rs               | 11 +----------
 8 files changed, 1 insertion(+), 45 deletions(-)

diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index c172dd107..624d6613d 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -129,9 +129,6 @@ embassy_hal_internal::peripherals! {
 
     // QDEC
     QDEC,
-
-    // RADIO
-    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -212,8 +209,6 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
 impl_saadc_input!(P0_04, ANALOG_INPUT2);
 impl_saadc_input!(P0_05, ANALOG_INPUT3);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index c607586db..002feab3b 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
-
-    // Radio
-    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -238,8 +235,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 5f70365b4..5952907f8 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
-
-    // Radio
-    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -240,8 +237,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
 impl_saadc_input!(P0_30, ANALOG_INPUT6);
 impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index 82d097407..c2f792cb9 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -130,9 +130,6 @@ embassy_hal_internal::peripherals! {
 
     // QDEC
     QDEC,
-
-    // Radio
-    RADIO,
 }
 
 impl_usb!(USBD, USBD, USBD);
@@ -227,8 +224,6 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
 impl_ppi_channel!(PPI_CH30, 30 => static);
 impl_ppi_channel!(PPI_CH31, 31 => static);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 67b32fe5f..65d52364d 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -150,9 +150,6 @@ embassy_hal_internal::peripherals! {
 
     // PDM
     PDM,
-
-    // Radio
-    RADIO,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -267,8 +264,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 20f14e2d6..7c9b66d69 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -170,9 +170,6 @@ embassy_hal_internal::peripherals! {
 
     // I2S
     I2S,
-
-    // Radio
-    RADIO,
 }
 
 impl_usb!(USBD, USBD, USBD);
@@ -309,8 +306,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
 
 impl_i2s!(I2S, I2S, I2S);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     POWER_CLOCK,
     RADIO,
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs
index 65e8f9653..a7cf82872 100644
--- a/embassy-nrf/src/chips/nrf5340_net.rs
+++ b/embassy-nrf/src/chips/nrf5340_net.rs
@@ -248,9 +248,6 @@ embassy_hal_internal::peripherals! {
     P1_13,
     P1_14,
     P1_15,
-
-    // Radio
-    RADIO,
 }
 
 impl_uarte!(SERIAL0, UARTE0, SERIAL0);
@@ -348,8 +345,6 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
 impl_ppi_channel!(PPI_CH30, 30 => configurable);
 impl_ppi_channel!(PPI_CH31, 31 => configurable);
 
-impl_radio!(RADIO, RADIO, RADIO);
-
 embassy_hal_internal::interrupt_mod!(
     CLOCK_POWER,
     RADIO,
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 767293230..5dba6f975 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -46,16 +46,7 @@ pub mod gpio;
 #[cfg(feature = "gpiote")]
 pub mod gpiote;
 
-#[cfg(any(
-    //feature = "nrf52805",
-    //feature = "nrf52810",
-    feature = "nrf52811",
-    feature = "nrf52820",
-    //feature = "nrf52832",
-    feature = "nrf52833",
-    feature = "nrf52840",
-    feature = "_nrf5340-net"
-))]
+#[cfg(any(feature = "nrf52840"))] // needs to be tested on other chips
 pub mod radio;
 
 #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]

From 802bdd1af86829482d6b847f73f02e6973bb68ea Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Sat, 10 Feb 2024 08:09:08 +0100
Subject: [PATCH 08/13] remove first person comments and assert disable state
 when it's necessary

---
 embassy-nrf/src/radio/ble.rs | 201 ++++++++++++++++++-----------------
 1 file changed, 106 insertions(+), 95 deletions(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 369b49c55..b0d374579 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -64,13 +64,13 @@ impl<'d, T: Instance> Radio<'d, T> {
                 // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
                 // packet larger than MAXLEN, the payload will be truncated at MAXLEN
                 //
-                // To simplify the implementation, I'm setting the max length to the maximum value
-                // and I'm using only the length field to truncate the payload
+                // To simplify the implementation, It is setted as the maximum value
+                // and the length of the packet is controlled only by the LENGTH field in the packet
                 .maxlen()
                 .bits(255)
                 // Configure the length of the address field in the packet
                 // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
-                //  The base address is truncated from the least significant byte if the BALEN is less than 4
+                // The base address is truncated from the least significant byte if the BALEN is less than 4
                 //
                 // BLE address is always 4 bytes long
                 .balen()
@@ -92,14 +92,14 @@ impl<'d, T: Instance> Radio<'d, T> {
                 // Before whitening or de-whitening, the shift register should be
                 // initialized based on the channel index.
                 .whiteen()
-                .set_bit() // Enable whitening
+                .set_bit()
         });
 
         // Configure CRC
         r.crccnf.write(|w| {
             // In BLE the CRC shall be calculated on the PDU of all Link Layer
             // packets (even if the packet is encrypted).
-            // So here we skip the address field
+            // It skips the address field
             w.skipaddr()
                 .skip()
                 // In BLE  24-bit CRC = 3 bytes
@@ -125,11 +125,18 @@ impl<'d, T: Instance> Radio<'d, T> {
         Self { _p: radio }
     }
 
+    fn state(&self) -> RadioState {
+        match T::regs().state.read().state().variant() {
+            Ok(s) => s,
+            None => unreachable!(),
+        }
+    }
+
     #[allow(dead_code)]
     fn trace_state(&self) {
         let r = T::regs();
 
-        match r.state.read().state().variant().unwrap() {
+        match self.state() {
             RadioState::DISABLED => trace!("radio:state:DISABLED"),
             RadioState::RX_RU => trace!("radio:state:RX_RU"),
             RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
@@ -142,86 +149,12 @@ impl<'d, T: Instance> Radio<'d, T> {
         }
     }
 
-    async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce() -> ()) {
-        //self.trace_state();
-
-        let r = T::regs();
-        let s = T::state();
-
-        // If the Future is dropped before the end of the transmission
-        // we need to disable the interrupt and stop the transmission
-        // to keep the state consistent
-        let drop = OnDrop::new(|| {
-            trace!("radio drop: stopping");
-
-            r.intenclr.write(|w| w.end().clear());
-            r.events_end.reset();
-
-            r.tasks_stop.write(|w| w.tasks_stop().set_bit());
-
-            // The docs don't explicitly mention any event to acknowledge the stop task
-            // So I guess it's the same as end
-            while r.events_end.read().events_end().bit_is_clear() {}
-
-            trace!("radio drop: stopped");
-        });
-
-        // trace!("radio:enable interrupt");
-        // Clear some remnant side-effects (I'm unsure if this is needed)
-        r.events_end.reset();
-
-        // Enable interrupt
-        r.intenset.write(|w| w.end().set());
-
-        compiler_fence(Ordering::SeqCst);
-
-        // Trigger the transmission
-        trigger();
-        // self.trace_state();
-
-        // On poll check if interrupt happen
-        poll_fn(|cx| {
-            s.end_waker.register(cx.waker());
-            if r.events_end.read().events_end().bit_is_set() {
-                // trace!("radio:end");
-                return core::task::Poll::Ready(());
-            }
-            Poll::Pending
-        })
-        .await;
-
-        compiler_fence(Ordering::SeqCst);
-        r.events_disabled.reset(); // ACK
-
-        // Everthing ends fine, so we can disable the drop
-        drop.defuse();
-    }
-
-    /// Disable the radio.
-    fn disable(&mut self) {
-        let r = T::regs();
-
-        compiler_fence(Ordering::SeqCst);
-        // If is already disabled, do nothing
-        if !r.state.read().state().is_disabled() {
-            trace!("radio:disable");
-            // Trigger the disable task
-            r.tasks_disable.write(|w| w.tasks_disable().set_bit());
-
-            // Wait until the radio is disabled
-            while r.events_disabled.read().events_disabled().bit_is_clear() {}
-
-            compiler_fence(Ordering::SeqCst);
-
-            // Acknowledge it
-            r.events_disabled.reset();
-        }
-    }
-
     /// Set the radio mode
     ///
     /// The radio must be disabled before calling this function
     pub fn set_mode(&mut self, mode: Mode) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
         r.mode.write(|w| w.mode().variant(mode));
 
@@ -235,10 +168,12 @@ impl<'d, T: Instance> Radio<'d, T> {
         });
     }
 
-    /// Set the header size changing the S1 field
+    /// Set the header size changing the S1's len field
     ///
     /// The radio must be disabled before calling this function
     pub fn set_header_expansion(&mut self, use_s1_field: bool) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         // s1 len in bits
@@ -268,6 +203,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     ///
     /// The radio must be disabled before calling this function
     pub fn set_whitening_init(&mut self, whitening_init: u8) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) });
@@ -276,9 +213,11 @@ impl<'d, T: Instance> Radio<'d, T> {
     /// Set the central frequency to be used
     /// It should be in the range 2400..2500
     ///
-    /// The radio must be disabled before calling this function
+    /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
     pub fn set_frequency(&mut self, frequency: u32) {
+        assert!(self.state() == RadioState::DISABLED);
         assert!(2400 <= frequency && frequency <= 2500);
+
         let r = T::regs();
 
         r.frequency
@@ -292,6 +231,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     ///
     /// The radio must be disabled before calling this function
     pub fn set_access_address(&mut self, access_address: u32) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         // Configure logical address
@@ -309,9 +250,9 @@ impl<'d, T: Instance> Radio<'d, T> {
         r.txaddress.write(|w| unsafe { w.txaddress().bits(0) });
 
         // Match on logical address
-        // For what I understand, this config only filter the packets
-        // by the address, so only packages send to the previous address
-        // will finish the reception
+        // This config only filter the packets by the address,
+        // so only packages send to the previous address
+        // will finish the reception (TODO: check the explanation)
         r.rxaddresses.write(|w| {
             w.addr0()
                 .enabled()
@@ -331,6 +272,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     ///
     /// The radio must be disabled before calling this function
     pub fn set_crc_poly(&mut self, crc_poly: u32) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         r.crcpoly.write(|w| unsafe {
@@ -351,6 +294,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     ///
     /// The radio must be disabled before calling this function
     pub fn set_crc_init(&mut self, crc_init: u32) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
@@ -360,6 +305,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     ///
     /// The radio must be disabled before calling this function
     pub fn set_tx_power(&mut self, tx_power: TxPower) {
+        assert!(self.state() == RadioState::DISABLED);
+
         let r = T::regs();
 
         r.txpower.write(|w| w.txpower().variant(tx_power));
@@ -408,19 +355,83 @@ impl<'d, T: Instance> Radio<'d, T> {
             // Initialize the transmission
             // trace!("rxen");
             r.tasks_rxen.write(|w| w.tasks_rxen().set_bit());
+        })
+        .await;
+    }
 
-            // Await until ready
-            while r.events_ready.read().events_ready().bit_is_clear() {}
+    async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
+        //self.trace_state();
+
+        let r = T::regs();
+        let s = T::state();
+
+        // If the Future is dropped before the end of the transmission
+        // we need to disable the interrupt and stop the transmission
+        // to keep the state consistent
+        let drop = OnDrop::new(|| {
+            trace!("radio drop: stopping");
+
+            r.intenclr.write(|w| w.end().clear());
+            r.events_end.reset();
+
+            r.tasks_stop.write(|w| w.tasks_stop().set_bit());
+
+            // The docs don't explicitly mention any event to acknowledge the stop task
+            while r.events_end.read().events_end().bit_is_clear() {}
+
+            trace!("radio drop: stopped");
+        });
+
+        // trace!("radio:enable interrupt");
+        // Clear some remnant side-effects (TODO: check if this is necessary)
+        r.events_end.reset();
+
+        // Enable interrupt
+        r.intenset.write(|w| w.end().set());
+
+        compiler_fence(Ordering::SeqCst);
+
+        // Trigger the transmission
+        trigger();
+        // self.trace_state();
+
+        // On poll check if interrupt happen
+        poll_fn(|cx| {
+            s.end_waker.register(cx.waker());
+            if r.events_end.read().events_end().bit_is_set() {
+                // trace!("radio:end");
+                return core::task::Poll::Ready(());
+            }
+            Poll::Pending
+        })
+        .await;
+
+        compiler_fence(Ordering::SeqCst);
+        r.events_disabled.reset(); // ACK
+
+        // Everthing ends fine, so we can disable the drop
+        drop.defuse();
+    }
+
+    /// Disable the radio
+    fn disable(&mut self) {
+        let r = T::regs();
+
+        compiler_fence(Ordering::SeqCst);
+        // If it is already disabled, do nothing
+        if self.state() != RadioState::DISABLED {
+            trace!("radio:disable");
+            // Trigger the disable task
+            r.tasks_disable.write(|w| w.tasks_disable().set_bit());
+
+            // Wait until the radio is disabled
+            while r.events_disabled.read().events_disabled().bit_is_clear() {}
 
             compiler_fence(Ordering::SeqCst);
 
             // Acknowledge it
-            r.events_ready.reset();
-
-            // trace!("radio:start");
-            r.tasks_start.write(|w| w.tasks_start().set_bit());
-        })
-        .await;
+            r.events_disabled.reset();
+        }
     }
 }
 

From 37739284253699d3f087fd8e1f9282090e6aaf95 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Sat, 10 Feb 2024 08:18:32 +0100
Subject: [PATCH 09/13] apply clippy sugestions

---
 embassy-nrf/src/radio/ble.rs | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index b0d374579..c74676f58 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -127,15 +127,13 @@ impl<'d, T: Instance> Radio<'d, T> {
 
     fn state(&self) -> RadioState {
         match T::regs().state.read().state().variant() {
-            Ok(s) => s,
+            Some(s) => s,
             None => unreachable!(),
         }
     }
 
     #[allow(dead_code)]
     fn trace_state(&self) {
-        let r = T::regs();
-
         match self.state() {
             RadioState::DISABLED => trace!("radio:state:DISABLED"),
             RadioState::RX_RU => trace!("radio:state:RX_RU"),
@@ -216,7 +214,7 @@ impl<'d, T: Instance> Radio<'d, T> {
     /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
     pub fn set_frequency(&mut self, frequency: u32) {
         assert!(self.state() == RadioState::DISABLED);
-        assert!(2400 <= frequency && frequency <= 2500);
+        assert!((2400..=2500).contains(&frequency));
 
         let r = T::regs();
 

From 40282c2666f8b8bda5785b30a62ab352ff8e7498 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Sat, 10 Feb 2024 08:49:14 +0100
Subject: [PATCH 10/13] add buffer input on transmit/receive

---
 embassy-nrf/src/radio/ble.rs | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index c74676f58..7718cfe14 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -316,7 +316,7 @@ impl<'d, T: Instance> Radio<'d, T> {
     /// for the life time of the transmission or if the buffer will be modified.
     /// Also if the buffer is smaller than the packet length, the radio will
     /// read/write memory out of the buffer bounds.
-    pub fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
+    fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
         // Because we are serializing the buffer, we should always have the buffer in RAM
         slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
 
@@ -334,27 +334,33 @@ impl<'d, T: Instance> Radio<'d, T> {
     }
 
     /// Send packet
-    pub async fn transmit(&mut self) {
-        let r = T::regs();
+    pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
+        self.set_buffer(buffer)?;
 
+        let r = T::regs();
         self.trigger_and_wait_end(move || {
             // Initialize the transmission
             // trace!("txen");
             r.tasks_txen.write(|w| w.tasks_txen().set_bit());
         })
         .await;
+
+        Ok(())
     }
 
     /// Receive packet
-    pub async fn receive(&mut self) {
-        let r = T::regs();
+    pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+        self.set_buffer(buffer)?;
 
+        let r = T::regs();
         self.trigger_and_wait_end(move || {
             // Initialize the transmission
             // trace!("rxen");
             r.tasks_rxen.write(|w| w.tasks_rxen().set_bit());
         })
         .await;
+
+        Ok(())
     }
 
     async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {

From fdb15b205424e71107f18b43553d43691f3d8fb7 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Sat, 10 Feb 2024 08:58:31 +0100
Subject: [PATCH 11/13] add comments about buffer unsound

---
 embassy-nrf/src/radio/ble.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 7718cfe14..ba9ac5f2e 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -334,6 +334,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     }
 
     /// Send packet
+    /// If the length byte in the package is greater than the buffer length
+    /// the radio will read memory out of the buffer bounds
     pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
         self.set_buffer(buffer)?;
 
@@ -349,6 +351,8 @@ impl<'d, T: Instance> Radio<'d, T> {
     }
 
     /// Receive packet
+    /// If the length byte in the received package is greater than the buffer length
+    /// the radio will write memory out of the buffer bounds
     pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
         self.set_buffer(buffer)?;
 

From b4f0f575388df405e4e7330f2e8cdac30c4d60d9 Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Mon, 12 Feb 2024 13:21:01 +0100
Subject: [PATCH 12/13] remove first person comments

---
 embassy-nrf/src/lib.rs       |  3 ++-
 embassy-nrf/src/radio/ble.rs | 22 +++++-----------------
 2 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 5dba6f975..04a6293a4 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -46,7 +46,8 @@ pub mod gpio;
 #[cfg(feature = "gpiote")]
 pub mod gpiote;
 
-#[cfg(any(feature = "nrf52840"))] // needs to be tested on other chips
+// TODO: tested on other chips
+#[cfg(any(feature = "nrf52840"))]
 pub mod radio;
 
 #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index ba9ac5f2e..81ef96b65 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -1,15 +1,4 @@
 //! Radio driver implementation focused on Bluetooth Low-Energy transmission.
-//!
-//! The radio can calculate the CRC, perform data whitening,
-//! automatically send the right preamble.
-//! Most of the configuration is done automatically when you choose the mode and this driver.
-//!
-//! Some configuration can just be done when de device is disabled,
-//! and the configuration varies depending if is a transmitter or a receiver.
-//! Because of that we have a state machine to keep track of the state of the radio.
-//! The Radio is the disable radio which configure the common parameters between
-//! the bluetooth protocols, like the package format, the CRC and the whitening.
-//! The TxRadio radio enable and configured as a transmitter with the specific parameters.
 
 use core::future::poll_fn;
 use core::sync::atomic::{compiler_fence, Ordering};
@@ -241,7 +230,7 @@ impl<'d, T: Instance> Radio<'d, T> {
             .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) });
 
         // The base address is truncated from the least significant byte (because the BALEN is less than 4)
-        // So we need to shift the address to the right
+        // So it shifts the address to the right
         r.base0.write(|w| unsafe { w.bits(access_address << 8) });
 
         // Don't match tx address
@@ -317,13 +306,12 @@ impl<'d, T: Instance> Radio<'d, T> {
     /// Also if the buffer is smaller than the packet length, the radio will
     /// read/write memory out of the buffer bounds.
     fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
-        // Because we are serializing the buffer, we should always have the buffer in RAM
         slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
 
         let r = T::regs();
 
-        // Here we are considering that the length of the packet is
-        // correctly set in the buffer, otherwise we will sending
+        // Here it consider that the length of the packet is
+        // correctly set in the buffer, otherwise it will send
         // unowned regions of memory
         let ptr = buffer.as_ptr();
 
@@ -374,7 +362,7 @@ impl<'d, T: Instance> Radio<'d, T> {
         let s = T::state();
 
         // If the Future is dropped before the end of the transmission
-        // we need to disable the interrupt and stop the transmission
+        // it disable the interrupt and stop the transmission
         // to keep the state consistent
         let drop = OnDrop::new(|| {
             trace!("radio drop: stopping");
@@ -417,7 +405,7 @@ impl<'d, T: Instance> Radio<'d, T> {
         compiler_fence(Ordering::SeqCst);
         r.events_disabled.reset(); // ACK
 
-        // Everthing ends fine, so we can disable the drop
+        // Everthing ends fine, so it disable the drop
         drop.defuse();
     }
 

From 368b3a9aaf97a7662217b9ff2784e3648368586c Mon Sep 17 00:00:00 2001
From: "Guilherme S. Salustiano" <guissalustiano@gmail.com>
Date: Wed, 28 Feb 2024 14:54:54 +0100
Subject: [PATCH 13/13] fix: radio error

---
 embassy-nrf/src/radio/ble.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
index 81ef96b65..24dba582f 100644
--- a/embassy-nrf/src/radio/ble.rs
+++ b/embassy-nrf/src/radio/ble.rs
@@ -15,7 +15,7 @@ use crate::interrupt::typelevel::Interrupt;
 use crate::radio::*;
 use crate::util::slice_in_ram_or;
 
-/// UART error.
+/// RADIO error.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]