0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

LWIP協(xié)議棧內(nèi)存管理方案 LWIP網(wǎng)卡設(shè)計(jì)與實(shí)現(xiàn)

CHANBAEK ? 來(lái)源:嵌入式攻城獅 ? 作者: 安迪西 ? 2023-04-19 11:20 ? 次閱讀

1.LWIP協(xié)議棧內(nèi)存管理

1.1 內(nèi)存管理需求

內(nèi)存管理需求分為兩類(lèi):

  • 常用內(nèi)存管理需求:靜態(tài)分配的變量(RAM),任務(wù)堆棧,動(dòng)態(tài)存儲(chǔ)器管理malloc/free
  • LWIP內(nèi)存管理需求:協(xié)議棧各層封裝的數(shù)據(jù)

1.2 內(nèi)存管理方案

LWIP內(nèi)存管理有兩種方案:堆(heap)和池(pool)

堆:堆內(nèi)存管理機(jī)制會(huì)根據(jù)需要分配的內(nèi)存大小在空閑的內(nèi)存塊中找到最佳擬合(best fit)的內(nèi)存區(qū)域

圖片

LWIP內(nèi)存堆管理API函數(shù):

//內(nèi)存堆初始化
void  mem_init(void);
//內(nèi)存堆分配內(nèi)存
void *mem_malloc(mem_size_t size);
//內(nèi)存堆釋放內(nèi)存
void  mem_free(void *mem);

池:池內(nèi)存管理機(jī)制將內(nèi)存分配成多個(gè)大小不一的內(nèi)存池,每個(gè)內(nèi)存池中又被分為N個(gè)相同大小的內(nèi)存塊。 程序可根據(jù)需要使用的內(nèi)存大小直接到不同的內(nèi)存池中取用即可。 池內(nèi)存管理機(jī)制分配內(nèi)存更快,效率更高

圖片

LWIP內(nèi)存池管理API函數(shù):

//內(nèi)存池初始化
void  memp_init(void);
//內(nèi)存池分配
void *memp_malloc(memp_t type);
//內(nèi)存池釋放
void  memp_free(memp_t type, void *mem);

1.3 網(wǎng)絡(luò)數(shù)據(jù)包管理

pbuf就是一個(gè)描述協(xié)議棧中數(shù)據(jù)包的數(shù)據(jù)結(jié)構(gòu),LWIP 中在 pbuf.c和 pubf.h實(shí)現(xiàn)了協(xié)議棧數(shù)據(jù)包管理的所有函數(shù)與數(shù)據(jù)結(jié)構(gòu)

圖片

pbuf數(shù)據(jù)結(jié)構(gòu)

pbuf結(jié)構(gòu)體

//在pbuf.h中定義
struct pbuf {
  /** 指向下一個(gè)pbuf結(jié)構(gòu)體,每個(gè)pbuf存放的數(shù)據(jù)有限,若應(yīng)用有大量的數(shù)據(jù)的話
  就需要多個(gè)pbuf來(lái)存放,可以將同一個(gè)數(shù)據(jù)包的pbuf連接在一起構(gòu)成一個(gè)鏈表  */
  struct pbuf *next;
  /** 指向該pbuf真正的數(shù)據(jù)存儲(chǔ)區(qū)的首地址。STM32F4內(nèi)部網(wǎng)絡(luò)模塊收到數(shù)據(jù),并
  將數(shù)據(jù)提交給LWIP時(shí),就是將數(shù)據(jù)存儲(chǔ)在payload指定的存儲(chǔ)區(qū)中;同樣在發(fā)送數(shù)
  據(jù)的時(shí)候?qū)ayload指向的存儲(chǔ)區(qū)數(shù)據(jù)轉(zhuǎn)給STM32F4的網(wǎng)絡(luò)模塊去發(fā)送 */
  void *payload;
  /** 在接收或發(fā)送數(shù)據(jù)的時(shí)候數(shù)據(jù)會(huì)存放在pbuf鏈表中,tot_Len表示當(dāng)前pbuf和鏈
  表中后面所有pbuf的數(shù)據(jù)長(zhǎng)度,它們屬于一個(gè)數(shù)據(jù)包 */
  u16_t tot_len;
  /** 當(dāng)前pbuf總數(shù)據(jù)的長(zhǎng)度 */
  u16_t len;
  /** 當(dāng)前pbuf類(lèi)型,共有四種:PBUF_RAM/PBUF_ROM/PBUF_REF/PBUF_POOL */
  u8_t  type;
  /** 保留位 */
  u8_t flags;
  /** 該pbuf被引用的次數(shù),當(dāng)還有其他指針指向這個(gè)pbuf的時(shí)候ref字段就加一 */
  u16_t ref;
};

