您的位置:首页 > 其它

contiki 中数据包的接收与发送流程

2016-01-12 16:05 519 查看
http://www.iotdev.net/forum.php?mod=viewthread&tid=144

--------------------------------------- 数据包接收流程 ----------------------------------------------------------------------

以ESB为例:CPU为msp430, 射频芯片为Tr1001。相关代码在/platform/esb 以及 /cpu/msp430 ,/core/net中

在contiki-conf.h中首先对各层的协议栈进行了定义。

如下:

#define NETSTACK_CONF_RADIO tr1001_driver

#define NETSTACK_CONF_NETWORK uip_driver

#define NETSTACK_CONF_MAC nullmac_driver

#define NETSTACK_CONF_RDC nullrdc_driver

一、 收包流程

下面我们从主函数Contiki-esb-main.c开始,先说数据的接收流程。

首先,定义一块网卡:

static struct uip_fw_netif tr1001if =

{UIP_FW_NETIF(0,0,0,0, 0,0,0,0, uip_driver_send)};

其中uip_driver_send就是这块网卡的发包函数。

在主函数中会进行协议栈的初始化,另外开启几个关于收发数据的进程。

int main(void)

{

msp430_cpu_init();

process_start(&etimer_process, NULL);

netstack_init();

init_uip_net();

autostart_start(autostart_processes);

watchdog_start();

while(1) {

int r;

do {

/* Reset watchdog. */

watchdog_periodic();

r = process_run();

} while(r > 0);

return 0;

}

其中,init_uip_net() 中启动了两个进程如下:

static void init_uip_net(void)

{

process_start(&tcpip_process, NULL);

process_start(&uip_fw_process, NULL);

}

这两个进程我们在以后会用到。

netstack_init()如下:

void netstack_init(void)

{

NETSTACK_RADIO.init();

NETSTACK_RDC.init();

NETSTACK_MAC.init();

NETSTACK_NETWORK.init();

}

即,netstack_init会对各层驱动均进行初始化。

下面,主要说Radio层的驱动初始化:

Int tr1001_init(void)

{

PT_INIT(&rxhandler_pt);

process_start(&tr1001_process, NULL);

return 1;

}

该初始化过程中启动了 tr1001_process, 如下:

PROCESS_THREAD(tr1001_process, ev, data)

{

PROCESS_BEGIN();

while(1) {

PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);

packetbuf_clear();

len = tr1001_read(packetbuf_dataptr(), PACKETBUF_SIZE); // 读取数据

if(len > 0) {

packetbuf_set_datalen(len);

NETSTACK_RDC.input(); // 向上提交

}

}

PROCESS_END();

}

当该进程收到事件ev == PROCESS_EVENT_POLL时,就会从射频芯片读数据。

我们知道,射频芯片每收到一个frame,都会向cpu 发送一个中断,进而CPU将收到的数据读走。下面,看这个PROCESS_EVENT_POLL事件的产生过程。

中断注册函数如下:

interrupt (UART0RX_VECTOR)

tr1001_rxhandler(void)

{

ENERGEST_ON(ENERGEST_TYPE_IRQ);

tr1001_default_rxhandler_pt(RXBUF0);

if(tr1001_rxstate == RXSTATE_FULL) {

LPM4_EXIT;

}

ENERGEST_OFF(ENERGEST_TYPE_IRQ);

}

Char tr1001_default_rxhandler_pt(unsigned char incoming_byte))

