text: /*
   * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha@intel.com>
   * Copyright (c) 2014 Intel Corporation.
   * BLE Beaconing based on http://dmitry.gr/index.php?r=05.Projects&proj=11.%20Bluetooth%20LE%20fakery
   *
   * This program and the accompanying materials are made available under the
   * terms of the The MIT License which is available at
   * https://opensource.org/licenses/MIT.
   *
   * SPDX-License-Identifier: MIT
   */
  
  #include <iostream>
  #include <unistd.h>
  #include <string>
  #include <stdexcept>
  #include <stdlib.h>
  
  #include "nrf24l01.hpp"
  
  using namespace upm;
  
  
  NRF24L01::NRF24L01 (int cs, int ce)
      : m_callback_obj(NULL), m_spi(0), m_csnPinCtx(cs), m_cePinCtx(ce)
  {
      init (cs, ce);
  }
  
  void
  NRF24L01::init (int chip_select, int chip_enable) {
      mraa::Result error = mraa::SUCCESS;
  
      m_csn       = chip_select;
      m_ce        = chip_enable;
      m_channel   = 99;
  
      error = m_csnPinCtx.dir(mraa::DIR_OUT);
      if (error != mraa::SUCCESS) {
          mraa::printError (error);
      }
  
      error = m_cePinCtx.dir(mraa::DIR_OUT);
      if (error != mraa::SUCCESS) {
          mraa::printError (error);
      }
  
      ceLow();
      csOff ();
  }
  
  void
  NRF24L01::configure () {
      /* Set RF channel */
      setRegister (RF_CH, m_channel);
  
      /* Set length of incoming payload */
      setRegister (RX_PW_P0, m_payload);
  
      /* Set length of incoming payload for broadcast */
      setRegister (RX_PW_P1, m_payload);
  
      /* Start receiver */
      rxPowerUp ();
      rxFlushBuffer ();
  }
  
  void
  NRF24L01::send (uint8_t * value) {
      uint8_t status;
      status = getStatus();
  
      while (m_ptx) {
          status = getStatus();
  
          if((status & ((1 << TX_DS)  | (1 << MAX_RT)))){
              m_ptx = 0;
              break;
          }
      } // Wait until last paket is send
  
      ceLow ();
      txPowerUp (); // Set to transmitter mode , Power up
      txFlushBuffer ();
  
      csOn ();
      m_spi.writeByte(W_TX_PAYLOAD); // Write cmd to write payload
      writeBytes (value, NULL, m_payload); // Write payload
      csOff ();
      ceHigh(); // Start transmission
  
      while (dataSending ()) { }
  
      usleep (10000);
  }
  
  void
  NRF24L01::send () {
      send (m_txBuffer);
  }
  
  void
  NRF24L01::setSourceAddress (uint8_t * addr) {
      ceLow ();
      writeRegister (RX_ADDR_P0, addr, ADDR_LEN);
      ceHigh ();
  }
  
  void
  NRF24L01::setDestinationAddress (uint8_t * addr) {
      writeRegister (TX_ADDR, addr, ADDR_LEN);
  }
  
  void
  NRF24L01::setBroadcastAddress (uint8_t * addr) {
      writeRegister (RX_ADDR_P1, addr, ADDR_LEN);
  }
  
  void
  NRF24L01::setPayload (uint8_t payload) {
      m_payload = payload;
  }
  
  void
  NRF24L01::setDataReceivedHandler (Callback *call_obj)
  {
      m_callback_obj = call_obj;
      dataReceivedHandler = &generic_callback;
  }
  
  bool
  NRF24L01::dataReady () {
      /* See note in getData() function - just checking RX_DR isn't good enough */
      uint8_t status = getStatus();
      /* We can short circuit on RX_DR, but if it's not set, we still need
       * to check the FIFO for any pending packets */
      if ( status & (1 << RX_DR) ) {
          return 1;
      }
  
      return !rxFifoEmpty();
  }
  
  bool
  NRF24L01::dataSending () {
      uint8_t status;
      if(m_ptx)   { // Sending mode.
          status = getStatus();
          /* if sending successful (TX_DS) or max retries exceded (MAX_RT). */
          if((status & ((1 << TX_DS)  | (1 << MAX_RT)))){
              rxPowerUp ();
              return false;
          }
          return true;
      }
      return false;
  }
  
  void
  NRF24L01::getData (uint8_t * data)  {
      csOn ();
      /* Send cmd to read rx payload */
      m_spi.writeByte(R_RX_PAYLOAD);
      /* Read payload */
      writeBytes (data, data, m_payload);
      csOff ();
      /* NVI: per product spec, p 67, note c:
       * "The RX_DR IRQ is asserted by a new packet arrival event. The procedure
       * for handling this interrupt should be: 1) read payload through SPI,
       * 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more
       * payloads available in RX FIFO, 4) if there are more data in RX FIFO,
       * repeat from step 1)."
       * So if we're going to clear RX_DR here, we need to check the RX FIFO
       * in the dataReady() function */
      /* Reset status register */
      setRegister (STATUS, (1<<RX_DR));
  }
  
  uint8_t
  NRF24L01::getStatus() {
      return getRegister (STATUS);
  }
  
  bool
  NRF24L01::rxFifoEmpty () {
      uint8_t fifoStatus = getRegister (FIFO_STATUS);
      return (fifoStatus & (1 << RX_EMPTY));
  }
  
  void
  NRF24L01::rxPowerUp () {
      m_ptx = 0;
      ceLow ();
      setRegister (CONFIG, _CONFIG | ( (1 << PWR_UP) | (1 << PRIM_RX) ));
      ceHigh ();
      setRegister (STATUS, (1 << TX_DS) | (1 << MAX_RT));
  }
  
  void
  NRF24L01::rxFlushBuffer () {
      sendCommand (FLUSH_RX);
  }
  
  void
  NRF24L01::txPowerUp () {
      m_ptx = 1;
      setRegister (CONFIG, _CONFIG | ( (1 << PWR_UP) | (0 << PRIM_RX) ));
  }
  
  void
  NRF24L01::powerDown(){
      ceLow ();
      setRegister (CONFIG, _CONFIG);
  }
  
  void
  NRF24L01::setChannel (uint8_t channel) {
      m_channel = channel;
      setRegister (RF_CH, channel);
  }
  
  void
  NRF24L01::setPower (power_t power) {
      uint8_t setupRegisterData = 0;
  
      switch (power) {
          case NRF_0DBM:
              m_power = 3;
          break;
          case NRF_6DBM:
              m_power = 2;
          break;
          case NRF_12DBM:
              m_power = 1;
          break;
          case NRF_18DBM:
              m_power = 0;
          break;
      }
  
      setupRegisterData = getRegister (RF_SETUP); // Read current value.
      setupRegisterData &= 0xFC; // Erase the old value;
      setupRegisterData |= (m_power & 0x3);
      setRegister (RF_SETUP, setupRegisterData); // Write the new value.
  }
  
  uint8_t
  NRF24L01::setSpeedRate (speed_rate_t rate) {
      uint8_t setupRegisterData = 0;
  
      setupRegisterData = getRegister (RF_SETUP); // Read current value.
      setupRegisterData &= ~((1 << RF_DR_LOW) | (1 << RF_DR_HIGH));
  
      switch (rate) {
          case NRF_250KBPS:
              setupRegisterData |= (1 << RF_DR_LOW) ;
          break;
          case NRF_1MBPS:
          break;
          case NRF_2MBPS:
              setupRegisterData |= (1 << RF_DR_HIGH);
          break;
      }
  
      setRegister (RF_SETUP, setupRegisterData); // Write the new value.
  
      if (setupRegisterData == getRegister (RF_SETUP)) {
          return 0x0;
      }
  
      return 0x1;
  }
  
  mraa::Result
  NRF24L01::ceHigh () {
      return m_cePinCtx.write(HIGH);
  }
  
  mraa::Result
  NRF24L01::ceLow () {
      return m_cePinCtx.write(LOW);
  }
  
  mraa::Result
  NRF24L01::csOn () {
      return m_csnPinCtx.write(LOW);
  }
  
  mraa::Result
  NRF24L01::csOff () {
      return m_csnPinCtx.write(HIGH);
  }
  
  void
  NRF24L01::pollListener() {
      if (dataReady()) {
          getData (m_rxBuffer);
  
          dataReceivedHandler (m_callback_obj); /* let know that data arrived */
      }
  }
  
  void
  NRF24L01::txFlushBuffer () {
      sendCommand (FLUSH_TX);
  }
  
  void
  NRF24L01::setBeaconingMode () {
      setRegister (CONFIG,     0x12); // on, no crc, int on RX/TX done
      setRegister (EN_AA,      0x00); // no auto-acknowledge
      setRegister (EN_RXADDR,  0x00); // no RX
      setRegister (SETUP_AW,   0x02); // 5-byte address
      setRegister (SETUP_RETR, 0x00); // no auto-retransmit
      setRegister (RF_SETUP,   0x06); // 1MBps at 0dBm
      setRegister (STATUS,     0x3E); // clear various flags
      setRegister (DYNPD,      0x00); // no dynamic payloads
      setRegister (FEATURE,    0x00); // no features
      setRegister (RX_PW_P0,   32);   // always RX 32 bytes
      setRegister (EN_RXADDR,  0x01); // RX on pipe 0
  
      uint8_t addr[4] = { swapbits(0x8E), swapbits(0x89), swapbits(0xBE), swapbits(0xD6)};
      writeRegister (TX_ADDR,     addr, 4);
      writeRegister (RX_ADDR_P0,  addr, 4);
  
      uint8_t index = 0;
      m_bleBuffer[index++] = 0x42;        // PDU type, given address is random
      m_bleBuffer[index++] = 0x1B;        // 6+3+2+16 = 27 bytes of payload
  
      m_bleBuffer[index++] = BLE_MAC_0;
      m_bleBuffer[index++] = BLE_MAC_1;
      m_bleBuffer[index++] = BLE_MAC_2;
      m_bleBuffer[index++] = BLE_MAC_3;
      m_bleBuffer[index++] = BLE_MAC_4;
      m_bleBuffer[index++] = BLE_MAC_5;
  
      m_bleBuffer[index++] = 2;           // flags (LE-only, limited discovery mode)
      m_bleBuffer[index++] = 0x01;
      m_bleBuffer[index++] = 0x05;
  
      m_bleBuffer[index++] = 17;
      m_bleBuffer[index++] = 0x08;
  }
  
  void
  NRF24L01::sendBeaconingMsg (uint8_t * msg) {
      const uint8_t   chRf[] = {2, 26,80};
      const uint8_t   chLe[] = {37,38,39};
      uint8_t         index = BLE_PAYLOAD_OFFSET + 16;
  
      memcpy (&m_bleBuffer[BLE_PAYLOAD_OFFSET], msg, 16);
      m_bleBuffer[index++] = 0x55;
      m_bleBuffer[index++] = 0x55;
      m_bleBuffer[index++] = 0x55;
  
      uint8_t channel = 0;
      while (++channel != sizeof(chRf)) {
          setRegister (RF_CH,     chRf[channel]);
          setRegister (STATUS,    0x6E);          //clear flags
  
          blePacketEncode (m_bleBuffer, index, chLe[channel]);
  
          sendCommand (FLUSH_TX); // Clear RX Fifo
          sendCommand (FLUSH_RX); // Clear TX Fifo
  
          csOn ();
          m_spi.writeByte(W_TX_PAYLOAD);        // Write cmd to write payload
          writeBytes (m_bleBuffer, NULL, 32);     // Write payload
          csOff ();
  
          setRegister (CONFIG, 0x12);             // tx on
          ceHigh ();                              // Start transmission
          usleep (10000);
          ceLow ();
      }
  }
  
  /*
   * ---------------
   * PRIVATE SECTION
   * ---------------
   */
  
  void
  NRF24L01::writeBytes (uint8_t * dataout, uint8_t * datain, uint8_t len) {
      if(len > MAX_BUFFER){
          len = MAX_BUFFER;
      }
      for (uint8_t i = 0; i < len; i++) {
          if (datain != NULL) {
              datain[i] = m_spi.writeByte(dataout[i]);
          } else {
              m_spi.writeByte(dataout[i]);
          }
      }
  }
  
  void
  NRF24L01::setRegister (uint8_t reg, uint8_t value) {
      csOn ();
      m_spi.writeByte(W_REGISTER | (REGISTER_MASK & reg));
      m_spi.writeByte(value);
      csOff ();
  }
  
  uint8_t
  NRF24L01::getRegister (uint8_t reg) {
      uint8_t data = 0;
  
      csOn ();
      m_spi.writeByte(R_REGISTER | (REGISTER_MASK & reg));
      data = m_spi.writeByte(data);
      csOff ();
  
      return data;
  }
  
  void
  NRF24L01::readRegister (uint8_t reg, uint8_t * value, uint8_t len) {
      csOn ();
      m_spi.writeByte(R_REGISTER | (REGISTER_MASK & reg));
      writeBytes (value, value, len);
      csOff ();
  }
  
  void
  NRF24L01::writeRegister (uint8_t reg, uint8_t * value, uint8_t len) {
      csOn ();
      m_spi.writeByte(W_REGISTER | (REGISTER_MASK & reg));
      writeBytes (value, NULL, len);
      csOff ();
  }
  
  void
  NRF24L01::sendCommand (uint8_t cmd) {
      csOn ();
      m_spi.writeByte(cmd);
      csOff ();
  }
  
  void
  NRF24L01::bleCrc (const uint8_t* data, uint8_t len, uint8_t* dst) {
      uint8_t v, t, d;
      while(len--) {
          d = *data++;
          for(v = 0; v < 8; v++, d >>= 1){
              t = dst[0] >> 7;
              dst[0] <<= 1;
              if(dst[1] & 0x80) dst[0] |= 1;
              dst[1] <<= 1;
              if(dst[2] & 0x80) dst[1] |= 1;
              dst[2] <<= 1;
  
              if(t != (d & 1)) {
                  dst[2] ^= 0x5B;
                  dst[1] ^= 0x06;
              }
          }
      }
  }
  
  void
  NRF24L01::bleWhiten (uint8_t* data, uint8_t len, uint8_t whitenCoeff) {
      uint8_t  m;
      while(len--) {
          for(m = 1; m; m <<= 1) {
              if(whitenCoeff & 0x80){
                  whitenCoeff ^= 0x11;
                  (*data) ^= m;
              }
              whitenCoeff <<= 1;
          }
          data++;
      }
  }
  
  void
  NRF24L01::blePacketEncode(uint8_t* packet, uint8_t len, uint8_t chan) {
      if(len > MAX_BUFFER){
          len = MAX_BUFFER;
      }
      
      //length is of packet, including crc. pre-populate crc in packet with initial crc value!
      uint8_t i, dataLen = len - 3;
  
      bleCrc(packet, dataLen, packet + dataLen);
      for(i = 0; i < 3; i++, dataLen++) {
          packet[dataLen] = swapbits(packet[dataLen]);
      }
  
      bleWhiten(packet, len, (swapbits(chan) | 2));
      for(i = 0; i < len; i++) {
          packet[i] = swapbits(packet[i]);
      }
  }
  
  uint8_t
  NRF24L01::swapbits(uint8_t a) {
      uint8_t v = 0;
  
      if(a & 0x80) v |= 0x01;
      if(a & 0x40) v |= 0x02;
      if(a & 0x20) v |= 0x04;
      if(a & 0x10) v |= 0x08;
      if(a & 0x08) v |= 0x10;
      if(a & 0x04) v |= 0x20;
      if(a & 0x02) v |= 0x40;
      if(a & 0x01) v |= 0x80;
  
      return v;
  }
  