pbuf類(lèi)型:共有四種PBUF_RAM、PBUF_ROM、PBUF_REF、PBUF_POOL

//在pbuf.h中定義
typedef enum {
  /** PBUF_RAM類(lèi)型的pbuf是通過(guò)內(nèi)存堆分配來(lái)的,其payload并未指向數(shù)據(jù)區(qū)的起始
  地址,而是隔了一段區(qū)域,在這個(gè)區(qū)域(offset)里通常存放TCP報(bào)文首部、IP首部
  、以太網(wǎng)幀首部等等 */
  PBUF_RAM,
  /** PBUF_ROM的數(shù)據(jù)指針payload指向外部存儲(chǔ)區(qū),外部存儲(chǔ)區(qū)指不由TCP/IP協(xié)議
  棧管理的存儲(chǔ)區(qū),它可以是應(yīng)用程序管理的存儲(chǔ)器為用戶(hù)數(shù)據(jù)分配的緩存,也可以是
  ROM區(qū)域,如靜態(tài)網(wǎng)頁(yè)中的字符串常量等 */
  PBUF_ROM,
  /** PBUF_REF和PBUF_ROM的特性非常相似,都可以實(shí)現(xiàn)數(shù)據(jù)的零拷貝 */
  PBUF_REF,
  /** PBUF_POOL類(lèi)型的pbuf是通過(guò)內(nèi)存池分類(lèi)來(lái)的,pbuf鏈表的第一個(gè)pbuf的payload
  未指向數(shù)據(jù)區(qū)的起始位置,原因通PBUF_RAM一樣,用來(lái)存放一些首部,pbuf鏈表后面
  的pbuf結(jié)構(gòu)體中的payload就指向了數(shù)據(jù)區(qū)的起始位置 */
  PBUF_POOL
} pbuf_type;

pbuf層:由于LWIP各層禁止數(shù)據(jù)拷貝,所以存在不同層次對(duì)數(shù)據(jù)包pbuf的alloc,前面的offest就是為不同層預(yù)留的頭部字段,下面枚舉了4種層次,分配時(shí)除了要知道大小、類(lèi)型還要傳入這個(gè)層次

//在pbuf.h中定義
typedef enum {
  /** 傳輸層,預(yù)留以太首部+IP首部+TCP首部 */
  PBUF_TRANSPORT,
  /** 網(wǎng)絡(luò)層,預(yù)留以太首部+IP首部 */
  PBUF_IP,
  /** 鏈路層,預(yù)留以太首部 */
  PBUF_LINK,
  /** 原始層,不預(yù)留空間 */
  PBUF_RAW_TX,
  /** Use this for input packets in a netif driver when calling netif->input()
   * in the most common case - ethernet-layer netif driver. */
  PBUF_RAW
} pbuf_layer;

pbuf_alloc:內(nèi)存申請(qǐng)函數(shù)

struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type);
//layer 申請(qǐng)的pbuf所在的層,以此來(lái)確定offset值
//length 存放數(shù)據(jù)的大小
//type pbuf的類(lèi)型

pbuf_free:數(shù)據(jù)包釋放函數(shù)

u8_t pbuf_free(struct pbuf *p);
//p 要釋放的pbuf數(shù)據(jù)包

pbuf_realloc:調(diào)整收縮pbuf的大小,在相應(yīng)pbuf(鏈表)尾部釋放一定的空間,將數(shù)據(jù)包pbuf中的數(shù)據(jù)長(zhǎng)度減少為某個(gè)長(zhǎng)度值

void pbuf_realloc(struct pbuf *p, u16_t new_len);
//p 要收縮的pbuf數(shù)據(jù)包
//new_len 新的長(zhǎng)度值

pbuf_header:調(diào)整payload指針和長(zhǎng)度字段以便為pbuf中的數(shù)據(jù)預(yù)置包頭,常用于實(shí)現(xiàn)對(duì)pbuf預(yù)留孔間的操作

