Z-stack终端加入网络与阅读协议栈的方法

来源:互联网 发布:淘宝上怎样收藏店铺 编辑:程序博客网 时间:2024/06/14 14:33

 终端加入网络,学习ZgiBee已经有两个月了;如何读像协议栈这种大量的代码;

NUM_DISC_ATTEMPTS 这个参数觉得了发送beancon的次数,默认值是2,发送3次(扫描三次网络后)加入网络。

1、加入网络的过程得回调函数ZDO_NetworkDiscoveryConfirmCB()开始说明
ZDO_NetworkDiscoveryConfirmCB();
这是终端的回调函数
ZStatus_t ZDO_NetworkDiscoveryConfirmCB(uint8 status)
{
  osal_event_hdr_t msg;

  // If Scan is initiated by ZDO_MGMT_NWK_DISC_REQ
  // Send ZDO_MGMT_NWK_DISC_RSP back
#if/**/defined ( ZDO_MGMT_NWKDISC_RESPONSE )
  if ( zdappMgmtNwkDiscReqInProgress )
  {
    zdappMgmtNwkDiscReqInProgress = false;
    ZDO_FinishProcessingMgmtNwkDiscReq();
  }
  else
#endif
  {
    // Pass the confirm to another task if it registers the callback
    // Otherwise, pass the confirm to ZDApp.
    if (zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID] != NULL )
    {
      zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID]( (void*)&status );
    }
    else
    {//前面的不太懂,但是这并没有我需要的内容,单步调试走到这里,接着跳到其函数
      // Otherwise, send scan confirm to ZDApp task to proceed
      msg.status = ZDO_SUCCESS;
      ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(osal_event_hdr_t), (uint8 *)&msg );
    }
  }
  return (ZSuccess);
}  // ZDO_NetworkDiscoveryConfirmCB


2、跳到了ZDA的loop函数,
const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,//跳到这里了
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
  SerialApp_ProcessEvent,
  NodeMgmtApp_ProcessEvent  //131204zcl:节点管理应用  事件处理函数
};
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
  uint8 *msg_ptr;

  if ( events & SYS_EVENT_MSG )
  {
    while ( (msg_ptr = osal_msg_receive( ZDAppTaskID )) )
    {
      ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );

      // Release the memory
      osal_msg_deallocate( msg_ptr );
    }

    // Return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }
  case ZDO_NWK_DISC_CNF://从ZDO_NetworkDiscoveryConfirmCB()跳过来
      if (devState != DEV_NWK_DISC)
        break;

      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        // Process the network discovery scan results and choose a parent
        // device to join/rejoin itself
        networkDesc_t *pChosenNwk;
        if ( ( (pChosenNwk = ZDApp_NwkDescListProcessing()) != NULL ) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )//我们首先判断是否有合适的网络,网络的此时是否大于2次,这样子才对网络进行判断。
        {
          if ( devStartMode == MODE_JOIN )
          {
            devState = DEV_NWK_JOINING;

            ZDApp_NodeProfileSync( pChosenNwk->stackProfile);

            if ( NLME_JoinRequest( pChosenNwk->extendedPANID, pChosenNwk->panId,
                                  pChosenNwk->logicalChannel,
                                  ZDO_Config_Node_Descriptor.CapabilityFlags,
                                  pChosenNwk->chosenRouter, pChosenNwk->chosenRouterDepth ) != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // if ( devStartMode == MODE_JOIN )
          else if ( devStartMode == MODE_REJOIN )
          {
            ZStatus_t rejoinStatus;

            devState = DEV_NWK_REJOIN;

            // Before trying to do rejoin, check if the device has a valid short address
            // If not, generate a random short address for itself
            if ( _NIB.nwkDevAddress == INVALID_NODE_ADDR )
            {
              uint16 commNwkAddr;

              // Verify if the Network address has been commissioned by external tool
              if ( ( osal_nv_read( ZCD_NV_COMMISSIONED_NWK_ADDR, 0,
                                 sizeof(commNwkAddr),
                                 (void*)&commNwkAddr ) == ZSUCCESS )   &&
                   ( commNwkAddr != INVALID_NODE_ADDR ) )
              {
                _NIB.nwkDevAddress = commNwkAddr;

                // clear Allocate address bit because device has a commissioned address
                _NIB.CapabilityFlags &= ~CAPINFO_ALLOC_ADDR;
              }
              else
              {
                _NIB.nwkDevAddress = osal_rand();
              }

              ZMacSetReq( ZMacShortAddress, (byte*)&_NIB.nwkDevAddress );
            }

            // Check if the device has a valid PanID, if not, set it to the discovered Pan
            if ( _NIB.nwkPanId == INVALID_PAN_ID )
            {
              _NIB.nwkPanId = pChosenNwk->panId;
              ZMacSetReq( ZMacPanId, (byte*)&(_NIB.nwkPanId) );
            }

            tmp = true;
            ZMacSetReq( ZMacRxOnIdle, &tmp ); // Set receiver always on during rejoin

            // Perform Secure or Unsecure Rejoin depending on available configuration
            if ( ZG_SECURE_ENABLED && ( ZDApp_RestoreNwkKey() == TRUE ) )
            {
              rejoinStatus = NLME_ReJoinRequest( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel);
            }
            else
            {
              rejoinStatus = NLME_ReJoinRequestUnsecure( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel);
            }

            if ( rejoinStatus != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // else if ( devStartMode == MODE_REJOIN )

          // The receiver is on, turn network layer polling off.
          if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE )
          {
            // for an End Device with NO Child Table Management process or for a Router
            if ( ( ZG_DEVICE_RTR_TYPE )  ||
#if/**/defined ( ZIGBEE_CHILD_AGING )
                 ( (ZG_DEVICE_ENDDEVICE_TYPE) && ( zgChildAgingEnable == FALSE ) ) )
#else
                 (ZG_DEVICE_ENDDEVICE_TYPE) )
#endif // ZIGBEE_CHILD_AGING
            {
              NLME_SetPollRate( 0 );
              NLME_SetQueuedPollRate( 0 );
              NLME_SetResponseRate( 0 );
            }
          }
          else
          {
            if ( (ZG_SECURE_ENABLED) && (devStartMode == MODE_JOIN) )
            {
              ZDApp_SavedPollRate = zgPollRate;
              NLME_SetPollRate( zgRejoinPollRate );
            }
          }
        }
        else
        {
          //HalLedSet ( HAL_LED_2, HAL_LED_MODE_FLASH );  // 20131118zcl:闪烁黄灯指示发现网络失败
          //HalLedBlink ( HAL_LED_2, 100, 50, 500 );
          if ( continueJoining )
          {
    #if/**/defined ( MANAGED_SCAN )
            ZDApp_NetworkInit( MANAGEDSCAN_DELAY_BETWEEN_SCANS );
    #else
            zdoDiscCounter++;
            
            if(zdoDiscCounter < 11)
            {
             ZDApp_NetworkInit( (uint16)(BEACON_REQUEST_DELAY
                   + ((uint16)(osal_rand()& BEACON_REQ_DELAY_MASK))) );
            }
    #endif
          }
        }
      }
      break;
3、进入ZDApp_NwkDescListProcessing()这个函数
networkDesc_t* ZDApp_NwkDescListProcessing(void)
{
  networkDesc_t *pNwkDesc;
  uint8 i, ResultCount = 0;
  uint8 stackProfile;
  uint8 stackProfilePro;
  uint8 selected;

  // Count the number of nwk descriptors in the list
  pNwkDesc = nwk_getNwkDescList();
  while (pNwkDesc)
  {
    ResultCount++;
    pNwkDesc = pNwkDesc->nextDesc;
  }//将所有网络的信息放进去了,找到我们所需要的信息,看是否有一个网络,还是两个网络,OK我们来测试一下

  // process discovery results
  stackProfilePro = FALSE;
  selected = FALSE;


  for ( stackProfile = 0; stackProfile < STACK_PROFILE_MAX; stackProfile++ )
  {
    pNwkDesc = nwk_getNwkDescList();
    for ( i = 0; i < ResultCount; i++, pNwkDesc = pNwkDesc->nextDesc )
    {
      /*140215zcl:取消掉扩展PANID的判断。这样路由设备的终端子节点才不会在父节点改变PANID后跟过去。


接下来判断了Bencon Paylodad的内容,
从他的PANID开始判断,依次往后进行判断,Dev.Cap,Dev.Depth 等等依次判断。
问题就来了:为什么它没有从物理层开始那,没有判断Superframe specification那?
首先要知道TI的协议栈并不是完全开源的协议栈,那么这些判断都是在低层就完成的。
      if ( nwk_ExtPANIDValid( ZDO_UseExtendedPANID ) == true )
      {
         If the extended Pan ID is commissioned to a non zero value
         Only join the Pan that has match EPID
        if ( osal_ExtAddrEqual( ZDO_UseExtendedPANID, pNwkDesc->extendedPANID) == false )
          continue;

      }
      else */if ( zgConfigPANID != 0xFFFF )
      {
        // PAN Id is preconfigured. check if it matches
        if ( pNwkDesc->panId != zgConfigPANID )
          continue;
      }

      // check that network is allowing joining
      if ( ZSTACK_ROUTER_BUILD )
      {
        if ( stackProfilePro == FALSE )
        {
          if ( !pNwkDesc->routerCapacity )
          {
            continue;
          }
        }
        else
        {
          if ( !pNwkDesc->deviceCapacity )
          {
            continue;
          }
        }
      }
      else if ( ZSTACK_END_DEVICE_BUILD )
      {
        if ( !pNwkDesc->deviceCapacity )
        {
          continue;
        }
      }

      // check version of zigbee protocol
      if ( pNwkDesc->version != _NIB.nwkProtocolVersion )
        continue;

      // check version of stack profile
      if ( pNwkDesc->stackProfile != zgStackProfile  )
      {
        if ( ((zgStackProfile == HOME_CONTROLS) && (pNwkDesc->stackProfile == ZIGBEEPRO_PROFILE))
            || ((zgStackProfile == ZIGBEEPRO_PROFILE) && (pNwkDesc->stackProfile == HOME_CONTROLS))  )
        {
          stackProfilePro = TRUE;
        }

        if ( stackProfile == 0 )
        {
          continue;
        }
      }

      break;
    }

    if (i < ResultCount)
    {
     selected = TRUE;
      break;
    }

    // break if selected or stack profile pro wasn't found
    if ( (selected == TRUE) || (stackProfilePro == FALSE) )
    {
      break;
    }
  }

  if ( i == ResultCount )
  {
    return (NULL);   // couldn't find appropriate PAN to join !
  }
  else
  {
    return (pNwkDesc);//最后我们我们找到了一个网络。
  }
}// ZDApp_NwkDescListProcessing()
这次是师傅带着我过完了终端加入网络过程,有很多的技巧需要注意。
1、Zstack是一个半开源的协议栈,有些数据包的解析(特别是低层数据包的解析),我们往往是看不到的,我们看到的是从网络层的数据包看的,那么我们应该如何去学习那?
答案:刚开始认为编程应该是一句一句的读懂, 这样子做事没错的,但这是学习裸机的方法,而面对这上万行的代码,又有人说我们就是看API,这种说法也是很片面的,只看API你觉得你有信心看完吗?有意思吗?
正确的方式应该是结合这我们的捕包工具的数据包,来分析一包数据是有谁发起的,函数是在哪里执行的,在哪里把值給判断出来的,缩小范围到几个API函数,进而对着几个API函数分析,有时API函数也会非常的复杂,我们可以通过设置端点,观察状态值(我们认为的),观察结构体(里面所有的数据都在里面),结合逻辑去分析。

当你在看一个简单的函数时,你需要看懂每一个标志位,一步一步的走通;
当你在看一个协议栈时,你更应该从协议栈的总体高度去分析问题,从功能性上分析实现的过程,而不是仅仅的去考虑函数的具体实现,寄存器的值的。

0 0