网络设备驱动模型
本章将介绍 MS-RTOS 网络设备驱动模型。
MS-RTOS 集成了 lwIP 轻量级 TCP/IP 协议栈,所以需要参照 lwIP 的以太网设备驱动规范来编写 MS-RTOS 上的以太网设备驱动。BSP 需要链接 libnet_lwip.a
静态库,即 bspxxx.mk
的 LOCAL_DEPEND_LIB
需要添加 -lnet_lwip
。同时,MS-RTOS 也支持 ESP 无线网络连接。
1. 网络基础知识
1.1 以太网
网口由 CPU、MAC 和 PHY 三部分组成。DMA 控制器通常属于 CPU 的一部分,用虚线放在这里是为了表示 DMA 控制器可能会参与到网口数据传输中。
对于上述的三部分,并不一定都是独立的芯片,根据组合形式,可分为下列几种类型:
- CPU 集成 MAC 与 PHY;
- CPU 集成 MAC,PHY 采用独立芯片;
- CPU 不集成 MAC 与 PHY,MAC 与 PHY 采用集成芯片;
本例中选用方案二做进一步说明,因为 CPU 总线接口很常见,通常都会做成可以像访问内存一样去访问,没必要拿出来说,而 Mac 与 PHY 之间的 MII 接口则需要多做些说明。下图是采用方案二的网口结构图。虚框表示 CPU,MAC 集成在 CPU 中。PHY 芯片通过 MII 接口与 CPU 上的 Mac 连接。
在软件上对网口的操作通常分为下面几步:
- 为数据收发分配内存;
- 初始化 MAC 寄存器;
- 初始化 PHY 寄存器(通过 MIIM);
- 启动收发。
1.2 WIFI (ESP8266)
ESP8266 是 ai-thinker 公司推出的一款无线 WIFI 模块,专为移动设备,可穿戴电子产品和物联应用设计,可以通过 AT 指令配置,和单片机上的串口进行通信,利用 WIFI 进行数据传输。ESP_XXXXXX,后面的数字是 MAC 地址后几位。ESP8266 可以用来做串口透传,PWM 调控,远程控制开关:控制插座、开关、电器等。
ESP8266 有几种不同的使用方式,适用于不同水平的开发工作者。
- 使用 AT 指令进行操作:这是最常见的方式,也是最简单是一种方式。无需编程,使用 PC 端的串口助手配合简单的指令就可以实现,也可以配合单片机发送指令使用。
- LUA 语言编程:这是一种单独 8266 编程的方式,可以不依靠单片机和串口调试软件,直接把程序编写到 8266 内部。
- Arduino 开发环境编程:这个接触过 Arduino 的都会比较熟悉。可以直接在 Arduino ide 的环境下使用 Arduino 的开发方式进行开发。
ESP8266 共有三种工作模式,分别是 Station 模式(客户端模式),AP 模式(接入点模式),AP+Station 模式(混合模式)。ESP8266 出厂默认是第三种模式。其中 STA 模式:ESP8266 模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。AP 模式:ESP8266 模块作为热点,手机或电脑直接与模块连接,实现局域网无线控制。STA+AP 模式:两种模式的共存模式,即可以通过互联网控制可实现无缝切换,方便操作。
透传即是透明传送,是指传送网络无论传输业务如何,只负责将需要传送的业务传送到目的节点,同时保证传输的质量即可,而不对传输的业务进行处理。在数据的传输过程中,这组数据不发生任何形式的改变,即不竭断,不分组,不编码,不加密,不混淆等等,仿佛传输过程是透明的一样,原封不动地到了最终接收者手里。 透传模式的要求:
- 透传模式只能在单链接模式下开启;
- 模块开启服务器模式时,必须开启多链接模式,所以只能作为单链接模式下的客户端。
透传与非透传的区别:开启透传模式,可以连续的发送数据,而非透传模式下,每次发送数据前都需要发送相关的发送数据的 AT 指令。
2. 网络子系统框架
下图描绘了注册网络设备驱动和应用层通过 socket 接口访问网络设备的部分 IO 流程,仅为示意图:
在 MS-RTOS 上适配新的网络类型时,需要实现/填充 ms_net_impl_t
结构体,即相当于实例化一个网络对象,然后调用 ms_net_impl_register(&ms_xxx_net_impl)
将该类型网络注册到 MS-RTOS 中。同时,还需要实现/填充 ms_io_driver_t
结构体,该结构体包含对 socket 进行 IO 操作的支持,然后调用 ms_io_driver_register(&ms_xxx_socket_drv)
。
(1)lwIP 协议栈初始化
BSP 需要调用 ms_lwip_net_init
函数初始化 lwIP 协议栈:
// 初始化 lwIP 协议栈
ms_err_t ms_lwip_net_init(void (*init_done_callback)(ms_ptr_t arg), ms_ptr_t arg);
(2)网络接口初始化
BSP 还需要完成网络接口的初始化及添加:
static void stm32_netif_init(void)
{
static struct netif gnetif;
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;
ip4addr_aton("192.168.1.11", &ipaddr);
ip4addr_aton("255.255.255.0", &netmask);
ip4addr_aton("192.168.1.1", &gw);
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/*
* Registers the default network interface.
*/
netif_set_default(&gnetif);
if (netif_is_link_up(&gnetif)) {
/*
* When the netif is fully configured this function must be called.
*/
netif_set_up(&gnetif);
} else {
/*
* When the netif link is down this function must be called
*/
netif_set_down(&gnetif);
}
}
(3)LwIP 接收数据的流程
网卡是怎么接收数据的,无需多说,基本就是开发板上 eth 接收完数据后产生一个中断,然后释放一个信号量通知网卡接收线程去处理这些接收的数据,然后将这些数据封装成消息,投递到 tcpip_mbox 邮箱中,LwIP 内核线程得到这个消息,就对消息进行解析,根据消息中数据包类型进行处理,实际上是调用 ethernet_input()
函数决定是否递交到 IP 层,如果是 ARP 包,内核就不会递交给 IP 层,而是更新 ARP 缓存表,对于 IP 数据包则递交给 IP 层去处理,这就是一个数据从网卡到内核的过程,具体见下图:
3. 网络设备驱动
STM32 以太网设备驱动示例,仅作为参考:
/* Includes ------------------------------------------------------------------*/
#include "stm32f7xx_hal.h"
#include "lwip/opt.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "stm32_drv_netif.h"
#include <string.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* The time to block waiting for input. */
#define TIME_WAITING_FOR_INPUT ( MS_TIMEOUT_FOREVER )
/* Stack size of the interface thread */
#define INTERFACE_THREAD_STACK_SIZE ( 512 )
/* Define those to better describe your network interface. */
#define IFNAME0 's'
#define IFNAME1 't'
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/*
@Note: The DMARxDscrTab and DMATxDscrTab must be declared in a device non cacheable memory region
In this example they are declared in the first 256 Byte of SRAM2 memory, so this
memory region is configured by MPU as a device memory.
In this example the ETH buffers are located in the SRAM2 with MPU configured as normal
not cacheable memory.
Please refer to MPU_Config() in main.c file.
*/
#if defined ( __CC_ARM )
ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __attribute__((at(0x2004C000)));/* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __attribute__((at(0x2004C0F0)));/* Ethernet Tx DMA Descriptors */
uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __attribute__((at(0x2004C1FE))); /* Ethernet Receive Buffers */
uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __attribute__((at(0x2004E0FF))); /* Ethernet Transmit Buffers */
#elif defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma data_alignment=4
#pragma location=0x2004C000
__no_init ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx DMA Descriptors */
#pragma location=0x2004C080
__no_init ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptors */
#pragma location=0x2004C100
__no_init uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffers */
#pragma location=0x2004D8D0
__no_init uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffers */
#elif defined ( __GNUC__ ) /*!< GNU Compiler */
ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __attribute__((section(".RxDescripSection")));/* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __attribute__((section(".TxDescripSection")));/* Ethernet Tx DMA Descriptors */
uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __attribute__((section(".RxarraySection"))); /* Ethernet Receive Buffers */
uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __attribute__((section(".TxarraySection"))); /* Ethernet Transmit Buffers */
#endif
/* Semaphore to signal incoming packets */
static ms_handle_t s_xSemaphore = MS_HANDLE_INVALID;
static ms_handle_t s_xThread = MS_HANDLE_INVALID;
/* Global Ethernet handle*/
ETH_HandleTypeDef EthHandle;
/* Private function prototypes -----------------------------------------------*/
static void ethernetif_input( void const * argument );
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
Ethernet MSP Routines
*******************************************************************************/
/**
* @brief Initializes the ETH MSP.
* @param heth: ETH handle
* @retval None
*/
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOs clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/* Ethernet pins configuration ************************************************/
/*
RMII_REF_CLK ----------------------> PA1
RMII_MDIO -------------------------> PA2
RMII_MDC --------------------------> PC1
RMII_MII_CRS_DV -------------------> PA7
RMII_MII_RXD0 ---------------------> PC4
RMII_MII_RXD1 ---------------------> PC5
RMII_MII_RXER ---------------------> PG2
RMII_MII_TX_EN --------------------> PG11
RMII_MII_TXD0 ---------------------> PG13
RMII_MII_TXD1 ---------------------> PG14
*/
/* Configure PA1, PA2 and PA7 */
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure PC1, PC4 and PC5 */
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Configure PG2, PG11, PG13 and PG14 */
GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
/* Enable the Ethernet global Interrupt */
HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
/* Enable ETHERNET clock */
__HAL_RCC_ETH_CLK_ENABLE();
}
/**
* @brief This function handles Ethernet interrupt request.
* @param None
* @retval None
*/
void ETH_IRQHandler(void)
{
(void)ms_int_enter();
HAL_ETH_IRQHandler(&EthHandle);
(void)ms_int_exit();
}
/**
* @brief Ethernet Rx Transfer completed callback
* @param heth: ETH handle
* @retval None
*/
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
ms_semb_post(s_xSemaphore);
}
/*******************************************************************************
LL Driver Interface ( LwIP stack --> ETH)
*******************************************************************************/
/**
* @brief In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
uint8_t macaddress[6]= { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 };
EthHandle.Instance = ETH;
EthHandle.Init.MACAddr = macaddress;
EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
EthHandle.Init.Speed = ETH_SPEED_100M;
EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE;
EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS;
/* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */
if (HAL_ETH_Init(&EthHandle) == HAL_OK)
{
/* Set netif link flag */
netif->flags |= NETIF_FLAG_LINK_UP;
}
/* Initialize Tx Descriptors list: Chain Mode */
HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
/* Initialize Rx Descriptors list: Chain Mode */
HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
/* set netif MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* set netif MAC hardware address */
netif->hwaddr[0] = MAC_ADDR0;
netif->hwaddr[1] = MAC_ADDR1;
netif->hwaddr[2] = MAC_ADDR2;
netif->hwaddr[3] = MAC_ADDR3;
netif->hwaddr[4] = MAC_ADDR4;
netif->hwaddr[5] = MAC_ADDR5;
/* set netif maximum transfer unit */
netif->mtu = 1500;
/* Accept broadcast address and ARP traffic */
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
/* create a binary semaphore used for informing ethernetif of frame reception */
ms_semb_create("netif_semb", MS_FALSE, MS_WAIT_TYPE_PRIO, &s_xSemaphore);
/* create the task that handles the ETH_MAC */
ms_thread_create("t_netif", (ms_thread_entry_t)ethernetif_input, netif,
INTERFACE_THREAD_STACK_SIZE, TCPIP_THREAD_PRIO, TCPIP_THREAD_TIME_SLICE,
MS_THREAD_OPT_SUPER, &s_xThread);
/* Enable MAC and DMA transmission and reception */
HAL_ETH_Start(&EthHandle);
}
/**
* @brief This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
err_t errval;
struct pbuf *q;
uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr);
__IO ETH_DMADescTypeDef *DmaTxDesc;
uint32_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
DmaTxDesc = EthHandle.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;
}
/* Prepare transmit descriptors to give to DMA */
HAL_ETH_TransmitFrame(&EthHandle, framelength);
errval = ERR_OK;
error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
EthHandle.Instance->DMASR = ETH_DMASR_TUS;
/* Resume DMA transmission*/
EthHandle.Instance->DMATPDR = 0;
}
return errval;
}
/**
* @brief Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p = NULL, *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;
/* get received frame */
if(HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK)
return NULL;
/* Obtain the size of the packet and put it into the "len" variable. */
len = EthHandle.RxFrameInfos.length;
buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer;
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
if (p != NULL)
{
dmarxdesc = EthHandle.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 = EthHandle.RxFrameInfos.FSRxDesc;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i< EthHandle.RxFrameInfos.SegCount; i++)
{
dmarxdesc->Status |= ETH_DMARXDESC_OWN;
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
EthHandle.RxFrameInfos.SegCount =0;
/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)
{
/* Clear RBUS ETHERNET DMA flag */
EthHandle.Instance->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception */
EthHandle.Instance->DMARPDR = 0;
}
return p;
}
/**
* @brief This function is the ethernetif_input task, it is processed when a packet
* is ready to be read from the interface. It uses the function low_level_input()
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input( void const * argument )
{
struct pbuf *p;
struct netif *netif = (struct netif *) argument;
for( ;; )
{
if (ms_semb_wait( s_xSemaphore, TIME_WAITING_FOR_INPUT) == MS_ERR_NONE)
{
do
{
p = low_level_input( netif );
if (p != NULL)
{
if (netif->input( p, netif) != ERR_OK )
{
pbuf_free(p);
}
}
}while(p!=NULL);
}
}
}
/**
* @brief Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
netif->output = etharp_output;
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
4. 网络应用程序
创建 socket 套接字,获取网卡 IP 和 MAC 地址等信息:
#include <ms_rtos.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
int show_mac_addr(int sockfd)
{
int ret;
struct ifreq ifreq;
if (sockfd < 0) {
return (-1);
}
bzero(&ifreq, sizeof(struct ifreq));
/*
* Get MAC address
*/
ret = ioctl(sockfd, SIOCGIFHWADDR, &ifreq);
if (ret < 0) {
return (-1);
}
ms_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[0],
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[1],
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[2],
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[3],
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[4],
(ms_uint8_t)ifreq.ifr_hwaddr.sa_data[5]);
return (0);
}
int show_ip_addr(int sockfd)
{
int ret;
struct ifreq ifreq;
struct sockaddr_in *psockaddrin;
char ip[sizeof("255.255.255.255")];
if (sockfd < 0) {
return (-1);
}
bzero(&ifreq, sizeof(struct ifreq));
psockaddrin = (struct sockaddr_in *)&(ifreq.ifr_addr);
/*
* Get IP address
*/
ret = ioctl(sockfd, SIOCGIFADDR, &ifreq);
if (ret < 0) {
return (-1);
}
inet_ntoa_r(psockaddrin->sin_addr, ip, sizeof(ip));
ms_printf("IP addr: %s\n", ip);
return (0);
}
int main (int argc, char **argv)
{
int ret;
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
ms_printf("Failed to create socket, errno = %d!\n", errno);
return (-1);
}
ret = show_mac_addr(sockfd);
if (ret < 0) {
ms_printf("Failed to show MAC address!\n");
close(sockfd);
return (-1);
}
ret = show_ip_addr(sockfd);
if (ret < 0) {
ms_printf("Failed to show IP address!\n");
close(sockfd);
return (-1);
}
close(sockfd);
return (0);
}
附录(Appendix)
1. Reference
https://www.cnblogs.com/jason-lu/p/3198424.html
https://www.cnblogs.com/jason-lu/articles/3196096.html
https://www.cnblogs.com/jason-lu/p/3195473.html
https://blog.csdn.net/xh870189248/article/details/77985541
https://www.cnblogs.com/kerwincui/p/12872828.html
https://blog.csdn.net/flyleaf91/article/details/52325542
2. FAQ
(暂无)