OpenHarmony NetDev 原创 精华
NetDev
WIFI 芯片属于网络设备,自然也要归OpenHarmony的网络框架管理,本文用于了解 网络数据如何在协议栈和网络驱动之间传输。
网络设备的使用需要配合网络协议栈,OpenHarmony的网络协议栈有两种,一种是liteos-a内核使用的lwip协议栈,一种是标准系统linux内核网络协议栈。
本文以lwip协议栈为例来了解。
一、前提
网络数据的通路:(这里的网口驱动是具体wifi芯片的驱动程序,由厂商实现。)
发送数据:应用程序->lwip->网口驱动
接受数据:网口驱动->lwip->应用程序
二、NetDevice
openharmony使用NetDevice结构体来描述所有的网络设备(网口)
typedef struct NetDevice {
NetIfCategory netifCateg; //内核类型
char name[IFNAMSIZ]; //设备名称
NetLinkType LinkLayerType; //链路层类型:以太网或wifi
IfType funType; //网络端口类型 including AP, STA, and P2P
uint8_t macAddr[MAC_ADDR_SIZE]; //mac地址
....
struct NetDeviceInterFace *netDeviceIf; //提供给驱动厂商的接口
struct NetDevice *owner; /**< Network device */
struct NetDevStats stats; /**< Network statistics */
} NetDevice;
其中 NetDeviceInterFace 对象的作用是初始化、打开、关闭一个网络设备等以下功能,必须由驱动开发厂商根据具体的wifi芯片编写的驱动来实现其中的函数:
struct NetDeviceInterFace {
int32_t (*init)(struct NetDevice *netDev); //初始化网络设备
void (*deInit)(struct NetDevice *netDev);
int32_t (*open)(struct NetDevice *netDev); //打开数据链路层
int32_t (*stop)(struct NetDevice *netDev);
NetDevTxResult (*xmit)(struct NetDevice *netDev, NetBuf *netBuff); //发送数据
int32_t (*ioctl)(struct NetDevice *netDev, IfReq *req, int32_t cmd); /**< Used for the control command word. */
int32_t (*setMacAddr)(struct NetDevice *netDev, void *addr); //设置mac地址
struct NetDevStats *(*getStats)(struct NetDevice *netDev); //获取设备状态
void (*setNetIfStatus)(struct NetDevice *netDev, NetIfStatus status); //设置状态
uint16_t (*selectQueue)(struct NetDevice *netDev, NetBuf *netBuff); //选择一个队列
uint32_t (*netifNotify)(struct NetDevice *netDev, NetDevNotify *notify); /**< Notifies the network port status. */
int32_t (*changeMtu)(struct NetDevice *netDev, int32_t newMtu); //改变mtu
void (*linkStatusChanged)(struct NetDevice *netDev); //检查以太网连接状态
ProcessingResult (*specialEtherTypeProcess)(const struct NetDevice *netDev, NetBuf *buff);
/**< Performs private processing without
* involving network-layer data.
*/
};
根据HdfWlanDevice 创建NetDevice:
struct NetDevice *AllocPlatformNetDevice(struct HdfWlanDevice *device)
{
struct NetDevice *result = NULL;
char ifName[IFNAMSIZ] = {0};
struct HdfWifiNetDeviceData *data = NULL;
data = (struct HdfWifiNetDeviceData *)OsalMemCalloc(sizeof(struct HdfWifiNetDeviceData));
do {
//分配网卡编号
for (i = 0; i < MAX_IF_COUNT; i++) {
if (((g_allocatedIfMap >> i) & 0x1) != 0x1) {
id = i;
break;
}
}
//生成网卡名称:wlan0
ret = GetPlatformIfName(id, ifName, IFNAMSIZ);
#ifdef _PRE_HDF_LINUX
result = NetDeviceInit(ifName, strlen(ifName), WIFI_LINK, FULL_OS);
#else
//获取netdev
result = NetDeviceInit(ifName, strlen(ifName), WIFI_LINK, LITE_OS);
#endif
} while (false);
result->classDriverName = HDF_WIFI_PLATFORM_DRIVER_NAME;
result->classDriverPriv = data;
data->netInterfaceId = id;
SET_BIT(device->netIfMap, id);
SET_BIT(g_allocatedIfMap, id);
return result;
}
三、NetBuf
NetBuf用于描述网络数据,以在网络协议栈和网络驱动之间传输数据。
//记录一段网络数据的偏移和长度
struct BufField {
uint32_t offset; /**< Offset of the buffer segment */
uint32_t len; /**< Length of the buffer segment */
};
typedef struct NetBuf {
struct DListHead dlist; //双链表
struct BufField bufs[MAX_BUF_NUM];
uint8_t *mem; //内存地址
uint32_t len; //内存长度
uint32_t dataLen; //真实数据长度
void *dev; //接受到该数据的netdev
uint32_t qmap; /**< Queue mappings of the network data buffer */
uint8_t rsv[MAX_NETBUF_RESEVER_SIZE]; /**< Reserved field. For details, see {@link MAX_NETBUF_RESEVER_SIZE}. */
} NetBuf;
四、适配器 NetDeviceImpl
这里有一个问题,就是openharmony使用NetDev来描述网口,用NetBuf来描述网口数据,但是在lwip协议栈中,有他自己的一套机制来描述网口和网络数据。
这里就存在一个适配(adapt)的问题,需要一个适配器(adapter)来适配这两套机制,使得数据能在这两者之间顺利传递。
NetDev ------> netif
NetBuf ------> pbuf
NetDeviceImpl结构体应运而生,该部分的代码在bearpi_micro/drivers/adapter/khdf/liteos/network/src/net_device_adapter.c,net_device_adapter顾名思义就是把net_device适配到lwip的netif。同理netbuf_adapter.c实现的就是把netbuf适配到lwip的pbuf。
struct NetDeviceImpl {
struct NetDevice *netDevice; //可通过netDevice->NetDeviceInterFace 调用芯片驱动程序
struct NetDeviceImplOp *interFace; //调用lwip的接口
void *osPrivate; //指向 lwip的netif对象
};
所以NetDeviceImpl 就是芯片驱动程序 和 lwip协议栈沟通的中介,其成员的作用很明显:
- NetDevice:可通过netDevice->NetDeviceInterFace 调用芯片驱动程序
- NetDeviceImplOp:提供给开发者的接口
- osPrivate:指向 lwip的netif对象
NetDeviceImplOp
NetDeviceImplOp结构体就是提供给开发者的接口,使得网口驱动程序能与lwip协议栈交互。在net_device_adapter.c中的g_liteNdImplOps就是NetDeviceImplOp。我们将通过这个结构体,看lwip协议栈如何收发数据。
struct NetDeviceImplOp {
//初始化netdev
int32_t (*init)(struct NetDeviceImpl *netDevice);
int32_t (*deInit)(struct NetDeviceImpl *netDevice);
int32_t (*add)(struct NetDeviceImpl *netDevice);
int32_t (*delete)(struct NetDeviceImpl *netDevice);
int32_t (*setStatus)(struct NetDeviceImpl *netDevice, NetIfStatus status);
int32_t (*setLinkStatus)(struct NetDeviceImpl *netDevice, NetIfLinkStatus status);
int32_t (*getLinkStatus)(struct NetDeviceImpl *netDevice, NetIfLinkStatus *status);
int32_t (*receive)(struct NetDeviceImpl *netDevice, NetBuf *buff, ReceiveFlag flag);
int32_t (*setIpAddr)(struct NetDeviceImpl *netDevice, const IpV4Addr *ipAddr, const IpV4Addr *netMask,
const IpV4Addr *gw);
int32_t (*dhcpsStart)(struct NetDeviceImpl *netDevice, char *ip, uint16_t ipNum);
int32_t (*dhcpsStop)(struct NetDeviceImpl *netDevice);
int32_t (*dhcpStart)(struct NetDeviceImpl *netDevice);
int32_t (*dhcpStop)(struct NetDeviceImpl *netDevice);
int32_t (*dhcpIsBound)(struct NetDeviceImpl *netDevice);
int32_t (*changeMacAddr)(struct NetDeviceImpl *netDevice);
};
static struct NetDeviceImplOp g_liteNdImplOps = {
.init = LiteNetDevInit, //创建netif
.deInit = LiteNetDevDeInit,
.add = LiteNetDevAdd, //创建netif并添加到协议栈
.delete = LiteNetDevDelete,
.setStatus = LiteNetDevSetStatus,
.setLinkStatus = LiteNetDevSetLinkStatus,
.getLinkStatus = LiteNetDevGetLinkStatus,
.receive = LiteNetDevReceive, //将数据上传到协议栈
.setIpAddr = LiteNetSetIpAddr, //设置ip
.dhcpsStart = LiteNetDhcpsStart, //开始dhcp
.dhcpsStop = LiteNetDhcpsStop,
.dhcpStart = LiteNetDhcpStart,
.dhcpStop = LiteNetDhcpStop,
.dhcpIsBound = LiteNetDhcpIsBound,
.changeMacAddr = LiteNetChangeMacAddr,
};
五、netif
要使用lwip协议栈,就需要创建一个netif来描述所使用的wifi 网口。NetDeviceImplOp结构体提供给开发者创建netif的接口:
创建初始化lwip netif的接口:
- g_liteNdImplOps.init()
- g_liteNdImplOps.add()
struct NetDeviceAdapterLite {
struct netif *lwipNetif;
};
//创建netif
static int32_t LiteNetDevInit(struct NetDeviceImpl *netDeviceImpl)
{
struct NetDeviceAdapterLite *liteNdPri = NULL;
liteNdPri = (struct NetDeviceAdapterLite *)OsalMemCalloc(sizeof(struct NetDeviceAdapterLite));
(void)memset_s(liteNdPri, sizeof(struct NetDeviceAdapterLite), 0, sizeof(struct NetDeviceAdapterLite));
netDeviceImpl->osPrivate = (void *)liteNdPri;
}
//创建netif并添加到协议栈
static int32_t LiteNetDevAdd(struct NetDeviceImpl *netDeviceImpl)
{
struct NetDeviceAdapterLite *liteNdPri = (struct NetDeviceAdapterLite *)netDeviceImpl->osPrivate;
struct NetDevice *lwipNd = netDeviceImpl->netDevice;
//根据lwipNd创建netif
lwipNf = CreateLwipNetIf(netDeviceImpl, lwipNd创建netif);
if ((ret = netifapi_netif_add(lwipNf, &ipaddr, &netmask, &gw)) != ERR_OK) {
return HDF_FAILURE;
}
//复制mac地址
if (memcpy_s(lwipNf->hwaddr, NETIF_MAX_HWADDR_LEN, lwipNd->macAddr, MAC_ADDR_SIZE) != EOK) {
return HDF_FAILURE;
}
liteNdPri->lwipNetif = lwipNf;
IpV6SpecialProc(lwipNd, lwipNf);
/* set netif default status */
netifapi_netif_set_default(lwipNf);
netif_set_link_callback(lwipNf, LiteNetifLinkChangeCallback);
return HDF_SUCCESS;
}
创建netif的同时实现了netif的一些函数如drv_send、drv_set_hwaddr,这些函数需要调用芯片驱动。
static struct netif *CreateLwipNetIf(const struct NetDeviceImpl *netDeviceImpl, const struct NetDevice *netDev)
{
lwipNf = (struct netif *)OsalMemCalloc(sizeof(struct netif));
(void)memset_s(lwipNf, sizeof(struct netif), 0, sizeof(struct netif));
/* register netif to lwip */
lwipNf->state = (void *)netDeviceImpl;
lwipNf->drv_send = LwipSend;
lwipNf->drv_set_hwaddr = LwipSetHwaddr;
lwipNf->link_layer_type = netDev->LinkLayerType;
lwipNf->hwaddr_len = MAC_ADDR_SIZE;
lwipNf->drv_config = LwipDrvConfig;
return lwipNf;
}
网口发送数据到lwip
在网口的驱动程序收到数据后,需要传递给lwip协议栈,这时调用NetDeviceImplOp->reveive()函数来传递数据:
首先把netdev转换成netif,把netbuf转换成pbuf,再调用driverif_input()传递pbuf给lwip协议栈。
static int32_t LiteNetDevReceive(struct NetDeviceImpl *netDeviceImpl, struct NetBuf *buff, ReceiveFlag flag)
{
//get lwip netif
struct netif *lwipNf = GetNetIfFromDevImpl(netDeviceImpl);
ProcessingResult ret = LiteNetDevDataFilter(netDeviceImpl, buff);
if (ret == PROCESSING_CONTINUE) {
//call LiteNetDevDataReceive to send pbuf
return LiteNetDevDataReceive(netDeviceImpl, buff);
} else if (ret == PROCESSING_COMPLETE) {
return HDF_SUCCESS;
} else {
return HDF_FAILURE;
}
}
static int32_t LiteNetDevDataReceive(struct NetDeviceImpl *netDeviceImpl, struct NetBuf *buff)
{
struct netif *lwipNf = GetNetIfFromDevImpl(netDeviceImpl);
//Conver NetBuf To PBuf
struct pbuf *pBuff = ConverNetBufToPBuf(buff);
//post pbuff to lwip
driverif_input(lwipNf, pBuff);
NetBufFree(buff);
return HDF_SUCCESS;
}
driverif_input()是由lwip提供的接口,定义如下:
/*
* This function should be called by network driver to pass the input packet to LwIP.
* Before calling this API, driver has to keep the packet in pbuf structure. Driver has to
* call pbuf_alloc() with type as PBUF_RAM to create pbuf structure. Then driver
* has to pass the pbuf structure to this API. This will add the pbuf into the TCPIP thread.
* Once this packet is processed by TCPIP thread, pbuf will be freed. Driver is not required to
* free the pbuf.
*
* @param netif the lwip network interface structure for this driverif
* @param p packet in pbuf structure format
*/
void driverif_input(struct netif *netif, struct pbuf *p)
网口从lwip接收数据
lwip协议栈的数据会传递到netif结构体,而我们在创建netif结构体时,设置了drv_send()等函数,lwip协议栈就会调用该函数,在这个函数中我们就需要实现netif和netdev,pbuf和netbuf的转换,调用NetDeviceInterFace 里的函数发送数据。
void LwipSend(struct netif *netif, struct pbuf *lwipBuf)
{
struct NetDeviceImpl *ndImpl = (struct NetDeviceImpl *)netif->state;
//get netdev
struct NetDevice *netDev = ndImpl->netDevice;
struct NetDeviceInterFace *netDevIf = NULL;
//get netbuf
struct NetBuf *netBuff = ConverPbuffToNetBuf(netDev, lwipBuf);
//get NetDeviceInterFace
netDevIf = netDev->netDeviceIf;
//send netbuf
netDevIf->xmit(netDev, netBuff);
return;
}