1 概述
chameleon-bitopt的直译是变色龙位操作,但是我们在这里将它叫做头部转换模块。之所以把它叫做头部转换模块,是因为adam多次在论文中提到的头部转换模块这个概念。
与原始变色龙不同的是,头部转换模块不仅将Rime传递下来的包属性封装成头部,还对其进行了优化,有效减小了头部尺寸。
从Rime传递下来的很多包属性,其长度都不足8bit,却在头部中占了整整一个字节,这是一种非常浪费的行为。头部转换模块将不同的属性按位进行组合,极大地减小了头部尺寸。
目前Rime协议栈中各子协议包属性的bit长度如下:
Primitive | Attribute | Bits | Scope |
---|---|---|---|
ibc | Single-hop sender | Addr len | 1 |
uc | Single-hop receiver | Addr len | 1 |
stuc | Retransmissions | 4 | 1 |
ruc | Single-hop reliable | 1 | 0 |
ruc | Single-hop packet type | 1 | 1 |
ruc | Single-hop packet ID | 2 | 1 |
ruc | Max. rexmit | 4 | 0 |
rmh | End-to-end sender | Addr len | 2 |
rmh | End-to-end receiver | Addr len | 2 |
rmh | Time to live | 5 | 2 |
其中,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 小结
有了上一篇博客的基础,该部分的功能很容易理解。但是涉及到具体的函数,很绕,需要大家多思考。