{

static unsigned char rxtmp, tmppos;

if(rxcrctmp == rxcrc) {

/* A full packet has been received and the CRC checks out. We'll

request the driver to take care of the incoming data. */

RIMESTATS_ADD(llrx);

process_poll(&tr1001_process);

PT_END(&rxhandler_pt);

}

到这里,radio层的收包过程就结束了。通过 NETSTACK_RDC.input() 将数据提交到rdc层。

进入函数:nullrdc_driver.input(), 即core\net\mac 下的 nullrdc.c 中的packet_input。

static void packet_input(void)

{

NETSTACK_MAC.input();

}

提交给mac层继续处理。进入函数nullmac_driver.input(),即core\net\mac 下的 nullmac.c 中的packet_input。

static void packet_input(void)

{

NETSTACK_NETWORK.input();

}

提交给网络层进行处理。进入函数uip_driver.input(), 即platform/esb/net/uip_dirver.c 中的 input函数。

static void input(void)

{

if(packetbuf_datalen() > 0 && packetbuf_datalen() <= UIP_BUFSIZE - UIP_LLH_LEN) {

memcpy(&uip_buf[UIP_LLH_LEN], packetbuf_dataptr(), packetbuf_datalen());

uip_len = hc_inflate(&uip_buf[UIP_LLH_LEN], packetbuf_datalen());

tcpip_input();

}

}

Void tcpip_input(void)

{

process_post_synch(&tcpip_process, PACKET_INPUT, NULL);

}

Tcpip_input 向进程tcpip_process 传递消息PACKET_INPUT。(下面三个函数均位于/core/net/tcpip.c)

PROCESS_THREAD(tcpip_process, ev, data)

{

PROCESS_BEGIN();

tcpip_event = process_alloc_event();

while(1) {

PROCESS_YIELD();

eventhandler(ev, data);

}

PROCESS_END();

}

/*---------------------------------------------------------------------------*/

static void

eventhandler(process_event_t ev, process_data_t data)

{

switch(ev) {

case PROCESS_EVENT_EXITED: …

case PROCESS_EVENT_TIMER: ….

case TCP_POLL: …

case UDP_POLL: ...

case PACKET_INPUT:

packet_input();

break;

};

}

static void

packet_input(void)

{

if(uip_len > 0) {

tcpip_is_forwarding = 1;

if(uip_fw_forward() == UIP_FW_LOCAL) { // 这里执行路由和转发

tcpip_is_forwarding = 0;

check_for_tcp_syn();

uip_input();

if(uip_len > 0) {

tcpip_output();

}

}

tcpip_is_forwarding = 0;

}

}

到uip_input,至此数据成功的转交到UIP协议栈进行处理。在uip_porcess 中会把数据包分发到不同的应用程序中。

Uip_process中会对网络层以及传输层的包头进行分析,并提交给应用程序。

至此,数据的接收流程结束。

------------------------------- 数据包发送过程 ----------------------------------------------------------

二、 发包流程

下面讨论发送数据包的流程。发数据包的过程相对复杂。用到了回调机制。其过程可以分为:产生完整的数据包 和 发送数据包 两部分。下面分别说明。

以doc/example-program.c 为例

PROCESS_THREAD(example_program_process, ev, data)

{

static struct uip_udp_conn *c;

PROCESS_BEGIN();

c = udp_broadcast_new(UIP_HTONS(4321), NULL); ------建立一个udp连接

while(1) {

etimer_set(&timer, CLOCK_SECOND); --- 设置定时器

PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); -----等待定时器到时

tcpip_poll_udp(c);

PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

uip_send("Hello", 5); -

}

PROCESS_END();

}

该函数执行的事情是:周期性的广播发送hello。

下面从tcpip_poll_udp(c) 开始

Void tcpip_poll_udp(struct uip_udp_conn *conn)

{

process_post(&tcpip_process, UDP_POLL, conn); // 给tcpip_process 传递消息 UDP_POLL

}

前面在收包时,我们已经看到过tcpip_process。当tcpip_process 收到消息后,进行的处理如下:

static void eventhandler(process_event_t ev, process_data_t data) // core/net/tcpip.c

{

switch(ev) {

case PROCESS_EVENT_EXITED:

case PROCESS_EVENT_TIMER:

case TCP_POLL:

case UDP_POLL:

if(data != NULL) {

uip_udp_periodic_conn(data); // 产生数据

if(uip_len > 0) {

tcpip_output(); // 发送

}

}

break;

};

}

其中uip_udp_periodic_conn 用于产生数据包,tcpip_output 用于发送。

先看uip_udp_periodic_conn:

#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn; \

uip_process(UIP_UDP_TIMER); } while(0)

转到uip_process()。 这是uip的核心处理函数。收数据包时已经看到过此函数。

void uip_process(u8_t flag)

if(flag == UIP_UDP_TIMER) {

if(uip_udp_conn->lport != 0) {

uip_conn = NULL;

uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN];

uip_len = uip_slen = 0;

uip_flags = UIP_POLL;

UIP_UDP_APPCALL(); /* 产生应用层数据 */

goto udp_send;

} else {

goto drop;

}

