Flowers_World
分类: 嵌入式
2016-08-25 15:06:56
原文地址:contiki 中数据包的发送流程 作者:a_jige
二、 发包流程
下面讨论发送数据包的流程。发数据包的过程相对复杂。用到了回调机制。其过程可以分为:产生完整的数据包 和 发送数据包 两部分。下面分别说明。
以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层头部是在哪里填充的?)
总结以上tcpip_output的流程如下: