查看: 18495|回复: 17

浅谈利用协议栈中Match_Desc_req和Match_Desc_rsp服务获取remote device地址的机制

[复制链接]
shaomengchao 发表于 2010-12-5 16:30:17 | 显示全部楼层 |阅读模式
本帖最后由 shaomengchao 于 2010-12-7 23:32 编辑

近日阅读了ZStack-CC2530-2.3.1-1.4.0\Projects\zstack\Samples\GenericApp,关于GenericApp_DstAddr如何通过match获取的十分不解
在GenericApp_Init中
GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
  GenericApp_DstAddr.endPoint = 0;
  GenericApp_DstAddr.addr.shortAddr = 0;
GenericApp_DstAdd是初始化为空的

而在函数GenericApp_ProcessZDOMsgs中
switch ( inMsg->clusterID )
......
case Match_Desc_rsp:
      {
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
        if ( pRsp )
        {
          if ( pRsp->status == ZSuccess && pRsp->cnt )
          {
            GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
            GenericApp_DstAddr.addr.shortAddr = pRsp->nwkAddr;
            // Take the first endpoint, Can be changed to search through endpoints
            GenericApp_DstAddr.endPoint = pRsp->epList[0];

            // Light LED
            HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
          }
          osal_mem_free( pRsp );
        }
      }
      break;
在clusterIDr为 Match_Desc_rsp的时候完成了对GenericApp_DstAddr内容的补充。

我查找了GenericApp_ProcessZDOMsgs函数发现是在GenericApp_ProcessEvent中一旦events & SYS_EVENT_MSG且MSGpkt->hdr.event==ZDO_CB_MSG时调用的。

搜索了全部函数后发现只有ZDO_SendMsgCBs发送了ZDO_CB_MSG这个类型的消息。这下子从GenericApp一下子跳到了zdo里面来了,我的头真的有点晕了。
ZDO_SendMsgCBs这个函数会查找zdoMsgCBs链表中的成员一旦发现成员的clusterID和接受消息的clusterID相同就给相应的task发ZDO_CB_MSG消息。而在
GenericApp_Init中 有   
ZDO_RegisterForZDOMsg( GenericApp_TaskID, End_Device_Bind_rsp );
ZDO_RegisterForZDOMsg( GenericApp_TaskID, Match_Desc_rsp );
这两个函数将Match_Desc_rsp和End_Device_Bind_rsp两个clusterID和GenericApp_TaskID添加到了zdoMsgCBs链表中。
所以我相信是一定是ZDO_SendMsgCBs 根据接受到的消息的clusterID,通过osal调用GenericApp_ProcessZDOMsgs来给 GenericApp_DstAddr赋值的。

现在继续看 ZDO_SendMsgCBs,它是ZDP_IncomingData调用的。而这个ZDP_IncomingData是zdo层灵魂函数ZDApp_event_loop中的
ZDApp_ProcessOSALMsg调用的。调用前提是events & SYS_EVENT_MSG且msgPtr->event==AF_INCOMING_MSG_CMD。

而这个AF_INCOMING_MSG_CMD是个十分关键的值,它是在afIncomingData完成对数据是否是单播、组播或是广播判断后调用afBuildMSGIncoming发送给endpoint的。根据endpoint的taskid来发消息给各个循环函数比如说ZDApp_event_loop或GenericApp_ProcessEvent之类的。

讲了这么多,头都有点晕了就到过来整理下吧。
先是afIncomingData从APS层上获取了一个其他设备发过来match数据包,其中包含该设备地址(我初读zigbee协议,对match数据包是否包含设备地址信息不是太清楚,但从程序上来看好像如此)。找到相应的endpoint后调用afBuildMSGIncoming,封装成MSGPacket_后根据他的taskid(这个是zdo的taskid)和event把数据包交给了ZDP_IncomingData来处理。之后就是 ZDP_IncomingData调用ZDO_SendMsgCBs根据MSGPacket里面的clusterID将它分配给了注册在zdoMsgCBs链表里task,这里的taskid就是GenericApp_TaskID而clusterID就是Match_Desc_rsp。经过osal的运行,最后调用GenericApp_ProcessZDOMsgs将GenericApp_DstAddr的信息完成。

  关于发送Match_Desc_rsp簇的地方在GenericApp_HandleKeys里,具体的我还没有看明白就不多废话了。以上是我对协议栈的一点粗浅的认识,搞不好会有一大堆错误,厚颜无耻的贴出来的目的是希望论坛上的各位能够指出我的错误。我很喜欢论坛开源的气氛,希望大家能多多交流。


晚上看了GenericApp_HandleKeys中的ZDP_EndDeviceBindReq函数就把发送match的部分补充一下吧。
在按键扫描中GenericApp_HandleKeys一旦keys & HAL_KEY_SW_2就调用ZDP_EndDeviceBindReq函数
if ( keys & HAL_KEY_SW_4 )
    {
      HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );
      // Initiate a Match Description Request (Service Discovery)
      dstAddr.addrMode = AddrBroadcast;
      dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
      ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR,
                        GENERICAPP_PROFID,
                        GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                        GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                        FALSE );
    }
在Newnes.ZigBee.Wireless.Networks.and.Transceivers中对Match_Desc_req请求有如下描述:
The local device uses this command to find possible matched devices in another node.The local device supplies the NWK address of a remote device, the profile identifier, and the list of its own input and output clusters.The remote device will compare the local device clusters with its own clusters and reply with the list of endpoint addresses that have matched the request criteria.
正如我们所见的dstAddr设置成了广播模式,而具体在ZDP_EndDeviceBindReq函数中也完成了对ZDP_TmpBuf数组 LocalCoordinator 、SrcExtAddr 、 ep 、 ProfileID 、NumInClusters 、NumOutClusters的赋值。最后通过fillAndSend调用AF_DataRequest把这个Match_Desc_req请求广播出去。一旦remote device接收到Match_Desc_req请求,太会比较双方的 clusters然后发出自己的地址信息。而local device接收到这个地址信息按前文所说的获取remote device的地址。
outman 发表于 2010-12-6 00:29:47 | 显示全部楼层
GenericApp的网络行为,我之前有个表面的介绍。楼主的文章对这部分的原理进行了深入的探讨,多谢楼主共享自己的经验。已经为您加了五点“威望”值!

楼主从代码一层层追踪,研究得很细。但“afIncomingData”这个函数的调用在zstack中是看不到的,难免会有些疑问。提供一下我学习过程里的一点小技巧,对于zstack中的某些网络行为,不直接一层层跟踪代码,而是先做些实验,从现象入手,或者用IAR看下运行状态。然后,猜测、总结,有时候借助下协议文档。这之后再读代码进行验证。
 楼主| shaomengchao 发表于 2010-12-7 16:55:44 | 显示全部楼层
回复 2# outman

感谢奥特曼将我的帖子发到了教程里面。关于afIncomingData这个函数究竟收到的是什么,奥特曼建议我去多做做实验,这个经验很好我会好好采纳,不过之后我又看了遍协议栈,终于找到发送Match_Desc_rsp请求的位置了。
    之前关于nwk请求的认识很不全面,所以在帖子中关于协议栈在Client Services方面描述的很粗糙现特地补正。
    nwk采取的是Client Services机制,一般是Client向 Service发出XXXX_req,而Service接受请求回复XXXX_rsp给Client完成nwk的一次数据传输。
   比如说节点1根据ieee地址广播发出NWK_addr_req 请求向节点2获取nwk地址,而节点2接受到NWK_addr_req 请求后根据发送的ieee地址判断是否符合本地ieee地址,再回复给节点1NWK_addr_rsp,这个NWK_addr_rsp里面包含了节点2的地址。如此节点1获得了节点2的地址,完成了一次nwk的请求。
   而节点的匹配也是如此。
   local device里的GenericApp_HandleKeys如果检测到HAL_KEY_SW_4 的话就调用ZDP_MatchDescReq发出 Match_Desc_req请求。关于这里我发现自己的帖子贴错函数了,应该是
if ( keys & HAL_KEY_SW_4 )
    {
      HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );
      // Initiate a Match Description Request (Service Discovery)
      dstAddr.addrMode = AddrBroadcast;
      dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
      ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR,
                        GENERICAPP_PROFID,
                        GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                        GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                        FALSE );
    }
  而Match_Desc_req的命令包格式在ZigBee Specification这个文件已经说的很清楚了,由于图片上传大小限制请恕本人不能上图。

而remote device里从afIncomingData函数中获得了Match_Desc_req的命令包,根据层层的调用最后到了ZDO_ProcessMatchDescReq里,而这个函数就是完成了Match_Desc_rsp的传送。
ZDO_ProcessMatchDescReq函数是注册在CONST zdpMsgProcItem_t zdpMsgProcs[]数组里的,这个数组存储了大部分的nwk的XXX_req的处理函数,也就是说存储了大部分XXX_rsp的发送函数。
现在仔细看ZDO_ProcessMatchDescReq,挺长的但逻辑很清晰。
void ZDO_ProcessMatchDescReq( zdoIncomingMsg_t *inMsg )
{
  uint8 epCnt = 0;
  uint8 numInClusters;
  uint16 *inClusters = NULL;
  uint8 numOutClusters;
  uint16 *outClusters = NULL;
  epList_t *epDesc;
  SimpleDescriptionFormat_t *sDesc = NULL;
  uint8 allocated;
  uint8 *msg;
  uint16 aoi;
  uint16 profileID;

  // Parse the incoming message
  msg = inMsg->asdu;
  aoi = BUILD_UINT16( msg[0], msg[1] );         //这个就是   NWK_BROADCAST_SHORTADDR
  profileID = BUILD_UINT16( msg[2], msg[3] );    //这个是  GENERICAPP_PROFID,这些全是ZDP_MatchDescReq的参数
  msg += 4;
                                                                                                               
  if ( ADDR_BCAST_NOT_ME == NLME_IsAddressBroadcast(aoi) )          //广播地址是0xfffd,如果当前device状态RxOnWhenIdle == TRUE 就能接受到
  {                                                                                                         // 如果当前device不满足的话发送 ZDP_INVALID_REQTYPE标志给原来的device
    ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_INVALID_REQTYPE,
                          ZDAppNwkAddr.addr.shortAddr, 0, NULL, inMsg->SecurityUse );
    return;
  }
  else if ( (ADDR_NOT_BCAST == NLME_IsAddressBroadcast(aoi)) && (aoi != ZDAppNwkAddr.addr.shortAddr) )//如果不是广播模式且nwk地址和自己不
  {                                                                                                                                                                     //匹配ZDP_INVALID_REQTYPE         
    ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_INVALID_REQTYPE,
                             ZDAppNwkAddr.addr.shortAddr, 0, NULL, inMsg->SecurityUse );
    return;
  }

  if ((numInClusters = *msg++) &&                                 //复制ZDP_MatchDescReq发送的GenericApp_ClusterList
      (inClusters = (uint16*)osal_mem_alloc( numInClusters * sizeof( uint16 ) )))
  {
    msg = ZDO_ConvertOTAClusters( numInClusters, msg, inClusters );
  }
  else
  {
    numInClusters = 0;
  }

  if ((numOutClusters = *msg++) &&
      (outClusters = (uint16 *)osal_mem_alloc( numOutClusters * sizeof( uint16 ) )))
  {
    msg = ZDO_ConvertOTAClusters( numOutClusters, msg, outClusters );
  }
  else
  {
    numOutClusters = 0;
  }

  // First count the number of endpoints that match.
  epDesc = epList;
  while ( epDesc )      //扫描本节点的全部endpoint
  {
    // Don't search endpoint 0 and check if response is allowed
    if ( epDesc->epDesc->endPoint != ZDO_EP && (epDesc->flags&eEP_AllowMatch) )
    {
      if ( epDesc->pfnDescCB )    //一般都是空的
      {
        sDesc = (SimpleDescriptionFormat_t *)epDesc->pfnDescCB( AF_DESCRIPTOR_SIMPLE, epDesc->epDesc->endPoint );
        allocated = TRUE;
      }
      else
      {
        sDesc = epDesc->epDesc->simpleDesc;
        allocated = FALSE;
      }

      if ( sDesc && sDesc->AppProfId == profileID )   //一旦profileID匹配 且endpoint的ClusterList和ZDP_MatchDescReq发送的GenericApp_ClusterList
      {                                                                         //中有相同的endpiont值
        uint8 *uint8Buf = (uint8 *)ZDOBuildBuf;

        // Are there matching input clusters?
        if ((ZDO_AnyClusterMatches( numInClusters, inClusters,
                   sDesc->AppNumInClusters, sDesc->pAppInClusterList )) ||
            // Are there matching output clusters?
            (ZDO_AnyClusterMatches( numOutClusters, outClusters,
                   sDesc->AppNumOutClusters, sDesc->pAppOutClusterList )))
        {
          // Notify the endpoint of the match.
          uint8 bufLen = sizeof( ZDO_MatchDescRspSent_t ) + (numOutClusters + numInClusters) * sizeof(uint16);
          ZDO_MatchDescRspSent_t *pRspSent = (ZDO_MatchDescRspSent_t *) osal_msg_allocate( bufLen );

          if (pRspSent)
          {
            pRspSent->hdr.event = ZDO_MATCH_DESC_RSP_SENT;
            pRspSent->nwkAddr = inMsg->srcAddr.addr.shortAddr;
            pRspSent->numInClusters = numInClusters;
            pRspSent->numOutClusters = numOutClusters;

            if (numInClusters)
            {
              pRspSent->pInClusters = (uint16*) (pRspSent + 1);
              osal_memcpy(pRspSent->pInClusters, inClusters, numInClusters * sizeof(uint16));
            }
            else
            {
              pRspSent->pInClusters = NULL;
            }

            if (numOutClusters)
            {
              pRspSent->pOutClusters = (uint16*)(pRspSent + 1) + numInClusters;
              osal_memcpy(pRspSent->pOutClusters, outClusters, numOutClusters * sizeof(uint16));
            }
            else
            {
              pRspSent->pOutClusters = NULL;
            }

            osal_msg_send( *epDesc->epDesc->task_id, (uint8 *)pRspSent );  //这个我十分不解,它发送了ZDO_MATCH_DESC_RSP_SENT消息
          }                                                                                                            //但在GenericApp没有相关参数,不过这个不影响我们的分析

          uint8Buf[epCnt++] = sDesc->EndPoint;   //非常关键的匹配EndPoint列表,这个就是Match_Desc_rsp回传的内容之一
        }
      }

      if ( allocated )
      {
        osal_mem_free( sDesc );
      }
    }
    epDesc = epDesc->nextDesc;
  }

  // Send the message only if at least one match found.
  if ( epCnt )  //有匹配的EndPoint
  {
    if ( ZSuccess == ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_SUCCESS,
              ZDAppNwkAddr.addr.shortAddr, epCnt, (uint8 *)ZDOBuildBuf, inMsg->SecurityUse ) )    //在这里本节点的shortAddr作为参数发送了,还有
    {                                                                                                                                                 //匹配EndPoint列表
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "Match Desc Req", "Rsp Sent" );
#endif
    }
  }
  else
  {
#if defined( LCD_SUPPORTED )
    HalLcdWriteScreen( "Match Desc Req", "Non Matched" );
#endif
  }

  if ( inClusters != NULL )
  {
    osal_mem_free( inClusters );
  }
  
  if ( outClusters != NULL )
  {
    osal_mem_free( outClusters );
  }
}

而Match_Desc_rsp的帧格式同样在ZigBee Specification中,有兴趣的同学可以自己去看看。


现在功德圆满了,local device发送 Match_Desc_rep给remote device,remote device回应了Match_Desc_rsp给local device,我们再回过头看看local device如何处理获取的信息的。
GenericApp_ProcessZDOMsgs中
switch ( inMsg->clusterID )
......
case Match_Desc_rsp:
      {
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
        if ( pRsp )
        {
          if ( pRsp->status == ZSuccess && pRsp->cnt )   //这个很关键,之前ZDO_ProcessMatchDescReq中如果不匹配也会发送Match_Desc_rsp数据不过
          {                                                                          //它们的status被改成了ZDP_INVALID_REQTYPE或cnt=0,所以函数直接忽视了。
            GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
            GenericApp_DstAddr.addr.shortAddr = pRsp->nwkAddr;   //获得地址
            // Take the first endpoint, Can be changed to search through endpoints
            GenericApp_DstAddr.endPoint = pRsp->epList[0];         //获得匹配endPoint,当然不一定只有一个

            // Light LED
            HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
          }
          osal_mem_free( pRsp );
        }
      }
      break;

终于讲完了,了解了Client Services后以前很多糊里糊涂的地方一下子豁然开朗了,还有ZIGBEE SPECIFICATION这个600多页的文档真的很不错。
outman 发表于 2010-12-7 17:09:33 | 显示全部楼层
楼主辛苦了,建议楼主把图片转为png格式,或者用ps等工具转为小分辨率的图片,这个应该不成问题的。
outman 发表于 2010-12-7 17:12:25 | 显示全部楼层
另外,建议楼主从内容中提炼出一个标题。这么丰富的内容,这个标题有点不配
 楼主| shaomengchao 发表于 2010-12-7 23:27:16 | 显示全部楼层
回复 5# outman


   其实我就是根据GenericApp_DstAddr信息的获取来说一说Match_Desc_req和Match_Desc_rsp的机制。既然奥特曼这么说了,我就改下标题吧。
sendoc 发表于 2010-12-8 16:00:37 | 显示全部楼层
不错,正想把绑定看看,没想就看到大神出现。
顶下。。
 楼主| shaomengchao 发表于 2010-12-8 17:48:33 | 显示全部楼层
回复 7# sendoc

大神太夸张了,关于zigbee我只是个初学者。以前从论坛上学了不少东西,现在就是把自己的学习经历分享出来。大家一起学习才能进步嘛。
li469173166 发表于 2010-12-18 22:04:00 | 显示全部楼层
楼主辛苦了!!  请问“ZIGBEE SPECIFICATION”哪儿有下载啊?给个链接吧。
 楼主| shaomengchao 发表于 2010-12-18 22:35:54 | 显示全部楼层
回复 9# li469173166
复制粘贴到地址栏里就可以了
http://www.armsky.net/UpFiles/DownLoad/DownUrl/200805/ZigBee-2007协议规范(英文).rar?8b4b460e0ab3f770931e7e7f0baed833
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表