您的位置:首页 > 其它

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

2015-09-09 21:10 267 查看
---------------------------------------   数据包接收流程 ----------------------------------------------------------------------
以ESB为例:CPU为msp430,
射频芯片为Tr1001。相关代码在/platform/esb
以及 /cpu/msp430 ,/core/net中
[align=left]在contiki-conf.h中首先对各层的协议栈进行了定义。[/align]
[align=left]如下:[/align]
[align=left]#define NETSTACK_CONF_RADIO     tr1001_driver[/align]
[align=left]#define NETSTACK_CONF_NETWORK  uip_driver[/align]
[align=left]#define NETSTACK_CONF_MAC      nullmac_driver[/align]
[align=left]#define NETSTACK_CONF_RDC       nullrdc_driver[/align]
[align=left][/align]
[align=left]一、           收包流程[/align]
[align=left]下面我们从主函数Contiki-esb-main.c开始,先说数据的接收流程。[/align]
[align=left]首先,定义一块网卡:[/align]
[align=left]static struct uip_fw_netif tr1001if =[/align]
[align=left]  {UIP_FW_NETIF(0,0,0,0, 0,0,0,0, uip_driver_send)};[/align]
[align=left]其中uip_driver_send就是这块网卡的发包函数。[/align]
[align=left]在主函数中会进行协议栈的初始化,另外开启几个关于收发数据的进程。[/align]
[align=left]int main(void)[/align]
[align=left]{[/align]
[align=left]  msp430_cpu_init();[/align]
[align=left]  process_start(&etimer_process, NULL);[/align]
[align=left]  netstack_init();[/align]
[align=left]  init_uip_net();[/align]
[align=left]  autostart_start(autostart_processes);[/align]
[align=left]  watchdog_start();[/align]
[align=left]  while(1) {[/align]
[align=left]    int r;[/align]
[align=left]    do {[/align]
[align=left]      /* Reset watchdog. */[/align]
[align=left]      watchdog_periodic();[/align]
[align=left]      r = process_run();[/align]
[align=left]    } while(r > 0);[/align]
[align=left]  return 0;[/align]
[align=left]}[/align]
其中,init_uip_net()
中启动了两个进程如下:
[align=left]static void init_uip_net(void)[/align]
[align=left]{[/align]
[align=left]  process_start(&tcpip_process, NULL);[/align]
[align=left]  process_start(&uip_fw_process, NULL);[/align]
[align=left]}[/align]
[align=left]这两个进程我们在以后会用到。[/align]

[align=left][/align]
[align=left]netstack_init()如下:[/align]
[align=left]void netstack_init(void)[/align]
[align=left]{[/align]
[align=left]  NETSTACK_RADIO.init();[/align]
[align=left]  NETSTACK_RDC.init();[/align]
[align=left]  NETSTACK_MAC.init();[/align]
[align=left]  NETSTACK_NETWORK.init();[/align]
[align=left]}[/align]
[align=left]即,netstack_init会对各层驱动均进行初始化。[/align]
[align=left]下面,主要说Radio层的驱动初始化:[/align]
[align=left]Int tr1001_init(void)[/align]
[align=left]{[/align]
[align=left]  PT_INIT(&rxhandler_pt);[/align]
[align=left]  process_start(&tr1001_process, NULL);[/align]
[align=left]  return 1;[/align]
[align=left]}[/align]
该初始化过程中启动了 tr1001_process,
如下:
[align=left]PROCESS_THREAD(tr1001_process, ev, data)[/align]
[align=left]{[/align]
[align=left]  PROCESS_BEGIN();[/align]
[align=left]  while(1) {[/align]
[align=left]    PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);[/align]
[align=left]    packetbuf_clear();[/align]
    len = tr1001_read(packetbuf_dataptr(), PACKETBUF_SIZE);  //
读取数据
[align=left]    if(len > 0) {[/align]
[align=left]      packetbuf_set_datalen(len);[/align]
      NETSTACK_RDC.input();                         //
向上提交
[align=left]    }[/align]
[align=left]  }[/align]
[align=left]  PROCESS_END();[/align]
[align=left]}[/align]
[align=left]当该进程收到事件ev == PROCESS_EVENT_POLL时,就会从射频芯片读数据。[/align]
我们知道,射频芯片每收到一个frame,都会向cpu
发送一个中断,进而CPU将收到的数据读走。下面,看这个PROCESS_EVENT_POLL事件的产生过程。
[align=left]中断注册函数如下:[/align]
[align=left]interrupt (UART0RX_VECTOR)[/align]
[align=left]     tr1001_rxhandler(void)[/align]
[align=left]{[/align]
[align=left]  ENERGEST_ON(ENERGEST_TYPE_IRQ);[/align]
[align=left]  tr1001_default_rxhandler_pt(RXBUF0);[/align]
[align=left]  if(tr1001_rxstate == RXSTATE_FULL) {[/align]
[align=left]    LPM4_EXIT;[/align]
[align=left]  }[/align]
[align=left]  ENERGEST_OFF(ENERGEST_TYPE_IRQ);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]Char tr1001_default_rxhandler_pt(unsigned char incoming_byte))[/align]
[align=left]{[/align]
[align=left]    static unsigned char rxtmp, tmppos;[/align]
[align=left]    if(rxcrctmp == rxcrc) {[/align]
[align=left]      /* A full packet has been received and the CRC checks out. We'll[/align]
[align=left]           request the driver to take care of the incoming data. */[/align]
[align=left]      RIMESTATS_ADD(llrx);[/align]
[align=left]      process_poll(&tr1001_process);[/align]
[align=left]      PT_END(&rxhandler_pt);[/align]
[align=left]}[/align]

到这里,radio层的收包过程就结束了。通过 NETSTACK_RDC.input()
将数据提交到rdc层。
进入函数:nullrdc_driver.input(),
即core\net\mac 下的 nullrdc.c
中的packet_input。
[align=left]static void  packet_input(void)[/align]
[align=left]{[/align]
[align=left]    NETSTACK_MAC.input();[/align]
[align=left]}[/align]
提交给mac层继续处理。进入函数nullmac_driver.input(),即core\net\mac
下的 nullmac.c 中的packet_input。
[align=left]static void packet_input(void)[/align]
[align=left]{[/align]
[align=left]  NETSTACK_NETWORK.input();[/align]
[align=left]}[/align]
提交给网络层进行处理。进入函数uip_driver.input(),
即platform/esb/net/uip_dirver.c
中的 input函数。

[align=left][/align]
[align=left]static void input(void)[/align]
[align=left]{[/align]
[align=left]  if(packetbuf_datalen() > 0 && packetbuf_datalen() <= UIP_BUFSIZE - UIP_LLH_LEN) {[/align]
[align=left]    memcpy(&uip_buf[UIP_LLH_LEN], packetbuf_dataptr(), packetbuf_datalen());[/align]
[align=left]    uip_len = hc_inflate(&uip_buf[UIP_LLH_LEN], packetbuf_datalen());[/align]
[align=left]    tcpip_input();[/align]
[align=left]  }[/align]
[align=left]}[/align]
[align=left]Void tcpip_input(void)[/align]
[align=left]{[/align]
[align=left]  process_post_synch(&tcpip_process, PACKET_INPUT, NULL);[/align]
[align=left]}[/align]
Tcpip_input 向进程tcpip_process
传递消息PACKET_INPUT。(下面三个函数均位于/core/net/tcpip.c)
[align=left]PROCESS_THREAD(tcpip_process, ev, data)[/align]
[align=left]{[/align]
[align=left]  PROCESS_BEGIN();[/align]
[align=left]  tcpip_event = process_alloc_event();[/align]
[align=left]  while(1) {[/align]
[align=left]    PROCESS_YIELD();[/align]
[align=left]    eventhandler(ev, data);[/align]
[align=left]  }[/align]
[align=left]  PROCESS_END();[/align]
[align=left]}[/align]
[align=left]/*---------------------------------------------------------------------------*/[/align]
[align=left]static void[/align]
[align=left]eventhandler(process_event_t ev, process_data_t data)[/align]
[align=left]{[/align]
[align=left]  switch(ev) {[/align]
[align=left]    case PROCESS_EVENT_EXITED:  …[/align]
[align=left]    case PROCESS_EVENT_TIMER:  ….[/align]
[align=left]    case TCP_POLL:   …[/align]
[align=left]    case UDP_POLL:  ...[/align]
[align=left]    case PACKET_INPUT:[/align]
[align=left]      packet_input();[/align]
[align=left]      break;[/align]
[align=left]  };[/align]
[align=left]}[/align]
[align=left]static void[/align]
[align=left]packet_input(void)[/align]
[align=left]{[/align]
[align=left]  if(uip_len > 0) {[/align]
[align=left]    tcpip_is_forwarding = 1;[/align]
[align=left]    if(uip_fw_forward() == UIP_FW_LOCAL) {    //  这里执行路由和转发[/align]
[align=left]      tcpip_is_forwarding = 0;[/align]
[align=left]      check_for_tcp_syn();[/align]
[align=left]      uip_input();[/align]
[align=left]      if(uip_len > 0) {[/align]
[align=left]        tcpip_output();[/align]
[align=left]      }[/align]
[align=left]    }[/align]
[align=left]    tcpip_is_forwarding = 0;[/align]
[align=left]  }[/align]
[align=left]}[/align]
到uip_input,至此数据成功的转交到UIP协议栈进行处理。在uip_porcess
中会把数据包分发到不同的应用程序中。
[align=left]Uip_process中会对网络层以及传输层的包头进行分析,并提交给应用程序。[/align]
[align=left]至此,数据的接收流程结束。[/align]

-------------------------------   数据包发送过程    ----------------------------------------------------------
二、
发包流程

下面讨论发送数据包的流程。发数据包的过程相对复杂。用到了回调机制。其过程可以分为:产生完整的数据包
和 发送数据包
两部分。下面分别说明。
以doc/example-program.c
为例
[align=left]PROCESS_THREAD(example_program_process, ev, data)[/align]
[align=left]{[/align]
[align=left]  static struct uip_udp_conn *c; [/align]
[align=left]  PROCESS_BEGIN();[/align]
  c = udp_broadcast_new(UIP_HTONS(4321), NULL);  ------建立一个udp连接

[align=left]  while(1) {[/align]
    etimer_set(&timer, CLOCK_SECOND);         ---
设置定时器
[align=left]    PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));  -----等待定时器到时[/align]
    tcpip_poll_udp(c);                        

[align=left]    PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);[/align]
[align=left]    uip_send("Hello", 5);                 -     [/align]
[align=left]  }[/align]
[align=left]  PROCESS_END();[/align]
[align=left]}[/align]
[align=left]该函数执行的事情是:周期性的广播发送hello。[/align]
下面从tcpip_poll_udp(c)
开始
[align=left]Void tcpip_poll_udp(struct uip_udp_conn *conn)[/align]
[align=left]{[/align]
  process_post(&tcpip_process, UDP_POLL, conn);  //
给tcpip_process 传递消息 UDP_POLL
[align=left]}[/align]
前面在收包时,我们已经看到过tcpip_process。当tcpip_process
收到消息后,进行的处理如下:
[align=left]static void eventhandler(process_event_t ev, process_data_t data)   // core/net/tcpip.c[/align]
[align=left]{[/align]
[align=left]  switch(ev) {[/align]
[align=left]    case PROCESS_EVENT_EXITED:[/align]
[align=left]    case PROCESS_EVENT_TIMER:[/align]
[align=left]    case TCP_POLL:[/align]
[align=left]    case UDP_POLL:[/align]
[align=left]      if(data != NULL) {[/align]
        uip_udp_periodic_conn(data);   //
产生数据
[align=left]        if(uip_len > 0) {[/align]
          tcpip_output();            //
发送
[align=left]        }[/align]
[align=left]      }[/align]
[align=left]      break;[/align]
[align=left]  };[/align]
[align=left]}[/align]
其中uip_udp_periodic_conn
用于产生数据包,tcpip_output 用于发送。
[align=left]先看uip_udp_periodic_conn:[/align]
[align=left]#define uip_udp_periodic_conn(conn) do { uip_udp_conn = conn;   \[/align]
[align=left]uip_process(UIP_UDP_TIMER); } while(0)[/align]
[align=left][/align]
转到uip_process()。
这是uip的核心处理函数。收数据包时已经看到过此函数。
[align=left][/align]
[align=left]void uip_process(u8_t flag)[/align]
[align=left]  if(flag == UIP_UDP_TIMER) {[/align]
[align=left]    if(uip_udp_conn->lport != 0) {[/align]
[align=left]      uip_conn = NULL;[/align]
[align=left]      uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN];[/align]
[align=left]      uip_len = uip_slen = 0;[/align]
[align=left]      uip_flags = UIP_POLL;[/align]
      UIP_UDP_APPCALL();   /* 产生应用层数据 */

[align=left]      goto udp_send;[/align]
[align=left]    } else {[/align]
[align=left]      goto drop;[/align]
[align=left]}[/align]
[align=left]udp_send:[/align]
[align=left]/* 填充udp的包头*/[/align]
[align=left]goto ip_send_nolen;[/align]
[align=left]ip_send_nolen:[/align]
[align=left]/* 填充ip层包头 , 校验和等*/[/align]
[align=left]Return;[/align]
[align=left]  }[/align]
下面看 UIP_UDP_APPCALL
是如何产生数据的:
[align=left]#define UIP_UDP_APPCALL   tcpip_uipcall[/align]
[align=left][/align]
[align=left]Void tcpip_uipcall(void)[/align]
[align=left]{[/align]
[align=left]  register uip_udp_appstate_t *ts;[/align]
[align=left]  ts = &uip_udp_conn->appstate;[/align]
[align=left]  if(ts->p != NULL) {[/align]
[align=left]    process_post_synch(ts->p, tcpip_event, ts->state);[/align]
[align=left]  }[/align]
[align=left]}[/align]
通过process_post_synch(ts->p, tcpip_event, ts->state)
调回到udp连接相关联的进程,即我们最初的进程:example_program_process,向这个进程发送消息: tcpip_event
[align=left]这是我们最初的进程,[/align]
[align=left]PROCESS_THREAD(example_program_process, ev, data)[/align]
[align=left]{[/align]
[align=left]  static struct uip_udp_conn *c; [/align]
[align=left]  PROCESS_BEGIN();[/align]
  c = udp_broadcast_new(UIP_HTONS(4321), NULL);  ------建立一个udp连接

[align=left]  while(1) {[/align]
    etimer_set(&timer, CLOCK_SECOND);         ---
设置定时器
[align=left]    PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));  -----等待定时器到时[/align]
    tcpip_poll_udp(c);                        

    PROCESS_WAIT_EVENT_UNTIL(ev ==
tcpip_event);
[align=left]    uip_send("Hello", 5);                 -     [/align]
[align=left]  }[/align]
[align=left]  PROCESS_END();[/align]
[align=left]}[/align]
这时,example_program_process
将继续执行,调用 uip_send()。
[align=left]Void uip_send(const void *data, int len)[/align]
[align=left]{[/align]
[align=left]    memcpy(uip_sappdata, (data), uip_slen);[/align]
[align=left]}[/align]
Uip_send 执行的功能就是把应用层的数据拷贝到
数据包的对应位置,然后返回。
[align=left]至此,网络层的数据包就已经完全生成了。[/align]

下面看发包函数tcpip_output
(/core/net/tcpip.c)
[align=left]u8_t tcpip_output(void)[/align]
[align=left]{[/align]
[align=left]  if(outputfunc != NULL) {[/align]
[align=left]    return outputfunc();[/align]
[align=left]  }[/align]
[align=left]  UIP_LOG("tcpip_output: Use tcpip_set_outputfunc() to set an output function");[/align]
[align=left]  return 0;[/align]
[align=left]}[/align]
[align=left]下面转到outputfunc()。在main函数中启动了进程  uip_fw_procss。[/align]
[align=left]PROCESS_THREAD(uip_fw_process, ev, data)   //  /core/net/uip_fw_drv.c[/align]
[align=left]{[/align]
[align=left]  PROCESS_BEGIN();[/align]
[align=left]  tcpip_set_outputfunc(uip_fw_output);[/align]
[align=left]  PROCESS_WAIT_UNTIL(ev == PROCESS_EVENT_EXIT);[/align]
[align=left]  PROCESS_END();[/align]
[align=left]}[/align]
[align=left]即outputfunc = uip_fw_output。[/align]
[align=left]下面进入 uip_fw_output。[/align]
[align=left]u8_t uip_fw_output(void)[/align]
[align=left]{[/align]
[align=left]  struct uip_fw_netif *netif;[/align]
[align=left]  if(uip_len == 0) {[/align]
[align=left]    return UIP_FW_ZEROLEN;[/align]
[align=left]  }  [/align]
[align=left]  netif = find_netif();[/align]
[align=left]  if(netif == NULL) {[/align]
[align=left]    return UIP_FW_NOROUTE;[/align]
[align=left]  }[/align]
[align=left]  return netif->output();[/align]
[align=left]}[/align]
该函数中先调用find_netif()
通过查路由表找到合适的网卡,然后调用该网卡的output函数。
[align=left]前面提到的,我们的网卡在最开始就定义过:[/align]
[align=left]static struct uip_fw_netif  tr1001if =[/align]
[align=left]  {UIP_FW_NETIF(0,0,0,0, 0,0,0,0, uip_driver_send)};[/align]
所以,假设找到的网卡就是tr1001if,那么接下来就要调用uip_driver_send
函数。