udp_send:

/* 填充udp的包头*/

goto ip_send_nolen;

ip_send_nolen:

/* 填充ip层包头 , 校验和等*/

Return;

}

下面看 UIP_UDP_APPCALL 是如何产生数据的:

#define UIP_UDP_APPCALL tcpip_uipcall

Void tcpip_uipcall(void)

{

register uip_udp_appstate_t *ts;

ts = &uip_udp_conn->appstate;

if(ts->p != NULL) {

process_post_synch(ts->p, tcpip_event, ts->state);

}

}

通过process_post_synch(ts->p, tcpip_event, ts->state) 调回到udp连接相关联的进程,即我们最初的进程:example_program_process,向这个进程发送消息: tcpip_event

这是我们最初的进程,

PROCESS_THREAD(example_program_process, ev, data)

{

static struct uip_udp_conn *c;

PROCESS_BEGIN();

c = udp_broadcast_new(UIP_HTONS(4321), NULL); ------建立一个udp连接

while(1) {

etimer_set(&timer, CLOCK_SECOND); --- 设置定时器

PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); -----等待定时器到时

tcpip_poll_udp(c);

PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

uip_send("Hello", 5); -

}

PROCESS_END();

}

这时,example_program_process 将继续执行,调用 uip_send()。

Void uip_send(const void *data, int len)

{

memcpy(uip_sappdata, (data), uip_slen);

}

Uip_send 执行的功能就是把应用层的数据拷贝到 数据包的对应位置,然后返回。

至此,网络层的数据包就已经完全生成了。

下面看发包函数tcpip_output (/core/net/tcpip.c)

u8_t tcpip_output(void)

{

if(outputfunc != NULL) {

return outputfunc();

}

UIP_LOG("tcpip_output: Use tcpip_set_outputfunc() to set an output function");

return 0;

}

下面转到outputfunc()。在main函数中启动了进程 uip_fw_procss。

PROCESS_THREAD(uip_fw_process, ev, data) // /core/net/uip_fw_drv.c

{

PROCESS_BEGIN();

tcpip_set_outputfunc(uip_fw_output);

PROCESS_WAIT_UNTIL(ev == PROCESS_EVENT_EXIT);

PROCESS_END();

}

即outputfunc = uip_fw_output。

下面进入 uip_fw_output。

u8_t uip_fw_output(void)

{

struct uip_fw_netif *netif;

if(uip_len == 0) {

return UIP_FW_ZEROLEN;

}

netif = find_netif();

if(netif == NULL) {

return UIP_FW_NOROUTE;

}

return netif->output();

}

该函数中先调用find_netif() 通过查路由表找到合适的网卡,然后调用该网卡的output函数。

前面提到的,我们的网卡在最开始就定义过:

static struct uip_fw_netif tr1001if =

{UIP_FW_NETIF(0,0,0,0, 0,0,0,0, uip_driver_send)};

所以,假设找到的网卡就是tr1001if,那么接下来就要调用uip_driver_send 函数。

uint8_t uip_driver_send(void)

{

uip_len = hc_compress(&uip_buf[UIP_LLH_LEN], uip_len);

packetbuf_copyfrom(&uip_buf[UIP_LLH_LEN], uip_len);

NETSTACK_MAC.send(NULL, NULL);

}

进入nullmac 的发送函数

static void send_packet(mac_callback_t sent, void *ptr)

{

NETSTACK_RDC.send(sent, ptr);

}

进入nullrdc的发送函数

static void send_packet(mac_callback_t sent, void *ptr)

{

int ret;

packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);

switch(NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen())) {

case RADIO_TX_OK:

ret = MAC_TX_OK;

break;

case RADIO_TX_COLLISION:

ret = MAC_TX_COLLISION;

break;

default:

ret = MAC_TX_ERR;

break;

}

}

mac_call_sent_callback(sent, ptr, ret, 1);

}

进入tr1001_driver的发送函数

Int tr1001_send(const void *packet, unsigned short len)

{

….(复杂,需要认真研究)

}

至此,数据的发送过程结束。

(问题:MAC层头部是在哪里填充的?)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: