1 // $Header: /volume1/cvs/cvsroot/microrisc/iqrf/DPA/Examples/DP2Papp.c,v 1.17 2022/02/25 09:41:26 hynek Exp $
    2 //############################################################################################
    3 // Example DP2P TR module application
    4 //############################################################################################
    5 // Revision history:
    6 //   2022/02/24  Release for DPA 4.17
    7 //
    8 // *********************************************************************
    9 
   10 #include "../includes/IQRF.h"
   11 #include "Dpa.h"
   12 
   13 // DP2P special (fake PNUM and PCMD) DPA values to indicate that DP2P parameters are specified
   14 #define DP2P_PNUM   0xDD
   15 #define DP2P_PCMD   0xDD
   16 
   17 // Incoming SPI DP2P packet request
   18 typedef union
   19 {
   20   // Used to send DP2P requests, then only PNUM+PCMD(+_zero_) fields from the next union field are used to specify DPA request PCMD+PNUN
   21 
   22   /* IQRF IDE Terminal example
   23    $07$03$00$FE$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$07$00$FF$FF
   24      PNUM = PNUM_LEDG = $07
   25      PCMD = CMD_LED_PULSE = $03
   26      Nodes #1-7 = $FE + 29 x $00
   27      SlotLength = 0x00 (default values)
   28      Response RF power = $07
   29      HWPID 0xFFFF = $FF$FF
   30   */
   31   TDP2Prequest DP2Prequest;
   32 
   33   // Used to specify PNUM and PCMD of the DP2P request or to setup DP2P parameters (when PNUM and PCMD equal DP2P_PNUM and DP2P_PCMD respectively)
   34   struct
   35   {
   36     // PNUM
   37     uns8  PNUM;
   38     // PCMD
   39     uns8  PCMD;
   40     // Zero
   41     uns8  _zero_;
   42     // Previous fields overlap Header part of the TDP2Prequest
   43 
   44     // Next fields are used only when PNUM and PCMD equal DP2P_PNUM and DP2P_PCMD respectively
   45 
   46     /* IQRF IDE Terminal example
   47      $DD$DD$00$01$23$07$05$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00
   48        STD+LP network = $01
   49        channel 35 = $23
   50        Request RF power 7 = $07
   51        RX filter 5 = $05
   52        AccessPassword = 16 x $00 (i.e. empty, not recommended to use!)
   53     */
   54 
   55     // Non-zero value when STD+LP network is used, otherwise STD
   56     uns8  STD_LP_network;
   57     // Channel to communicate DP2P packets
   58     uns8  RfChannel;
   59     // Power to send DP2P request
   60     uns8  ReqTxPower;
   61     // Filter to receive DP2P responses
   62     uns8  RespRxFilter;
   63     // Access Password to use for de/encryption
   64     uns8  AccessPassword[sizeof( _DpaMessage.PerOSSetSecurity_Request.Data )];
   65   } DP2PrequestParams;
   66 } TSPI_TDP2Prequest;
   67 
   68 // Outgoing SPI DP2P packet invite (fields must exactly overlap TDP2Invite)
   69 typedef struct
   70 {
   71   uns8  Header[3];  // 0x000001 (0x00 is used to detect TSPI_TDP2Pinvite, 0xFF specifies TSPI_TDP2Presponse - see below)
   72   uns8  NADR;
   73 } TSPI_TDP2Pinvite;
   74 
   75 // Outgoing SPI DP2P packet response
   76 typedef union
   77 {
   78   // Real DP2P response but PNUM and PCMD is overlapped, see the next union field
   79   TDP2Presponse DP2Presponse;
   80 
   81   struct
   82   {
   83     // PNUM
   84     uns8  PNUM;
   85     // PCMD
   86     uns8  PCMD;
   87     // 0xFF (remainder from the DP2P Response, used to detect Response)
   88     uns8 _0xFF_;
   89   } DP2PresponseParams;
   90 } TSPI_TDP2Presponse;
   91 
   92 /*
   93 IQRF IDE terminal log example
   94  <> Length Data (comment)
   95  Tx 23     DD.DD.00.01.23.07.05.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00. (Sets DP2P parameters, see details above)
   96  Tx 38     07.03.00.FE.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.07.00.FF.FF. (Nodes 1-7 will pulse the LEDG, see details above)
   97  Rx 4      01.00.00.01. (Invite from Node #1)
   98  Rx 4      07.83.FF.01. (Response from Node #1)
   99  Rx 4      01.00.00.02. (Invite from Node #2)
  100  Rx 4      07.83.FF.02. (Response from Node #2)
  101  Rx 4      01.00.00.03. (Invite from Node #3)
  102  Rx 4      07.83.FF.03. (Response from Node #3)
  103  Rx 4      01.00.00.04. (Invite from Node #4)
  104  Rx 4      07.83.FF.04. (Response from Node #4)
  105  Rx 4      01.00.00.06. (Invite from Node #5)
  106  Rx 4      07.83.FF.05. (Response from Node #5)
  107  Rx 4      01.00.00.06. (Invite from Node #6)
  108  Rx 4      07.83.FF.06. (Response from Node #6)
  109  Rx 4      01.00.00.07. (Invite from Node #7)
  110  Rx 4      07.83.FF.07. (Response from Node #7)
  111 */
  112 
  113 //############################################################################################
  114 void APPLICATION()
  115 //############################################################################################
  116 {
  117   // Indicate start
  118   pulseLEDR();
  119   pulseLEDG();
  120 
  121   // We use SPI
  122   enableSPI();
  123 
  124   // Filter used to receive DP2P responses
  125   uns8 RespRxFilter;
  126   // RF mode to send DP2P Requests
  127   uns8 ReqRfMode;
  128 
  129   // Loop forever
  130   for ( ;; )
  131   {
  132     // Anything at SPI received?
  133     if ( !getStatusSPI() && _SPIRX )
  134     {
  135       // No CRC error
  136       if ( _SPICRCok )
  137       {
  138         // Stop SPI
  139         stopSPI();
  140 
  141         // SPI incoming packet variable
  142         TSPI_TDP2Prequest SPI_TDP2Prequest @ bufferCOM;
  143         // Check the packet a little
  144         if ( SPI_TDP2Prequest.DP2PrequestParams._zero_ == 0 )
  145         {
  146           // DP2P parameters to set?
  147           if ( SPI_TDP2Prequest.DP2PrequestParams.PNUM == DP2P_PNUM && SPI_TDP2Prequest.DP2PrequestParams.PCMD == DP2P_PCMD )
  148           {
  149             // Check the length
  150             if ( SPIpacketLength == sizeof( SPI_TDP2Prequest.DP2PrequestParams ) )
  151             {
  152               // Indicate reception
  153               pulseLEDR();
  154 
  155               // Set RF mode and toutRF according to the network type
  156               if ( SPI_TDP2Prequest.DP2PrequestParams.STD_LP_network != 0 )
  157                 // STD+LP network
  158                 ReqRfMode = _WPE | _RX_STD | _TX_LP;
  159               else
  160                 // STD network
  161                 ReqRfMode = _WPE | _RX_STD | _TX_STD | _STDL;
  162 
  163               // Set RF channel
  164               setRFchannel( SPI_TDP2Prequest.DP2PrequestParams.RfChannel );
  165               // Set RF power
  166               setRFpower( SPI_TDP2Prequest.DP2PrequestParams.ReqTxPower );
  167               // Remember RX filter
  168               RespRxFilter = SPI_TDP2Prequest.DP2PrequestParams.RespRxFilter;
  169               // Set Access Password
  170               copyMemoryBlock( SPI_TDP2Prequest.DP2PrequestParams.AccessPassword, bufferINFO, sizeof( SPI_TDP2Prequest.DP2PrequestParams.AccessPassword ) );
  171               setAccessPassword();
  172             }
  173           }
  174           else
  175           {
  176             // Check the length
  177             if ( SPIpacketLength >= offsetof( TDP2Prequest, PDATA ) )
  178             {
  179               // Send DP2P request
  180 
  181               // Prepare DPA Request
  182               // Get PNUN
  183               _PNUM = SPI_TDP2Prequest.DP2PrequestParams.PNUM;
  184               // Get PCMD
  185               _PCMD = SPI_TDP2Prequest.DP2PrequestParams.PCMD;
  186               // DP2P Request variable
  187               TDP2Prequest DP2Prequest @ bufferRF;
  188               // Make sure header is correct (there were PNUM and PMCD stored)
  189               SPI_TDP2Prequest.DP2Prequest.Header[0] = 0;
  190               SPI_TDP2Prequest.DP2Prequest.Header[1] = 0;
  191               // Set data length
  192               PPAR = SPIpacketLength - offsetof( TDP2Prequest, PDATA );
  193               // Set the DP2P packet length
  194               DLEN = sizeof( DP2Prequest );
  195               // Use Access Password for encryption
  196               encryptByAccessPassword = TRUE;
  197               // Encrypt the DP2P Request
  198               copyBufferCOM2RF();
  199               encryptBufferRF( sizeof( DP2Prequest ) / 16 );
  200               // Set RF Flags
  201               PIN = _DPAF_MASK;
  202               // And finally send DP2P Request
  203               setRFmode( ReqRfMode );
  204               RFTXpacket();
  205               // Indicate reception
  206               pulseLEDR();
  207             }
  208           }
  209         }
  210       }
  211       // Start SPI again
  212       startSPI( 0 );
  213     }
  214 
  215     // Ready to receive STD packets
  216     toutRF = 1;
  217     setRFmode( _WPE | _RX_STD | _TX_STD );
  218     // Anything from RF received?
  219     if ( // There is a signal
  220          checkRF( RespRxFilter ) &&
  221          // Packet was received
  222          RFRXpacket() &&
  223          // DPA flags are used
  224          _DPAF &&
  225          // There is enough data (at least one AES block)
  226          DLEN >= 16 &&
  227          // Real packet has a correct AES-128 block length
  228          ( ( PPAR + 15 ) & ~15 ) == DLEN )
  229     {
  230       // Decrypt the packet
  231       encryptByAccessPassword = TRUE;
  232       decryptBufferRF( DLEN / 16 );
  233 
  234       // DP2P Invite variable
  235       TDP2Invite DP2Invite @ bufferRF;
  236       // Check the DP2P Invite validity
  237       if ( PPAR == sizeof( TDP2Invite ) && DP2Invite.Header[0] == 1 && ( DP2Invite.Header[1] | DP2Invite.Header[2] ) == 0 )
  238       {
  239         // Save N address
  240         uns8 addr = DP2Invite.NADR;
  241 
  242         // Prepare DP2P Confirm
  243         TDP2Confirm DP2Confirm @ bufferRF;
  244         DP2Confirm.Header[0] = 0x03;
  245         // Encrypt the DP2P Confirm
  246         encryptByAccessPassword = TRUE;
  247         encryptBufferRF( sizeof( TDP2Confirm ) / 16 );
  248         // And finally send DP2P Confirm
  249         RFTXpacket();
  250         // Indicate reception
  251         pulseLEDG();
  252 
  253         // Finish SPI work
  254         while ( getStatusSPI() );
  255         stopSPI();
  256         // Prepare SPI Invite
  257         TSPI_TDP2Pinvite SPI_TDP2Pinvite @ bufferCOM;
  258         SPI_TDP2Pinvite.NADR = addr;
  259         SPI_TDP2Pinvite.Header[0] = 0x01;
  260         SPI_TDP2Pinvite.Header[1] = 0x00;
  261         SPI_TDP2Pinvite.Header[2] = 0x00;
  262         // Send SPI outgoing Invite packet
  263         startSPI( sizeof( TSPI_TDP2Pinvite ) );
  264       }
  265       else
  266       {
  267         // DP2P Response variable
  268         TDP2Presponse DP2Presponse @ bufferRF;
  269         // Check the DP2P Response validity
  270         if ( PPAR >= offsetof( TDP2Presponse, PDATA ) && ( DP2Presponse.Header[0] & DP2Presponse.Header[1] & DP2Presponse.Header[2] ) == 0xFF )
  271         {
  272           // SPI outgoing Response packet variable
  273           TSPI_TDP2Presponse SPI_TDP2Presponse @ bufferCOM;
  274           // Finish SPI work
  275           while ( getStatusSPI() );
  276           stopSPI();
  277           // Copy to COM buffer
  278           copyBufferRF2COM();
  279           // Report PNUM and PCMD back via SPI
  280           SPI_TDP2Presponse.DP2PresponseParams.PNUM = _PNUM;
  281           SPI_TDP2Presponse.DP2PresponseParams.PCMD = _PCMD;
  282           // Send response to SPI
  283           startSPI( PPAR );
  284           // Indicate reception
  285           pulseLEDR();
  286           pulseLEDG();
  287         }
  288       }
  289     }
  290   }
  291 }
  292 
  293 //############################################################################################