[align=left][/align]
[align=left]uint8_t uip_driver_send(void)[/align]
[align=left]{[/align]
[align=left]  uip_len = hc_compress(&uip_buf[UIP_LLH_LEN], uip_len);[/align]
[align=left]  packetbuf_copyfrom(&uip_buf[UIP_LLH_LEN], uip_len);[/align]
[align=left]  NETSTACK_MAC.send(NULL, NULL);[/align]
[align=left]}[/align]
进入nullmac
的发送函数
[align=left]static void send_packet(mac_callback_t sent, void *ptr)[/align]
[align=left]{[/align]
[align=left]  NETSTACK_RDC.send(sent, ptr);[/align]
[align=left]}[/align]
[align=left]进入nullrdc的发送函数[/align]
[align=left]static void send_packet(mac_callback_t sent, void *ptr)[/align]
[align=left]{[/align]
[align=left]  int ret;[/align]
[align=left]  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);[/align]
[align=left]   switch(NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen())) {[/align]
[align=left]    case RADIO_TX_OK:[/align]
[align=left]      ret = MAC_TX_OK;[/align]
[align=left]      break;[/align]
[align=left]    case RADIO_TX_COLLISION:[/align]
[align=left]      ret = MAC_TX_COLLISION;[/align]
[align=left]      break;[/align]
[align=left]    default:[/align]
[align=left]      ret = MAC_TX_ERR;[/align]
[align=left]      break;[/align]
[align=left]    }[/align]
[align=left]  }[/align]
[align=left]  mac_call_sent_callback(sent, ptr, ret, 1);[/align]
[align=left]}[/align]
[align=left]进入tr1001_driver的发送函数[/align]
[align=left]Int tr1001_send(const void *packet, unsigned short len)[/align]
[align=left]{[/align]
[align=left]….(复杂,需要认真研究)[/align]
[align=left]}[/align]
[align=left]至此,数据的发送过程结束。 [/align]
[align=left](问题:MAC层头部是在哪里填充的?)[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: