1#![no_std]
41#![deny(missing_docs)]
42
43#[macro_use]
44extern crate bitflags;
45#[macro_use]
46extern crate bluetooth_hci as hci;
47extern crate byteorder;
48extern crate embedded_hal as emhal;
49#[macro_use(block)]
50extern crate nb;
51
52use byteorder::{ByteOrder, LittleEndian};
53use core::cmp::min;
54use core::convert::TryFrom;
55use core::marker::PhantomData;
56use hci::host::HciHeader;
57use hci::Controller;
58
59mod cb;
60mod command;
61pub mod event;
62mod opcode;
63
64pub use command::gap;
65pub use command::gatt;
66pub use command::hal;
67pub use command::l2cap;
68
69pub use hci::host::{AdvertisingFilterPolicy, AdvertisingType, OwnAddressType};
70
71#[derive(Debug, PartialEq)]
73pub enum Error<SpiError, GpioError> {
74 Spi(SpiError),
76
77 Gpio(GpioError),
80}
81
82pub struct BlueNRG<'buf, SPI, OutputPin1, OutputPin2, InputPin, GpioError> {
84 chip_select: OutputPin1,
87
88 reset: OutputPin2,
90
91 data_ready: InputPin,
94
95 rx_buffer: cb::Buffer<'buf, u8>,
98
99 #[doc(hidden)]
100 _spi: PhantomData<SPI>,
101
102 #[doc(hidden)]
103 _gpio_error: PhantomData<GpioError>,
104}
105
106pub struct ActiveBlueNRG<'bnrg, 'spi, 'dbuf, SPI, OutputPin1, OutputPin2, InputPin, GpioError> {
112 d: &'bnrg mut BlueNRG<'dbuf, SPI, OutputPin1, OutputPin2, InputPin, GpioError>,
114
115 spi: &'spi mut SPI,
117}
118
119fn parse_spi_header<E>(header: &[u8; 5]) -> Result<(u16, u16), nb::Error<E>> {
130 const BNRG_READY: u8 = 0x02;
131 if header[0] == BNRG_READY {
132 Ok((
133 LittleEndian::read_u16(&header[1..]),
134 LittleEndian::read_u16(&header[3..]),
135 ))
136 } else {
137 Err(nb::Error::WouldBlock)
138 }
139}
140
141enum Access {
142 Read,
143 Write,
144}
145
146impl Access {
147 fn byte(&self) -> u8 {
148 match self {
149 Access::Read => 0x0b,
150 Access::Write => 0x0a,
151 }
152 }
153}
154
155impl<'bnrg, 'spi, 'dbuf, SPI, OutputPin1, OutputPin2, InputPin, SpiError, GpioError>
156 ActiveBlueNRG<'bnrg, 'spi, 'dbuf, SPI, OutputPin1, OutputPin2, InputPin, GpioError>
157where
158 SPI: emhal::blocking::spi::Transfer<u8, Error = SpiError>
159 + emhal::blocking::spi::Write<u8, Error = SpiError>,
160 OutputPin1: emhal::digital::v2::OutputPin<Error = GpioError>,
161 OutputPin2: emhal::digital::v2::OutputPin<Error = GpioError>,
162 InputPin: emhal::digital::v2::InputPin<Error = GpioError>,
163{
164 fn block_until_ready(
174 &mut self,
175 access_byte: u8,
176 ) -> nb::Result<(u16, u16), Error<SpiError, GpioError>> {
177 loop {
178 let mut write_header = [access_byte, 0x00, 0x00, 0x00, 0x00];
179 self.spi
180 .transfer(&mut write_header)
181 .map_err(Error::Spi)
182 .map_err(nb::Error::Other)?;
183
184 match parse_spi_header(&write_header) {
185 Ok(lengths) => return Ok(lengths),
186 Err(nb::Error::WouldBlock) => {
187 self.d
188 .chip_select
189 .set_high()
190 .map_err(Error::Gpio)
191 .map_err(nb::Error::Other)?;
192 self.d
193 .chip_select
194 .set_low()
195 .map_err(Error::Gpio)
196 .map_err(nb::Error::Other)?;
197 }
198 Err(err) => return Err(err),
199 }
200 }
201 }
202
203 fn block_until_ready_for(
204 &mut self,
205 access: Access,
206 ) -> nb::Result<u16, Error<SpiError, GpioError>> {
207 let (write_len, read_len) = self.block_until_ready(access.byte())?;
208 Ok(match access {
209 Access::Read => read_len,
210 Access::Write => write_len,
211 })
212 }
213
214 fn try_write(
230 &mut self,
231 header: &[u8],
232 payload: &[u8],
233 ) -> nb::Result<(), Error<SpiError, GpioError>> {
234 if !header.is_empty() {
235 self.spi
236 .write(header)
237 .map_err(Error::Spi)
238 .map_err(nb::Error::Other)?;
239 }
240 if !payload.is_empty() {
241 self.spi
242 .write(payload)
243 .map_err(Error::Spi)
244 .map_err(nb::Error::Other)?;
245 }
246
247 Ok(())
248 }
249
250 fn read_available_data(&mut self) -> nb::Result<(), Error<SpiError, GpioError>> {
266 if !self
267 .d
268 .data_ready()
269 .map_err(Error::Gpio)
270 .map_err(nb::Error::Other)?
271 {
272 return Err(nb::Error::WouldBlock);
273 }
274
275 let read_len = self.block_until_ready_for(Access::Read)?;
276 let mut bytes_available = read_len as usize;
277 while bytes_available > 0 && self.d.rx_buffer.next_contiguous_slice_len() > 0 {
278 let transfer_count = min(
279 bytes_available,
280 self.d.rx_buffer.next_contiguous_slice_len(),
281 );
282 {
283 let rx = self.d.rx_buffer.next_mut_slice(transfer_count);
284 for byte in rx.iter_mut() {
285 *byte = 0;
286 }
287 self.spi
288 .transfer(rx)
289 .map_err(Error::Spi)
290 .map_err(nb::Error::Other)?;
291 }
292 bytes_available -= transfer_count;
293 }
294
295 Ok(())
296 }
297
298 fn write_command(
299 &mut self,
300 opcode: opcode::Opcode,
301 params: &[u8],
302 ) -> nb::Result<(), Error<SpiError, GpioError>> {
303 const HEADER_LEN: usize = 4;
304 let mut header = [0; HEADER_LEN];
305 hci::host::uart::CommandHeader::new(opcode, params.len()).copy_into_slice(&mut header);
306
307 self.write(&header, ¶ms)
308 }
309}
310
311impl<'bnrg, 'spi, 'dbuf, SPI, OutputPin1, OutputPin2, InputPin, SpiError, GpioError> hci::Controller
312 for ActiveBlueNRG<'bnrg, 'spi, 'dbuf, SPI, OutputPin1, OutputPin2, InputPin, GpioError>
313where
314 SPI: emhal::blocking::spi::Transfer<u8, Error = SpiError>
315 + emhal::blocking::spi::Write<u8, Error = SpiError>,
316 OutputPin1: emhal::digital::v2::OutputPin<Error = GpioError>,
317 OutputPin2: emhal::digital::v2::OutputPin<Error = GpioError>,
318 InputPin: emhal::digital::v2::InputPin<Error = GpioError>,
319{
320 type Error = Error<SpiError, GpioError>;
321 type Header = hci::host::uart::CommandHeader;
322 type Vendor = BlueNRGTypes;
323
324 fn write(&mut self, header: &[u8], payload: &[u8]) -> nb::Result<(), Self::Error> {
325 self.d
326 .chip_select
327 .set_low()
328 .map_err(Error::Gpio)
329 .map_err(nb::Error::Other)?;
330 let write_len = self.block_until_ready_for(Access::Write)?;
331 if (write_len as usize) < header.len() + payload.len() {
332 return Err(nb::Error::WouldBlock);
333 }
334
335 let result = self.try_write(header, payload);
336 self.d
337 .chip_select
338 .set_high()
339 .map_err(Error::Gpio)
340 .map_err(nb::Error::Other)?;
341
342 result
343 }
344
345 fn read_into(&mut self, buffer: &mut [u8]) -> nb::Result<(), Self::Error> {
346 let result = if buffer.len() > self.d.rx_buffer.size() {
347 self.d
348 .chip_select
349 .set_low()
350 .map_err(Error::Gpio)
351 .map_err(nb::Error::Other)?;
352 let r = self.read_available_data();
353 self.d
354 .chip_select
355 .set_high()
356 .map_err(Error::Gpio)
357 .map_err(nb::Error::Other)?;
358
359 r
360 } else {
361 Ok(())
362 };
363
364 if buffer.len() <= self.d.rx_buffer.size() {
365 self.d.rx_buffer.take_slice(buffer.len(), buffer);
366 Ok(())
367 } else if let Err(e) = result {
368 Err(e)
369 } else {
370 Err(nb::Error::WouldBlock)
371 }
372 }
373
374 fn peek(&mut self, n: usize) -> nb::Result<u8, Self::Error> {
375 if n >= self.d.rx_buffer.size() {
376 if !self
377 .d
378 .data_ready()
379 .map_err(Error::Gpio)
380 .map_err(nb::Error::Other)?
381 {
382 return Err(nb::Error::WouldBlock);
383 }
384
385 self.d
386 .chip_select
387 .set_low()
388 .map_err(Error::Gpio)
389 .map_err(nb::Error::Other)?;
390 let result = self.read_available_data();
391 self.d
392 .chip_select
393 .set_high()
394 .map_err(Error::Gpio)
395 .map_err(nb::Error::Other)?;
396
397 if n >= self.d.rx_buffer.size() {
398 if let Err(e) = result {
399 return Err(e);
400 }
401
402 }
404 }
405
406 if n < self.d.rx_buffer.size() {
407 Ok(self.d.rx_buffer.peek(n))
408 } else {
409 Err(nb::Error::WouldBlock)
410 }
411 }
412}
413
414pub struct BlueNRGTypes;
416impl hci::Vendor for BlueNRGTypes {
417 type Status = event::Status;
418 type Event = event::BlueNRGEvent;
419}
420
421pub trait UartController<E>:
423 crate::gap::Commands<Error = E>
424 + crate::gatt::Commands<Error = E>
425 + crate::hal::Commands<Error = E>
426 + crate::l2cap::Commands<Error = E>
427 + bluetooth_hci::host::uart::Hci<E, crate::event::BlueNRGEvent, crate::event::BlueNRGError>
428{
429}
430impl<T, E> UartController<E> for T where
431 T: crate::gap::Commands<Error = E>
432 + crate::gatt::Commands<Error = E>
433 + crate::hal::Commands<Error = E>
434 + crate::l2cap::Commands<Error = E>
435 + bluetooth_hci::host::uart::Hci<E, crate::event::BlueNRGEvent, crate::event::BlueNRGError>
436{
437}
438
439impl<'buf, SPI, OutputPin1, OutputPin2, InputPin, GpioError>
440 BlueNRG<'buf, SPI, OutputPin1, OutputPin2, InputPin, GpioError>
441where
442 OutputPin1: emhal::digital::v2::OutputPin<Error = GpioError>,
443 OutputPin2: emhal::digital::v2::OutputPin<Error = GpioError>,
444 InputPin: emhal::digital::v2::InputPin<Error = GpioError>,
445{
446 pub fn new(
448 rx_buffer: &'buf mut [u8],
449 cs: OutputPin1,
450 dr: InputPin,
451 rst: OutputPin2,
452 ) -> BlueNRG<'buf, SPI, OutputPin1, OutputPin2, InputPin, GpioError> {
453 BlueNRG {
454 chip_select: cs,
455 rx_buffer: cb::Buffer::new(rx_buffer),
456 data_ready: dr,
457 reset: rst,
458 _spi: PhantomData,
459 _gpio_error: PhantomData,
460 }
461 }
462
463 pub fn with_spi<'spi, T, F, E>(&mut self, spi: &'spi mut SPI, body: F) -> T
468 where
469 F: FnOnce(&mut ActiveBlueNRG<SPI, OutputPin1, OutputPin2, InputPin, GpioError>) -> T,
470 SPI: emhal::blocking::spi::transfer::Default<u8, Error = E>
471 + emhal::blocking::spi::write::Default<u8, Error = E>,
472 {
473 let mut active =
474 ActiveBlueNRG::<SPI, OutputPin1, OutputPin2, InputPin, GpioError> { spi, d: self };
475 body(&mut active)
476 }
477
478 pub fn reset<T, Time>(&mut self, timer: &mut T, freq: Time) -> nb::Result<(), OutputPin2::Error>
481 where
482 T: emhal::timer::CountDown<Time = Time>,
483 Time: Copy,
484 {
485 self.reset.set_low().map_err(nb::Error::Other)?;
486 timer.start(freq);
487 block!(timer.wait()).unwrap();
488
489 self.reset.set_high().map_err(nb::Error::Other)?;
490 timer.start(freq);
491 block!(timer.wait()).unwrap();
492
493 Ok(())
494 }
495
496 fn data_ready(&self) -> Result<bool, InputPin::Error> {
498 self.data_ready.is_high()
499 }
500}
501
502#[derive(Clone)]
504pub struct Version {
505 pub hw_version: u8,
507
508 pub major: u8,
510
511 pub minor: u8,
513
514 pub patch: u8,
516}
517
518pub trait LocalVersionInfoExt {
521 fn bluenrg_version(&self) -> Version;
524}
525
526impl<VS> LocalVersionInfoExt for hci::event::command::LocalVersionInfo<VS> {
527 fn bluenrg_version(&self) -> Version {
528 Version {
529 hw_version: (self.hci_revision >> 8) as u8,
530 major: (self.hci_revision & 0xFF) as u8,
531 minor: ((self.lmp_subversion >> 4) & 0xF) as u8,
532 patch: (self.lmp_subversion & 0xF) as u8,
533 }
534 }
535}
536
537#[derive(Copy, Clone, Debug, PartialEq)]
539pub enum HardwareError {
540 SpiFraming,
543
544 RadioState,
548
549 TimerOverrun,
553}
554
555#[derive(Copy, Clone, Debug, PartialEq)]
557pub struct InvalidHardwareError(pub u8);
558
559impl TryFrom<u8> for HardwareError {
560 type Error = InvalidHardwareError;
561 fn try_from(value: u8) -> Result<Self, Self::Error> {
562 match value {
563 0 => Ok(HardwareError::SpiFraming),
564 1 => Ok(HardwareError::RadioState),
565 2 => Ok(HardwareError::TimerOverrun),
566 _ => Err(InvalidHardwareError(value)),
567 }
568 }
569}