u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment);
//p 要操作的pbuf數(shù)據(jù)包
//header_size_increment 大于0,payload前移,數(shù)據(jù)傳遞下層;
//			小于0,表示payload后移,數(shù)據(jù)傳遞上層

pbuf_take:用于向pbuf的數(shù)據(jù)區(qū)域拷貝數(shù)據(jù)

err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
//buf 要填充數(shù)據(jù)的pbuf
//dataptr 應(yīng)用程序中的數(shù)據(jù)緩沖區(qū)
//len 數(shù)據(jù)緩沖區(qū)的長(zhǎng)度

2.LWIP網(wǎng)卡設(shè)計(jì)與實(shí)現(xiàn)

2.1 LWIP網(wǎng)絡(luò)接口管理

在LWIP中對(duì)于網(wǎng)絡(luò)接口的描述是通過(guò)一個(gè)netif結(jié)構(gòu)體完成的,netif結(jié)構(gòu)體在netif.h文件中有定義

netif結(jié)構(gòu)體

//在netif.h中定義
struct netif {
  struct netif *next;  //指向下一個(gè)netif結(jié)構(gòu)體
#if LWIP_IPV4
  ip_addr_t ip_addr;	//ip地址
  ip_addr_t netmask;	//子網(wǎng)掩碼
  ip_addr_t gw;	//網(wǎng)關(guān)地址
#endif /* LWIP_IPV4 */
  netif_input_fn input;	//netif數(shù)據(jù)包輸入接口函數(shù)指針
#if LWIP_IPV4
  netif_output_fn output;//netif數(shù)據(jù)包輸出接口函數(shù)指針
#endif /* LWIP_IPV4 */
  netif_linkoutput_fn linkoutput;//鏈路層數(shù)據(jù)輸出接口函數(shù)指針
#if LWIP_NETIF_STATUS_CALLBACK
  //當(dāng)netif狀態(tài)發(fā)生變化時(shí),此接口函數(shù)會(huì)調(diào)用
  netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
  /* PHY必須和交換機(jī)或者路由器或者其他具備網(wǎng)卡的主機(jī)相連接,
  我們才可能正常通信比如路由器突然斷電,這個(gè)函數(shù)就會(huì)被調(diào)用 */
  netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
  //netif移除網(wǎng)絡(luò)驅(qū)動(dòng)接口,這個(gè)函數(shù)會(huì)被調(diào)用
  netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  void *state;	//主機(jī)的狀態(tài)
#if LWIP_NETIF_HOSTNAME
  const char*  hostname;	//自定義的主機(jī)名稱(chēng)
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
  u16_t chksum_flags;		
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
  u16_t mtu;	//數(shù)據(jù)鏈路層最大傳輸大小
  u8_t hwaddr_len;	//mac地址長(zhǎng)度
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];//mac地址
  u8_t flags;	//當(dāng)前的netif的狀態(tài),其實(shí)就是下面的netif_flag
  char name[2];	//網(wǎng)卡驅(qū)動(dòng)的名稱(chēng)
  u8_t num;		//網(wǎng)卡驅(qū)動(dòng)的硬件編號(hào)
#if LWIP_IPV4 && LWIP_IGMP
  netif_igmp_mac_filter_fn igmp_mac_filter;//組播底層接口
#endif /* LWIP_IPV4 && LWIP_IGMP */
};

netif flag宏定義

/** netif網(wǎng)絡(luò)接口,可以進(jìn)行正常使用(即lwIP可以正常使用)了 */
#define NETIF_FLAG_UP           0x01U
/** 廣播通訊的標(biāo)志 */
#define NETIF_FLAG_BROADCAST    0x02U
/** STM32 MAC和PHY可以正常使用 */
#define NETIF_FLAG_LINK_UP      0x04U
/** ARP標(biāo)志 */
#define NETIF_FLAG_ETHARP       0x08U
/** TCP/IP協(xié)議正常通信  */
#define NETIF_FLAG_ETHERNET     0x10U

2.2 netif典型API函數(shù)

netif的API函數(shù)是供應(yīng)用層調(diào)用的函數(shù)

netif_add:添加網(wǎng)卡驅(qū)動(dòng)到lwip

struct netif *netif_add(struct netif *netif,
			const ip4_addr_t *ipaddr, 
			const ip4_addr_t *netmask, 
			const ip4_addr_t *gw,
			void *state, 
			netif_init_fn init, 
			netif_input_fn input);

netif_set_default:把網(wǎng)卡恢復(fù)出廠設(shè)置,目前l(fā)wip有一套默認(rèn)參數(shù)

void netif_set_default(struct netif *netif);

netif_set_up&netif_set_down:設(shè)置我們網(wǎng)卡工作狀態(tài),是上線還是離線

void netif_set_up(struct netif *netif);
void netif_set_down(struct netif *netif);

callback:需要自己實(shí)現(xiàn)link_callback函數(shù)

#if LWIP_NETIF_LINK_CALLBACK
void netif_set_link_callback(struct netif *netif, 
		netif_status_callback_fn link_callback);
#endif /* LWIP_NETIF_LINK_CALLBACK */

2.3 netif底層接口函數(shù)

netif底層接口即與硬件打交道的函數(shù)接口,主要包括以下函數(shù)

ethernetif_init:初始化網(wǎng)卡驅(qū)動(dòng)(會(huì)調(diào)用底層驅(qū)動(dòng))

err_t ethernetif_init(struct netif *netif){
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
//arp相關(guān)的函數(shù)接口賦值
#if LWIP_ARP
  netif->output = etharp_output;
#else
  netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
  //鏈路層數(shù)據(jù)輸出函數(shù)接口賦值
  netif->linkoutput = low_level_output;
  /* 底層接口初始化 */
  low_level_init(netif);
  return ERR_OK;
}

ethernetif_input:網(wǎng)卡數(shù)據(jù)輸入(會(huì)調(diào)用底層接口)

void ethernetif_input(void const * argument){
  struct pbuf *p;
  struct netif *netif = (struct netif *) argument;
  for( ;; )
  {
    if (osSemaphoreWait(s_xSemaphore, TIME_WAITING_FOR_INPUT) == osOK)
    {
      do
      {   
        p = low_level_input( netif );
        if   (p != NULL)
        {
          if (netif->input( p, netif) != ERR_OK )
          {
            pbuf_free(p);
          }
        }
      } while(p!=NULL);
    }
  }
}

low_level_init:網(wǎng)卡底層驅(qū)動(dòng),主要針對(duì)硬件(STM32網(wǎng)卡初始化會(huì)在此調(diào)用)

/** 硬件初始化,其實(shí)就STM32 ETH外設(shè)初始化 */
static void low_level_init(struct netif *netif){ 
  uint32_t regvalue = 0;
  HAL_StatusTypeDef hal_eth_init_status;
  /* Init ETH */
  uint8_t MACAddr[6] ;
  heth.Instance = ETH;
  heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
  heth.Init.PhyAddress = DP83848_PHY_ADDRESS;
  MACAddr[0] = 0x00;
  MACAddr[1] = 0x80;
  MACAddr[2] = 0xE1;
  MACAddr[3] = 0x00;
  MACAddr[4] = 0x00;
  MACAddr[5] = 0x00;
  heth.Init.MACAddr = &MACAddr[0];
  heth.Init.RxMode = ETH_RXINTERRUPT_MODE;
  heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
  heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;

  hal_eth_init_status = HAL_ETH_Init(&heth);
  if (hal_eth_init_status == HAL_OK)
  {
    /* 當(dāng)初始化成功后,會(huì)置位flag,同時(shí)在tcp/ip初始化完畢后,會(huì)
       進(jìn)行判斷,此標(biāo)志位決定網(wǎng)卡驅(qū)動(dòng)是否	可以正常使用 */  
    netif->flags |= NETIF_FLAG_LINK_UP;
  }
  /* Initialize Tx Descriptors list: Chain Mode */
  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);     
  /* Initialize Rx Descriptors list: Chain Mode  */
  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); 
