Contiki的NETWORK层之间的数据流路径:
Send:
Network->Mac->Rdc(->Frame802154)->Radio
Recv:
Radio->Rdc(->Frame802154)->Mac->Network
注:在此结构中,802154应该是属于RDC层的,
如果不将RDC算作一层,应该是介于Mac和Radio之间。是Mac层的出口。这也是Contiki的一项重要模块之一。在Contiki中ContikiMAC.c模块实现了RDC功能。论文《The ContikiMAC Radio Duty Cycling Protocol》详细描述了RDC的实现与理论基础。
在Contiki中接收采用事件驱动方式。接收通过一个PThread任务,等待数据到来事件,如果数据到来事件触发,则将数据则取出来。这个事件触发是在Radio的中断中设置的。
即:NETSTACK_RDC.input()的调用一般都是在Radio的一个PThread中,由于在Network.open的时候已经将每一层的回调函数设置完备,所以当接收到数据的时候,通过每一层的回调,将数据传递到Network_Network处,即协议栈的最顶部应用层处理。
以contiki\cpu\avr\radio\rf230bb\rf230bb.c文件为例
rf230_interrupt()函数中判断接收到数据, 调用process_poll(&rf230_process),发送PROCESS_EVENT_POLL事件:
[cpp] view plaincopy
int
rf230_interrupt(void)
{
/* Poll the receive process, unless the stack thinks the radio is off */
#if RADIOALWAYSON
if (RF230_receive_on) {
DEBUGFLOW('+');
#endif
#if RF230_CONF_TIMESTAMPS
interrupt_time = timesynch_time();
interrupt_time_set = 1;
#endif /* RF230_CONF_TIMESTAMPS */
process_poll(&rf230_process);
rf230_pending = 1;
#if RADIOSTATS //TODO:This will double count buffered packets
RF230_receivepackets++;
#endif
RIMESTATS_ADD(llrx);
#if RADIOALWAYSON
} else {
DEBUGFLOW('-');
rxframe[rxframe_head].length=0;
}
#endif
return 1;
}
在rf230_process PThread中收到PROCESS_EVENT_POLL,调用NETSTACK_RDC.input()进入数据接收处理流程。最后在Radio文件中,找到了调用process_poll()函数来,poll一个PROCESS_EVENT_POLL。
[cpp] view plaincopy
PROCESS_THREAD(rf230_process, ev, data)
{
int len;
PROCESS_BEGIN();
RF230PROCESSFLAG(99);
while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
RF230PROCESSFLAG(42);
packetbuf_clear();
/* Turn off interrupts to avoid ISR writing to the same buffers we are reading. */
HAL_ENTER_CRITICAL_REGION();
len = rf230_read(packetbuf_dataptr(), PACKETBUF_SIZE);
/* Restore interrupts. */
HAL_LEAVE_CRITICAL_REGION();
PRINTF("rf230_read: %u bytes lqi %u\n",len,rf230_last_correlation);
#if DEBUG>1
{
uint8_t i;
unsigned const char * rxdata = packetbuf_dataptr();
PRINTF("0000");
for (i=0;i<len+AUX_LEN;i++) PRINTF(" %02x",rxdata[i]);
PRINTF("\n");
}
#endif
RF230PROCESSFLAG(1);
if(len > 0) {
packetbuf_set_datalen(len);
RF230PROCESSFLAG(2);
NETSTACK_RDC.input();
} else {
#if RADIOSTATS
RF230_receivefail++;
#endif
}
}
PROCESS_END();
}
附1:
以Contiki中example-mesh.c历程为例:
examples/example-mesh.c/example_mesh_process/mesh_open
send:
(在rime中:
mesh.c/mesh_send()
multihop_send()
unicast_send()
broadcast_send()
abc_send()(abc_send中调用rime_output)
)
rime.c/rime_output()/NETSTACK_MAC.send
nullmac.c/NETSTACK_RDC.send
(NETSTACK_FRAMER.create 生成framer)
nullrdc.c/NET_RADIO.send
nullrdc.c/mac_call_sent_callback 处理发送结束后的状态。
recv:
tf230bb/fr230bb.c/rf230_interrupt 调用process_poll产生PROCESS_EVENT_POLL。
pthread
rf230bb.c/rf230_process任务/NETSTACK_RDC.input
(NETSTACK_FRAMER.parse解析收到的数据帧,是否符合MAC层要求)
nullrdc.c/NETSTACK_MAC.input
nullmac.c/NETSTACK_NETWORK.input 调用rime.c/input()/abc_input()
通过rime协议栈的回调传递给
mesh.c/data_packet_received()此函数回调应用层的接收处理函数
example-mesh.c/example_recv()处理接收数据。
附2:
stunicast.c中
对于重发的处理,由在发送过程中设置timer如果在规定的时间内,如果没有数据返回则通过timer调用回调处理函数,如果有数据返回则在在recv_from_stunicast()调用stunicast_cancel()取消timer,避免调用回调。
在Contiki中,数据的发送与接收分别使用一个任务,这样可以避免之间的冲突问题。
NETWORK协议栈层之间的数据交互,通过buf来实现。这一点与Linux的sk_buff很像,所以导致了很多不必要的内存拷贝,这也是为什么Contiki内存占用很小的原因之一。