Contiki协议栈Rime:头部转换模块chameleon-bitopt

本文介绍Chameleon-BitOpt头部转换模块的工作原理和技术细节,包括如何通过位组合减少头部尺寸,提高网络传输效率。文章详细阐述了关键数据结构与函数,如bitopt_hdr、set_bits_in_byte等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 概述

  chameleon-bitopt的直译是变色龙位操作,但是我们在这里将它叫做头部转换模块。之所以把它叫做头部转换模块,是因为adam多次在论文中提到的头部转换模块这个概念。
  与原始变色龙不同的是,头部转换模块不仅将Rime传递下来的包属性封装成头部,还对其进行了优化,有效减小了头部尺寸。
  从Rime传递下来的很多包属性,其长度都不足8bit,却在头部中占了整整一个字节,这是一种非常浪费的行为。头部转换模块将不同的属性按位进行组合,极大地减小了头部尺寸。

  目前Rime协议栈中各子协议包属性的bit长度如下:

PrimitiveAttributeBitsScope
ibcSingle-hop senderAddr len1
ucSingle-hop receiverAddr len1
stucRetransmissions41
rucSingle-hop reliable10
rucSingle-hop packet type11
rucSingle-hop packet ID21
rucMax. rexmit40
rmhEnd-to-end senderAddr len2
rmhEnd-to-end receiverAddr len2
rmhTime to live52

其中,Addr len的长度是8bit。

2 相关定义

struct bitopt_hdr

struct bitopt_hdr {
  uint8_t channel[2];
};

类似于原始变色龙中的raw_hdr,用来定义一个2个字节大小的通道号。

BITOPT_HDR_SIZE

#define BITOPT_HDR_SIZE 2

定义本模块头部大小,占2个字节,其实就是sizeof(struct bitopt_hdr)

chameleon_bitopt

CC_CONST_FUNCTION struct chameleon_module chameleon_bitopt = {
  unpack_header,
  pack_header,
  header_size
};

定义了变色龙处理模块的结构体。

3 相关函数

le16_read

static uint16_t CC_INLINE le16_read(const void *ptr)
{
  const uint8_t *p = (const uint8_t *)ptr;
  return ((uint16_t)p[1] << 8) | p[0];
}

字节序转换,将ptr所指向的内存处存放的2个字节的值转换为16bit的主机字节序(小端),并返回转换后的结果。

le16_write

static void CC_INLINE le16_write(void *ptr, uint16_t v)
{
  uint8_t *p = (uint8_t *)ptr;
  p[0] = v & 0xff;
  p[1] = v >> 8;
}

字节序转换,将16bit的变量v转换成网络字节序格式。

set_bits_in_byte

void CC_INLINE set_bits_in_byte(uint8_t *target, int bitpos, uint8_t val, int vallen)
{
  unsigned short shifted_val;
  shifted_val = val << (8 - bitpos + 8 - vallen);
  target[0] |= shifted_val >> 8;
  target[1] |= shifted_val & 0xff;
}

target:指向一个字节,且该字节里面已经填充了bitpos比特的数据。
bitpos:表示target所执行的字节已经被填充了多少比特的数据。取值范围是[0, 7]。
val:要填充的值。参考vallen,可知取值范围是[0, 0xFF]。
vallen:val转换为二进制后的二进制位数,取值范围是[1, 8]。
该函数的作用是紧跟着上一次填充的比特后面继续填充二进制比特位,比较难解释,来个示意图:

假定target地址处已经填充了4个比特`1011`,需要填充的val为`10101`,因此对应函数的各个参数分别是:
bitpos:已经被填充了4bit
val:二进制的`10101`
vallen:`10101`的位数,5
|++++++++|++++++++|++++++++|
|1011....|........|........| target
|++++++++|++++++++|++++++++|
|........|...10101|........| val
|++++++++|++++++++|++++++++|
|........|10101000|........| val << (8-vallen)
|++++++++|++++++++|++++++++|
|....1010|10000000|........| shifted_val = val << (8-vallen + 8-bitpos)
|++++++++|++++++++|++++++++|
|10111010|........|........| target[0] |= shifted_val >> 8;
|++++++++|++++++++|++++++++| 
|10111010|10000000|........| target[1] |= shifted_val & 0xff;
|++++++++|++++++++|++++++++|
|10111010|1.......|........|

set_bits

void
set_bits(uint8_t *ptr, int bitpos, uint8_t *val, int vallen)
{
  int i, bits;

  if(vallen < 8) {
    set_bits_in_byte(ptr, bitpos, *val /*>> (8 - vallen)*/, vallen);
  } else {
    if(bitpos == 0) {
      for(i = 0; i < vallen / 8; ++i) {
    ptr[i] = val[i];
      }
      bits = vallen & 7;
      if(bits) {
    set_bits_in_byte(&ptr[i], 0, val[i] >> (8 - bits), bits);
      }
    } else {
      for(i = 0; i < vallen / 8; ++i) {
    set_bits_in_byte(&ptr[i], bitpos, val[i], 8);
      }
      bits = vallen & 7;
      if(bits) {
    set_bits_in_byte(&ptr[i], 0, val[i] >> (8 - bits + bitpos), bits);
      }
    }
  }
}

ptr:指向某个字节。
bitopts:ptr地址处已被填充的位数
val:待填充的值。
vallen:val转换为二进制后的二进制位数。

get_bits_in_byte

static uint8_t CC_INLINE
get_bits_in_byte(uint8_t *from, int bitpos, int vallen)
{
  uint16_t shifted_val;

  shifted_val = (from[0] << 8) | from[1];
  return (((shifted_val << bitpos) >> 8) & bitmask[vallen]) >> (8 - vallen);
}

from:指向一个字节的地址
bitpos:表示from指向的地址的前bitpos比特已经被取出,本次从bitpos+1除开始取vallen比特
vallen:本次要取的位数。取值是[1, 8]
shifted_val由from地址开始出的两个字节组成,shifted_val << bitpos先砍掉左边已被上次取走的比特,(shifted_val << bitpos) >> 8再砍掉第二个字节里的内容。后面再与bitmask[vallen]相与,额,没看懂,先留在这儿,如果谁知道,请一定告诉我。
该函数一次最多能取8bit。

get_bits

void
get_bits(uint8_t *to, uint8_t *from, int bitpos, int vallen)
{
  int i, bits;


  if(vallen < 8) {
    //小于8bit,直接get_bits_in_byte取走
    *to = get_bits_in_byte(from, bitpos, vallen);
  } else {
    if(bitpos == 0) {
      for(i = 0; i < vallen / 8; ++i) {
        // 整字节,直接取走
    to[i] = from[i];
      }
      bits = vallen & 7;
      if(bits) {
        // 剩下的字节,调用get_bits_in_byte取走
    to[i] = get_bits_in_byte(&from[i], 0, bits);
      }
    } else {
      for(i = 0; i < vallen / 8; ++i) {
        // 先取整字节
    to[i] = get_bits_in_byte(&from[i], bitpos, 8);
      }
      bits = vallen & 7;
      if(bits) {
        // 再取剩下的字节
    to[i] = get_bits_in_byte(&from[i], bitpos, bits);
      }
    }
  }
}

to:取出来的数据放给to指针
from: 从from地址处取数据
bitpos: 零碎的比特数,范围是[0,7]
vallen:需要取的比特数

header_size

static int
header_size(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 link layer handle sender and receiver */
      continue;
    }
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
    len = a->len;
    size += len;
  }
  return size;
}

该函数很简单,直接将属性链表中的包属性的成员len进行累加。

pack_header

static int
pack_header(struct channel *c)
{
  const struct packetbuf_attrlist *a;
  int hdrbytesize;
  int byteptr, bitptr, len;
  uint8_t *hdrptr;
  struct bitopt_hdr *hdr;

  hdrbytesize = c->hdrsize / 8 + ((c->hdrsize & 7) == 0? 0: 1);
  if(packetbuf_hdralloc(hdrbytesize + BITOPT_HDR_SIZE) == 0) {
    PRINTF("chameleon-bitopt: insufficient space for headers\n");
    return 0;
  }
  hdr = (struct bitopt_hdr *)packetbuf_hdrptr();
  hdr->channel[0] = c->channelno & 0xff;
  hdr->channel[1] = (c->channelno >> 8) & 0xff;

  hdrptr = ((uint8_t *)packetbuf_hdrptr()) + BITOPT_HDR_SIZE;
  memset(hdrptr, 0, hdrbytesize);

  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;
    byteptr = bitptr / 8;
    if(PACKETBUF_IS_ADDR(a->type)) {
      set_bits(&hdrptr[byteptr], bitptr & 7, (uint8_t *)packetbuf_addr(a->type), len);
    } else {
      uint8_t buffer[2];
      packetbuf_attr_t val = packetbuf_attr(a->type);
      le16_write(buffer, val);
      set_bits(&hdrptr[byteptr], bitptr & 7, buffer, len);
    }
    bitptr += len;
  }

  return 1; /* Send out packet */
}

该函数功能与原始变色龙的output函数功能类似,都负责将从Rime传递下来的包属性组装成最终的头部,唯一的差别是对头部进行了位组合操作,减小了头部尺寸。

unpack_header

static struct channel *unpack_header(void)
{
  const struct packetbuf_attrlist *a;
  int byteptr, bitptr, len;
  int hdrbytesize;
  uint8_t *hdrptr;
  struct bitopt_hdr *hdr;
  struct channel *c;

  hdr = (struct bitopt_hdr *)packetbuf_dataptr();
  if(packetbuf_hdrreduce(BITOPT_HDR_SIZE) == 0) {
    PRINTF("chameleon-bitopt: too short packet\n");
    return NULL;
  }
  c = channel_lookup((hdr->channel[1] << 8) + hdr->channel[0]);
  if(c == NULL) {
    PRINTF("chameleon-bitopt: input: channel %u not found\n",
           (hdr->channel[1] << 8) + hdr->channel[0]);
    return NULL;
  }

  hdrptr = packetbuf_dataptr();
  hdrbytesize = c->hdrsize / 8 + ((c->hdrsize & 7) == 0? 0: 1);
  if(packetbuf_hdrreduce(hdrbytesize) == 0) {
    PRINTF("chameleon-bitopt: too short packet\n");
    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;
    byteptr = bitptr / 8;
    if(PACKETBUF_IS_ADDR(a->type)) {
      linkaddr_t addr;
      get_bits((uint8_t *)&addr, &hdrptr[byteptr], bitptr & 7, len);
      packetbuf_set_addr(a->type, &addr);
    } else {
      packetbuf_attr_t val;
      uint8_t buffer[2] = {0};
      get_bits(buffer, &hdrptr[byteptr], bitptr & 7, len);
      val = le16_read(buffer);
      packetbuf_set_attr(a->type, val);
    }
    bitptr += len;
  }
  return c;
}

同样地,该函数对应于原始变色龙中的input函数,负责将接收到的包的包头转换为包属性,以供Rime协议栈的子协议使用。

4 小结

有了上一篇博客的基础,该部分的功能很容易理解。但是涉及到具体的函数,很绕,需要大家多思考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值