#if LWIP_ARP || LWIP_ETHERNET 
  /* MAC地址初始化 */
  netif->hwaddr_len = ETH_HWADDR_LEN;
  netif->hwaddr[0] =  heth.Init.MACAddr[0];
  netif->hwaddr[1] =  heth.Init.MACAddr[1];
  netif->hwaddr[2] =  heth.Init.MACAddr[2];
  netif->hwaddr[3] =  heth.Init.MACAddr[3];
  netif->hwaddr[4] =  heth.Init.MACAddr[4];
  netif->hwaddr[5] =  heth.Init.MACAddr[5]; 
  /* maximum transfer unit */
  netif->mtu = 1500; 
  /* Accept broadcast address and ARP traffic */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  #if LWIP_ARP
    netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
  #else 
    netif->flags |= NETIF_FLAG_BROADCAST;
  #endif /* LWIP_ARP */ 
  /* 二值信號(hào)量,用于信息同步,當(dāng)網(wǎng)卡接口到數(shù)據(jù)后,會(huì)釋放二值信號(hào)量讓其他任務(wù)進(jìn)行解析 */
  osSemaphoreDef(SEM);
  s_xSemaphore = osSemaphoreCreate(osSemaphore(SEM), 1);
  /* 創(chuàng)建網(wǎng)卡數(shù)據(jù)接收解析任務(wù)-ethernetif_input */
  osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
  osThreadCreate (osThread(EthIf), netif);
  /* 使能 網(wǎng)卡 發(fā)送和接口 */
  HAL_ETH_Start(&heth); 
  /* 上面的都是針對(duì)STM32 ETH外設(shè)進(jìn)行初始化
  	但是實(shí)際網(wǎng)絡(luò)交互是用過(guò)PHY,下面就是初始化PHY */
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_MICR, ®value); 
  regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
  /* Enable Interrupts */
  HAL_ETH_WritePHYRegister(&heth, PHY_MICR, regvalue );
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_MISR, ®value);
  regvalue |= PHY_MISR_LINK_INT_EN;    
  /* Enable Interrupt on change of link status */
  HAL_ETH_WritePHYRegister(&heth, PHY_MISR, regvalue);
#endif /* LWIP_ARP || LWIP_ETHERNET */
}

low_level_output:底層網(wǎng)卡的數(shù)據(jù)輸出,實(shí)際的數(shù)據(jù)輸出,是通過(guò)pbuf進(jìn)行封裝管理的

static err_t low_level_output(struct netif *netif, struct pbuf *p){
  err_t errval;
  struct pbuf *q;
  uint8_t *buffer = (uint8_t *)(heth.TxDesc->Buffer1Addr);
  __IO ETH_DMADescTypeDef *DmaTxDesc;
  uint32_t framelength = 0;
  uint32_t bufferoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t payloadoffset = 0;
  DmaTxDesc = heth.TxDesc;
  bufferoffset = 0;

  /* copy frame from pbufs to driver buffers */
  for(q = p; q != NULL; q = q->next)
  {
    /* Is this buffer available? If not, goto error */
    if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
    {
      errval = ERR_USE;
      goto error;
    }   
    /* Get bytes in current lwIP buffer */
    byteslefttocopy = q->len;
    payloadoffset = 0;
    /* Check if the length of data to copy is bigger than Tx buffer size*/
    while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
    {
      /* Copy data to Tx buffer*/
      memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );      
      /* Point to next descriptor */
      DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
      /* Check if the buffer is available */
      if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
      {
        errval = ERR_USE;
        goto error;
      }
      buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
      byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
      payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
      framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
      bufferoffset = 0;
    }

    /* Copy the remaining bytes */
    memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
    bufferoffset = bufferoffset + byteslefttocopy;
    framelength = framelength + byteslefttocopy;
  }  
  /* 把pbuf里面的數(shù)據(jù),發(fā)送到ETH外設(shè)里面 */ 
  HAL_ETH_TransmitFrame(&heth, framelength);  
  errval = ERR_OK;  
error:  
  /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
  if ((heth.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
  {
    /* Clear TUS ETHERNET DMA flag */
    heth.Instance->DMASR = ETH_DMASR_TUS;
    /* Resume DMA transmission*/
    heth.Instance->DMATPDR = 0;
  }
  return errval;
}

low_level_input:底層網(wǎng)卡的數(shù)據(jù)接口,當(dāng)接收到網(wǎng)卡數(shù)據(jù)后,會(huì)通過(guò)此函數(shù),封裝為pbuf提供上層使用

static struct pbuf * low_level_input(struct netif *netif){
  struct pbuf *p = NULL;
  struct pbuf *q = NULL;
  uint16_t len = 0;
  uint8_t *buffer;
  __IO ETH_DMADescTypeDef *dmarxdesc;
  uint32_t bufferoffset = 0;
  uint32_t payloadoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t i=0;  
  /* 通過(guò)HAL庫(kù),獲取網(wǎng)卡幀數(shù)據(jù) */
  if (HAL_ETH_GetReceivedFrame_IT(&heth) != HAL_OK)
    return NULL;  
  /* 獲取網(wǎng)卡數(shù)據(jù)超度,及內(nèi)存地址 */
  len = heth.RxFrameInfos.length;
  buffer = (uint8_t *)heth.RxFrameInfos.buffer;
  //網(wǎng)卡中數(shù)據(jù)有效
  if (len > 0){
    /* 網(wǎng)卡數(shù)據(jù)不能大于1500,屬于原始層接口  */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  }
  //如果pbuf創(chuàng)建成功,則從ETH中拷貝數(shù)據(jù)到pbuf里,最終把pbuf返回給上層應(yīng)用
  if (p != NULL){
    dmarxdesc = heth.RxFrameInfos.FSRxDesc;
    bufferoffset = 0;
    for(q = p; q != NULL; q = q->next){
      byteslefttocopy = q->len;
      payloadoffset = 0;
      /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
      while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ){
        /* Copy data to pbuf */
        memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));  
        /* Point to next descriptor */
        dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
        buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);   
        byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
        payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
        bufferoffset = 0;
      }
      /* Copy remaining data in pbuf */
      memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
      bufferoffset = bufferoffset + byteslefttocopy;
    }
  }   
    /* Release descriptors to DMA */
    /* Point to first descriptor */
    dmarxdesc = heth.RxFrameInfos.FSRxDesc;
    /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
    for (i=0; i< heth.RxFrameInfos.SegCount; i++){  
      dmarxdesc->Status |= ETH_DMARXDESC_OWN;
      dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
    }   
    /* Clear Segment_Count */
    heth.RxFrameInfos.SegCount =0;    
    /* When Rx Buffer unavailable flag is set: clear it and resume reception */
    if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)  {
      /* Clear RBUS ETHERNET DMA flag */
      heth.Instance->DMASR = ETH_DMASR_RBUS;
      /* Resume DMA reception */
      heth.Instance->DMARPDR = 0;
    }
  return p;
}

tcpip_init:工作在操作系統(tǒng)下的初始化

/**
 * @工作在操作系統(tǒng)下的初始化:
 * - 初始化所有的子功能模塊
 * - 啟動(dòng)tcp/ip任務(wù)(tcp/ip網(wǎng)絡(luò)協(xié)議棧的實(shí)現(xiàn)是一個(gè)任務(wù)里面執(zhí)行的)
 * @param 用于用戶(hù)初始化的函數(shù)指針,在lwip初始化完成,tcp/ip任務(wù)開(kāi)始執(zhí)行就是進(jìn)行調(diào)用
 * @param 用戶(hù)初始化相關(guān)參數(shù)傳入
 */
void tcpip_init(tcpip_init_done_fn initfunc, void *arg){
  //lwip的初始化---初始化lwip所有功能模塊
  lwip_init();
  //用戶(hù)初始化函數(shù)指針賦值,參數(shù)賦值
  tcpip_init_done = initfunc;
  tcpip_init_done_arg = arg;
  //消息郵箱(freeRTOS是通過(guò)消息隊(duì)列實(shí)現(xiàn)),任務(wù)與任務(wù)間消息通信,網(wǎng)卡收到數(shù)據(jù),網(wǎng)絡(luò)分層解析
  if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
  }
#if LWIP_TCPIP_CORE_LOCKING
  //創(chuàng)建互斥鎖(互斥信號(hào)量),保護(hù)共享資源的
  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
  }
#endif /* LWIP_TCPIP_CORE_LOCKING */
  //這是標(biāo)準(zhǔn)的cmis接口,其實(shí)內(nèi)部調(diào)用的freeRTOS的創(chuàng)建任務(wù)接口
  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

lwip_init:工作在裸機(jī)模式下的初始化

/**
 * @工作在裸機(jī)模式下的初始化
 * Use this in NO_SYS mode. Use tcpip_init() otherwise.
 */
void lwip_init(void){
  /* Modules initialization */
  //狀態(tài)初始化
  stats_init();
#if !NO_SYS
  //與操作系統(tǒng)相關(guān)的初始化
  sys_init();
#endif /* !NO_SYS */
  //內(nèi)存堆 內(nèi)存池 pbuf netif初始化
  mem_init();
  memp_init();
  pbuf_init();
  netif_init();
#if LWIP_IPV4
  //ip層初始化
  ip_init();
#if LWIP_ARP
  //arp+以太網(wǎng)相關(guān)的初始化
  etharp_init();
#endif /* LWIP_ARP */
#endif /* LWIP_IPV4 */
#if LWIP_RAW
  //原生接口初始化
  raw_init();
#endif /* LWIP_RAW */
#if LWIP_UDP
  udp_init();
#endif /* LWIP_UDP */
#if LWIP_TCP
  tcp_init();
#endif /* LWIP_TCP */
#if LWIP_IGMP
  igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
  dns_init();
#endif /* LWIP_DNS */
#if PPP_SUPPORT
  ppp_init();
#endif 
#if LWIP_TIMERS
  //lwip內(nèi)部有很多超時(shí)機(jī)制,是通過(guò)timeouts實(shí)現(xiàn)的(一個(gè)軟件定時(shí)器
  sys_timeouts_init();
#endif /* LWIP_TIMERS */
}

MX_LWIP_Init:HAL庫(kù)實(shí)現(xiàn)的lwip初始化函數(shù)

void MX_LWIP_Init(void){
  /* IP 地址初始化 */
  IP_ADDRESS[0] = 192;
  IP_ADDRESS[1] = 168;
  IP_ADDRESS[2] = 1;
  IP_ADDRESS[3] = 10;
  NETMASK_ADDRESS[0] = 255;
  NETMASK_ADDRESS[1] = 255;
  NETMASK_ADDRESS[2] = 255;
  NETMASK_ADDRESS[3] = 0;
  GATEWAY_ADDRESS[0] = 192;
  GATEWAY_ADDRESS[1] = 168;
  GATEWAY_ADDRESS[2] = 1;
  GATEWAY_ADDRESS[3] = 1;  
  /* 初始化lwip協(xié)議棧 */
  tcpip_init( NULL, NULL );
  /* 數(shù)組格式的IP地址轉(zhuǎn)換為lwip格式的地址 */
  IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
  IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
  IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
  /* 裝載網(wǎng)卡驅(qū)動(dòng),并初始化網(wǎng)卡 */
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
  /* gnetif注冊(cè)為默認(rèn)網(wǎng)卡驅(qū)動(dòng) */
  netif_set_default(&gnetif);
  // 判斷phy和mac層是否正常工作
  if (netif_is_link_up(&gnetif)){
    /* netif 網(wǎng)卡驅(qū)動(dòng)可以正常使用,上線 */
    netif_set_up(&gnetif);
  }
  else{
    /* netif 網(wǎng)卡驅(qū)動(dòng)下線 */
    netif_set_down(&gnetif);
  }
}
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲(chǔ)器
    +關(guān)注

    關(guān)注

    38

    文章

    7366

    瀏覽量

    163091
  • 網(wǎng)卡
    +關(guān)注

    關(guān)注

    3

    文章

    296

    瀏覽量

    27246
  • 內(nèi)存管理
    +關(guān)注

    關(guān)注

    0

    文章

    167

    瀏覽量

    14099
  • LwIP
    +關(guān)注

    關(guān)注

    2

    文章

    84

    瀏覽量

    26932
  • 協(xié)議棧
    +關(guān)注

    關(guān)注

    2

    文章

    137

    瀏覽量

    33571
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    lwip協(xié)議代碼分析

    lwIP(Lightweight IP)是一個(gè)為嵌入式系統(tǒng)設(shè)計(jì)的輕量級(jí)TCP/IP協(xié)議
    的頭像 發(fā)表于 10-29 17:37 ?1612次閱讀
    <b class='flag-5'>lwip</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>代碼分析

    LwIP協(xié)議的設(shè)計(jì)與實(shí)現(xiàn)資料分享!

    LwIP協(xié)議的設(shè)計(jì)與實(shí)現(xiàn)_中文譯稿LwIP協(xié)議
    發(fā)表于 07-31 23:47

    嵌入式TCPIP協(xié)議LWIP的內(nèi)部結(jié)構(gòu)

    分析了嵌入式 TCPIP協(xié)議主要對(duì)LWIP的基本結(jié)構(gòu),介紹了嵌入式TCPIP協(xié)議LWIP
    發(fā)表于 02-17 15:55 ?76次下載
    嵌入式TCPIP<b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b><b class='flag-5'>LWIP</b>的內(nèi)部結(jié)構(gòu)

    Lwip協(xié)議的設(shè)計(jì)方案

    LWIP是TCP/IP協(xié)議的一種實(shí)現(xiàn)。LWIP的主要目的是減少存儲(chǔ)器利用量和代碼尺寸,使LWIP
    發(fā)表于 09-16 15:18 ?33次下載
    <b class='flag-5'>Lwip</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>的設(shè)計(jì)<b class='flag-5'>方案</b>

    基于ARM的LwIP協(xié)議研究與移植

    提出基于ARM的LwIP協(xié)議研究與移植
    發(fā)表于 10-14 17:50 ?65次下載
    基于ARM的<b class='flag-5'>LwIP</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>研究與移植

    lwip協(xié)議中文版

    LWIP是TCP/IP協(xié)議的一種實(shí)現(xiàn)。LWIP的主要目的是減少存儲(chǔ)器利用量和代碼尺寸,使LWIP
    發(fā)表于 02-03 16:47 ?0次下載
    <b class='flag-5'>lwip</b><b class='flag-5'>協(xié)議</b>中文版

    uCOS-II下實(shí)現(xiàn)lwip協(xié)議實(shí)現(xiàn)Ping功能

    uCOS-II下實(shí)現(xiàn)lwip協(xié)議實(shí)現(xiàn)Ping功能
    發(fā)表于 03-26 15:51 ?143次下載

    LwIP協(xié)議詳解

    LwIP協(xié)議詳解,LwIP是Light Weight (輕型)IP協(xié)議,有無(wú)操作系統(tǒng)的支持都可以運(yùn)行。LwIP
    發(fā)表于 11-09 18:25 ?49次下載

    TCPIP協(xié)議實(shí)現(xiàn)lwip

    TCPIP協(xié)議實(shí)現(xiàn)lwip方便初學(xué)者剛開(kāi)始接觸lwip,有個(gè)大概的了解與認(rèn)識(shí)。
    發(fā)表于 03-14 15:40 ?13次下載

    lwip協(xié)議源碼詳解說(shuō)明

    lwip是瑞典計(jì)算機(jī)科學(xué)院(SICS)的Adam Dunkels 開(kāi)發(fā)的一個(gè)小型開(kāi)源的TCP/IP協(xié)議實(shí)現(xiàn)的重點(diǎn)是在保持TCP協(xié)議主要功
    發(fā)表于 12-11 15:27 ?3.7w次閱讀
    <b class='flag-5'>lwip</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>源碼詳解說(shuō)明

    LWIP內(nèi)存管理知識(shí)匯總

    LWIP內(nèi)存管理LWIP內(nèi)存管理使用了2種方式:內(nèi)存
    的頭像 發(fā)表于 03-06 10:01 ?6785次閱讀
    <b class='flag-5'>LWIP</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>知識(shí)匯總

    LWIP協(xié)議中Raw TCP中使用

    本文檔的主要內(nèi)容詳細(xì)介紹的是LWIP協(xié)議中Raw TCP中使用的資料免費(fèi)下載
    發(fā)表于 11-05 17:36 ?17次下載
    <b class='flag-5'>LWIP</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>中Raw TCP中使用

    Xilinx LwIP 例程解析:網(wǎng)卡驅(qū)動(dòng)(接收篇)

    對(duì)于 LwIP 協(xié)議的移植來(lái)說(shuō),用戶(hù)的主要工作是為其提供網(wǎng)卡驅(qū)動(dòng)函數(shù)。LwIP 可以運(yùn)行在多種不同的硬件平臺(tái)上,配合不同型號(hào)的網(wǎng)絡(luò) Phy
    發(fā)表于 02-07 10:09 ?9次下載
    Xilinx <b class='flag-5'>LwIP</b> 例程解析:<b class='flag-5'>網(wǎng)卡</b>驅(qū)動(dòng)(接收篇)

    如何更好的理解LWIP協(xié)議

    LwIP(Light weight IP),是一種輕量化且開(kāi)源的TCP/IP協(xié)議,它可以在有限的RAM和ROM條件下,實(shí)現(xiàn)一個(gè)完整的TCP/IP
    的頭像 發(fā)表于 10-27 09:13 ?4015次閱讀

    LwIP協(xié)議源碼詳解—TCP/IP協(xié)議實(shí)現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《LwIP協(xié)議源碼詳解—TCP/IP協(xié)議實(shí)現(xiàn).pdf》資料免費(fèi)下載
    發(fā)表于 07-03 11:22 ?1次下載