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

来源:互联网 发布:java decode函数 编辑:程序博客网 时间:2024/06/10 07:03
---------------------------------------   数据包接收流程 ----------------------------------------------------------------------
ESB为例:CPUmsp430,射频芯片为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层头部是在哪里填充的?)
0 0
原创粉丝点击