Contiki学习笔记:Rime协议栈通道channel

5068阅读 1评论2012-07-10 Jelline
分类:嵌入式

摘要:

    Rime协议栈所有通信都是通过通道channel标识的,本文介绍通道channel结构体及相关函数,包括channel_init、channel_open、channel_close、channel_lookup、channel_set_attributes。


PS:channel有通道、信道、渠道、频道等含义,本文将其翻译为通道,理由是Rime中channel是逻辑上的,并不是真实的信道(信道指信号的传输媒质)。


一、概述

    Rime协议栈所有通信都是通过通道channel标识的,即两个应用进程通信需要相同的channel。想想套接字编程,两个进程通信具有相同的端口号。

    channel由两字节标识,小于128保留给系统使用(如shell、系统应用程序)。系统所有channel连成一个链表,channel结构体源码如下:

  1. struct channel
  2. {
  3.   struct channel *next;
  4.   uint16_t channelno;
  5.   const struct packetbuf_attrlist *attrlist;
  6.   uint8_t hdrsize;
  7. };

  8. struct packetbuf_attrlist
  9. {
  10.   uint8_t type;
  11.   uint8_t len;
  12. };

其示意图如下(值得注意的是attrlist指向的是packetbuf_attrlist数组):

channel链表示意图源文件 Rime协议栈通道channel.rar   

图1 channel链表示意图

结构体channel各成员变量含义如下:

next

    指向下一个channel,最后一个channel的next指向空。

channelno

    用两字节标识不同的channel。

attrlist

    指向由类型type和长度len两个成员变量组成的结构体数组packetbuf_attrlist[]。

hdrsize

    hdrsize是指数据报的头部大小。即为packetbuf_attrlist类型的数组attrlist[]中所有结构体成员变量的长度len的总和。详情见2.7。


二、相关操作

2.1 声明链表channel_list

    宏LIST(name)用来声明一个结构体类型的链表,并且该结构体的第一个成员变量必须是指针,宏LIST用该指针形成链表。除些之外,该链表被声明为静态变量(不用导出符号,从而方便其他模块使用)。部分源码如下:

  1. //LIST(channel_list);
  2. #define LIST(name) \
  3.     static void *LIST_CONCAT(name,_list) = NULL; \
  4.     static list_t name = (list_t)&LIST_CONCAT(name,_list)

  5. typedef void *list_t;

LIST_CONCAT宏用于连结两个字符串,源码如下:

  1. #define LIST_CONCAT(s1, s2) LIST_CONCAT2(s1, s2)
  2. #define LIST_CONCAT2(s1, s2) s1##s2

    在C语言中,##表示连结(concatenate),#表示字符串化。那么,把上述语句翻译下,就是这样了:

  1. LIST(channel_list);
  2. static void *channel_list_list = NULL;
  3. static list_t channel_list = (list_t) &channel_list_list;

    这里涉及到指针的指针,channel_list是指向channel_list_list的指针,channel_list_list指向NULL指针。见下图:

图2 channel_list示意图

2.2 channel初始化channel_init

    LIST(channel_list)只是声明了链表,使用之前需先初始化,即channel_init。结合图2可知,channel_init实际上是将channel_list指向的值设为NULL(不再是channel_list_list)。源代码如下:

  1. void channel_init(void)
  2. {
  3.   list_init(channel_list);
  4. }

  5. void list_init(list_t list)
  6. {
  7.   *list = NULL;
  8. }

2.3 channel_open

channel_open用于打开一个通道,实际上设置通道号并将通道c加到channel链表中,源代码如下:

  1. void channel_open(struct channel *c, uint16_t channelno)
  2. {
  3.   c->channelno = channelno;
  4.   list_add(channel_list, c);
  5. }

list_add将节点加入到链表末尾,list_add源代码如下:

  1. void list_add(list_t list, void *item)
  2. {
  3.   struct list *l;
  4.   list_remove(list, item); /* Make sure not to add the same element twice */

  5.   ((struct list*)item)->next = NULL;

  6.   l = list_tail(list);
  7.   if(l == NULL)
  8.   {
  9.     *list = item;
  10.   }
  11.   else
  12.   {
  13.     l->next = item;
  14.   }
  15. }

2.4 channel_close

channel_close用于关闭一个通道,即从链表删除相应的channel。channel_close源代码如下: 

  1. void channel_close(struct channel *c)
  2. {
  3.   list_remove(channel_list, c);
  4. }

list_remove用于从链表移除指定的元素,源代码如下:

  1. void list_remove(list_t list, void *item)
  2. {
  3.   struct list *l, *r;

  4.   if(*list == NULL)
  5.   {
  6.     return ;
  7.   }

  8.   r = NULL;
  9.   for(l = *list; l != NULL; l = l->next)
  10.   {
  11.     if(l == item)
  12.     {
  13.       if(r == NULL)
  14.       {
  15.         *list = l->next; /* First on list */
  16.       }
  17.       else
  18.       {
  19.         r->next = l->next; /* Not first on list */
  20.       }
  21.       l->next = NULL;
  22.       return ;
  23.     }
  24.     r = l;
  25.   }
  26. }

2.5 channel_lookup

查找通道号对应的channel,源代码如下:

  1. struct channel *channel_lookup(uint16_t channelno)
  2. {
  3.   struct channel *c;
  4.   for(c = list_head(channel_list); c != NULL; c = list_item_next(c))
  5.   {
  6.     if(c->channelno == channelno)
  7.     {
  8.       return c;
  9.     }
  10.   }
  11.   return NULL;
  12. }

list_head用于返回链表的第一个元素,list_item_next用于返回当前元素的下一个元素,源代码如下:

  1. void *list_head(list_t list)
  2. {
  3.   return *list;
  4. }

  5. void *list_item_next(void *item)
  6. {
  7.   return item == NULL ? NULL : ((struct list*)item)->next;
  8. }

2.6 channel_set_attributes

    channel_set_attributes用于设置通道的属性,首先用channel_lookup函数找到通道号对应的channel。而后设置attrlist、hdrsize成员变量。源代码如下: 

  1. void channel_set_attributes(uint16_t channelno, const struct packetbuf_attrlist attrlist[])
  2. {
  3.   struct channel *c;
  4.   c = channel_lookup(channelno);
  5.   if(c != NULL)
  6.   {
  7.     c->attrlist = attrlist;
  8.     c->hdrsize = chameleon_hdrsize(attrlist);
  9.   }
  10. }

chameleon_hdrsize用于计算channel的hdrsize,源代码如下:

  1. //chameleon_hdrsize(attrlist);
  2. int chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
  3. {
  4.   return CHAMELEON_MODULE.hdrsize(attrlist);
  5. }

CHAMELEON_MODULE由一系列条件编译定义,如下:

  1. #ifndef CHAMELEON_MODULE
  2.   #ifdef CHAMELEON_CONF_MODULE
  3.     #define CHAMELEON_MODULE CHAMELEON_CONF_MODULE
  4.   #else
  5.     #define CHAMELEON_MODULE chameleon_bitopt
  6.   #endif
  7. #endif

    可见,可以自定义CHAMELEON_MODULE,即定义CHAMELEON_CONF_MODULE,默认情况是chameleon_bitopt,其源代码如下:

  1. CC_CONST_FUNCTION struct chameleon_module chameleon_bitopt =
  2. {
  3.   unpack_header, pack_header, header_size
  4. };

chameleon_bitopt是chameleon_module结构体类型,源代码如下:

  1. struct chameleon_module
  2. {
  3.   struct channel *(*input)(void);
  4.   int(*output)(struct channel*);
  5.   int(*hdrsize)(const struct packetbuf_attrlist*);
  6. };

    可见,chameleon_module结构体包含3个函数指针,函数unpack_header、pack_header、header_size在core/net/rime/chameleon-bitopt.c实现,这里先不介绍,待用到再介绍。

2.7 hdrsize计算

    透过2.6的分析,chameleon_hdrsize实际上是调用header_size函数。去除一些无关的注释,header_size源码如下:

  1. //chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
  2. static int header_size(const struct packetbuf_attrlist *a)
  3. {
  4.   int size, len;
  5.   size = 0;
  6.   
  7.   for(; a->type != PACKETBUF_ATTR_NONE; ++a)
  8.   {
  9.     #if CHAMELEON_WITH_MAC_LINK_ADDRESSES
  10.       if(a->type == PACKETBUF_ADDR_SENDER || a->type == PACKETBUF_ADDR_RECEIVER)
  11.       {
  12.         continue;
  13.       }
  14.     #endif

  15.     len = a->len;
  16.     size += len;
  17.   }
  18.   return size;
  19. }

    显然,channel的hdrsize为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。

上一篇:Contiki学习笔记:Contiki名称来源
下一篇:Contiki学习笔记:Rime协议栈单跳单播建立连接rucb_open

文章评论