更多的Contiki协议栈知识,请参考索引目录:
《Contiki协议栈:索引目录》
1 概述
在Contiki中,默认的变色龙模块是chameleon_bitopt,它能让不同层的协议头进行跨层位组合处理,减小总体头部尺寸。
我们也可以在contiki-conf.h中自定义变色龙头处理模块为chameleon_raw:
#define CHAMELEON_CONF_MODULE chameleon_raw
之所以将chamelon_raw叫做原始变色龙,因为它是chameleon_bitopt的简化版,未进行跨层位组合。
相关源码位于:contiki/core/net/rime/chameleon_raw.[ch]
。
2 相关定义
raw_hdr
struct raw_hdr {
uint8_t channel[2];
};
raw_hdr里面定义了两个字节的channel,它是用来存放通道号的。
chameleon_raw
CC_CONST_FUNCTION struct chameleon_module chameleon_raw = {
input,
output,
hdrsize
};
定义了变色龙处理模块的结构体。
2 相关函数
hdrsize
static int hdrsize(const struct packetbuf_attrlist *a)
{
int size, len;
/* 通过这个通道中的所有属性,计算最终的头部尺寸。 */
size = 0;
for(; a->type != PACKETBUF_ATTR_NONE; ++a) {
#if CHAMELEON_WITH_MAC_LINK_ADDRESSES
if(a->type == PACKETBUF_ADDR_SENDER ||
a->type == PACKETBUF_ADDR_RECEIVER) {
/* Let the mac layer handle the sender and receiver */
continue;
}
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
len = a->len;
if(len < 8) { // 这里有疑问
len = 8;
}
size += len;
}
return size / 8; // size的单位是bit,除8将其转为字节
}
函数的实现过程很简单:累加属性链表中记录的属性的长度,然后将单位由bit转换为byte。
这段代码将来可能会产生bug,因为在对size累加时,只是将len < 8的包属性的值记为8,没有考虑到今后的包属性可能大于8。我已经在contiki的github仓库中提出了该BUG,并修改了代码,且已经在申请了pull request(请看代码改动),希望能被采纳吧!
output
static int
output(struct channel *c)
{
const struct packetbuf_attrlist *a;
int byteptr, len;
uint8_t *hdrptr;
struct raw_hdr *hdr;
/* 通过计算本通道中对应属性,得出最终头部尺寸. */
if(packetbuf_hdralloc(c->hdrsize + sizeof(struct raw_hdr)) == 0) {
return 0;
}
hdr = (struct raw_hdr *)packetbuf_hdrptr(); // 先存放通道号
hdr->channel[0] = c->channelno & 0xff; //低8位
hdr->channel[1] = (c->channelno >> 8) & 0xff; // 高8位
hdrptr = ((uint8_t *)packetbuf_hdrptr()) + sizeof(struct raw_hdr);
byteptr = 0;
/* 根据属性链表,以此将属性数组中对应值写入头部 */
for(a = c->attrlist; a->type != PACKETBUF_ATTR_NONE; ++a) {
#if CHAMELEON_WITH_MAC_LINK_ADDRESSES
if(a->type == PACKETBUF_ADDR_SENDER ||
a->type == PACKETBUF_ADDR_RECEIVER) {
/* Let the link layer handle sender and receiver */
continue;
}
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
// 为啥len是这样的?估计跟上面的问题是关联的。
len = (a->len & 0xf8) + ((a->len & 7) ? 8: 0);
if(PACKETBUF_IS_ADDR(a->type)) {
// 如果对应的属性是地址属性,将其存入头部
const linkaddr_t *linkaddr;
linkaddr = packetbuf_addr(a->type);
hdrptr[byteptr] = linkaddr->u8[0];
hdrptr[byteptr + 1] = linkaddr->u8[1];
} else {
packetbuf_attr_t val;
val = packetbuf_attr(a->type);
memcpy(&hdrptr[byteptr], &val, len / 8);
}
byteptr += len / 8;
}
return 1; /* Send out packet */
}
该函数负责根据属性链表中的type和len,依次将属性数组中对应属性压入都最终的头部。在压入头部前,先压入通道号,这是因为在解析头部时,会根据通道号查找到对应的通道,然后根据通道的属性链表和头长度做相应的处理。
看到这里,我们难免会有一点小兴奋,因为我们终于知道packetbuf中的那些函数该如何应用了。可以回头再看看之前的博客。
input
static struct channel *input(void)
{
const struct packetbuf_attrlist *a;
int byteptr, bitptr, len;
uint8_t *hdrptr;
struct raw_hdr *hdr;
struct channel *c;
/* 头部的前两个字节是发送端压入的通道号,先把它解析出来 */
hdr = (struct raw_hdr *)packetbuf_dataptr();
if(packetbuf_hdrreduce(sizeof(struct raw_hdr)) == 0) {
return NULL;
}
/* 根据通道号查找对应的通道 */
c = channel_lookup((hdr->channel[1] << 8) + hdr->channel[0]);
if(c == NULL) {
return NULL;
}
hdrptr = packetbuf_dataptr();
if(packetbuf_hdrreduce(c->hdrsize) == 0) {
return NULL;
}
byteptr = bitptr = 0;
for(a = c->attrlist; a->type != PACKETBUF_ATTR_NONE; ++a) {
/* 根据通道中的属性链表,依次解析头部信息到属性数组中 */
#if CHAMELEON_WITH_MAC_LINK_ADDRESSES
if(a->type == PACKETBUF_ADDR_SENDER ||
a->type == PACKETBUF_ADDR_RECEIVER) {
/* Let the link layer handle sender and receiver */
continue;
}
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
len = (a->len & 0xf8) + ((a->len & 7) ? 8: 0);
if(PACKETBUF_IS_ADDR(a->type)) {
const linkaddr_t addr;
memcpy((uint8_t *)&addr, &hdrptr[byteptr], len / 8);
packetbuf_set_addr(a->type, &addr);
} else {
packetbuf_attr_t val = 0;
memcpy((uint8_t *)&val, &hdrptr[byteptr], len / 8);
packetbuf_set_attr(a->type, val);
}
byteptr += len / 8;
}
return c;
}
output函数的逆过程,负责将packetbuf中的信息转换为包属性,存放到属性数组中。
4 小结
关于这次留的坑,今后补上。
到了这里,我们终于可以packetbuf中的接口应该如何使用了,再赶紧回顾一下之前的日志吧!